ReactiveUi: How can I replace the default IViewLocator when using AutoFac? - c#

High!
I am trying to replace the default IViewLocator of ReactiveUi/Splat in WPF. I am using AutoFac as container.
My goal is very simple: ReactiveUi/Splat should use my custom implementation of IViewLocator when resolving a view for view model.
I really read every availabe tutorial and stackoverflow thread, but nothing helped.
Currently I do the following while bootstrapping: (I tried many different things...)
namespace MDM
{
public static class Bootstrapper
{
private static AutofacDependencyResolver? Locator;
private static IContainer? Container;
public static void Register()
{
Splat.Locator.CurrentMutable.InitializeSplat();
Splat.Locator.CurrentMutable.InitializeReactiveUI();
Splat.Locator.CurrentMutable.RegisterConstant<IViewLocator>(new ViewLocator());
var builder = new ContainerBuilder();
builder.RegisterType<MainWindowView>().As<IViewFor<MainWindowViewModel>>().SingleInstance();
builder.RegisterType<EinstellungenView>().As<IViewFor<EinstellungenViewModel>>().SingleInstance();
builder.RegisterType<MainWindowViewModel>().AsSelf().SingleInstance();
builder.RegisterType<EinstellungenViewModel>().AsSelf().SingleInstance();
Locator = builder.UseAutofacDependencyResolver();
builder.RegisterInstance(Locator);
Locator.InitializeReactiveUI();
Splat.Locator.SetLocator(Locator);
Container = builder.Build();
Locator.SetLifetimeScope(Container);
}
public static T Resolve<T>() where T : class
{
return Container!.Resolve<T>();
}
}
}
While debugging the following line of code in my IViewLocator is never hit:
public IViewFor? ResolveView<T>(T viewModel, string? contract = null)
{
}
So my question is: What do I need to do while bootstrapping, to tell ReactiveUi to use my IViewLocator?

If you decide to drop Splat (the IoC container used by ReactiveUI) and use Autofac, then you must go with it, especially when registering services.
Once you have registered a custom IoC container (properly), you shouldn't use Splat anymore to resolve any dependencies. Although Splat will redirect service requests to the Autofac container, I recommend against mixing the APIs.
var containerBuilder = new ContainerBuilder();
// TODO::Register application's dependencies with Autofac
/* Configure Splat to use Autofac */
var autofacResolver = containerBuilder.UseAutofacDependencyResolver();
containerBuilder.RegisterInstance(autofacResolver);
autofacResolver.InitializeReactiveUI();
// AFTER configuring the IoC redirect, register the Splat service overrides
containerBuilder.RegisterType<ViewLocator>()
.As<IViewLocator>()
.SingleInstance();
var container = containerBuilder.Build();
autofacResolver.SetLifetimeScope(container);
Don't use the Service Locator anti-pattern. The IoC container should not be distributed across the application. Neither as injected reference nor as static reference.
Use the Abstract Factory pattern instead.
For this reason, IViewLocator.ResolveView must use factories instead of the static Resolve method that you have implemented in your Bootstrapper.

Related

Registering a third party class in Autofac whose constructor take in values from another service

I am working with IQueueClient interface in Microsoft.Azure.ServiceBus namespace.
Here is my code
public HomeControllerBL(IApplicationSettings appSettings)
{
_appSettings = appSettings;
}
and here is my IApplicationSettings Interface
public interface IApplicationSettings
{
string GetServiceBusConnectionString();
string GetQueueName();
}
Now for creating an object of QueueClient
IQueueClient queueClient = new QueueClient(appSettings.GetServiceBusConnectionString(), appSettings.GetQueueName());
So IQueueClient has a dependency on IApplicationSettings .
Is there a way I can register both IQueueClient and IApplicationSettings with Autofac as a dependency for HomeControllerBL
Something on these Lines :-
builder.RegisterType<ApplicationSettings>()
.As<IApplicationSettings>()
.InstancePerLifetimeScope();
builder.RegisterType<QueueClient>()
.As<IQueueClient>().WithParameters(new List<Parameter>() { How to access Applicationsettings methods here ??? })
.InstancePerLifetimeScope();
Reference Lambda Expression Components
Reflection is a pretty good default choice for component creation. Things get messy, though, when component creation logic goes beyond a simple constructor call.
Autofac can accept a delegate or lambda expression to be used as a component creator:
builder.Register(c => {
IApplicationSettings appSettings = c.Resolve<IApplicationSettings>();
IQueueClient queueClient = new QueueClient(appSettings.GetServiceBusConnectionString(), appSettings.GetQueueName());
return queueClient;
})
.As<IQueueClient>()
.InstancePerLifetimeScope();
The parameter c provided to the expression is the component context (an IComponentContext object) in which the component is being created. You can use this to resolve other values from the container to assist in creating your component. It is important to use this rather than a closure to access the container so that deterministic disposal and nested containers can be supported correctly.
So now the controller can depend on IQueueClient explicitly
private readonly IQueueClient queueClient;
public HomeControllerBL(IQueueClient queueClient) {
this.queueClient = queueClient;
}

How to get DbContext in nested methods using Simple Injector

I have a doubt since I'm new to Dependency Injection and IoC.
I have a domain layer (with business logic) and a data layer. We do not implement repositories, we use EF Core directly.
It is a Class Library project, we use it in a ASP.NET CCore Web API, WinForms, and inside another framework.
The idea is to use the same context inside a scope.
The problem is that I'm not being able to get the same context in the nested method execution, I'm sure it is because I did not understand the concept completely, can you guys give me a help on that?
Example:
public class MyTest
{
public void TestContainer()
{
var parentContext = MyContainer.Container.GetInstance<MyContext>();
TestParentAndChildContext(parentContext);
}
private void TestParentAndChildContext(MyContext parentContext)
{
var childContext = MyContainer.Container.GetInstance<MyContext>();
Assert.AreEqual(parentContext, childContext);
}
}
public class MyContainer
{
public static Container Container
{
get { return container ?? (container = RegisterAndVerifyContainer()); }
}
private static Container RegisterAndVerifyContainer()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
container.Register<DbContext, MyContext>(Lifestyle.Scoped);
container.Verify();
return container;
}
}
In Simple Injector you register an implementation by its abstraction. In your case you registed an MyContext by its DbContext base type. From this point on Simple Injector will know that it will need to construct a MyContext in case someone asks for a DbContext. This is the whole purpose of
Program to an interface, not an implementation
In your case however, although you do register the MyContext by its abstraction, you request a new instance of MyContext directly, instead of requesting it through its abstraction. This causes Simple Injector to look for the MyContext in its list of registered abstractions. Since there is no registration for MyContext (there is for DbContext though, but that's a totally different type what Simple Injector is concerned), Simple Injector will try to add the missing registration. This succeeds because MyContext is concrete and has single resolvable constructor, while you are using Simple Injector v4.x.
By default the older versions of Simple Injector will resolve unregistered concrete types as Transient. This default changed in v5, where it won't create any unregistered concrete type by default.
So MyContext is resolved as transient when requested directly. You can solve this by changing your test to the following:
public void TestContainer()
{
using (MyContainer.Container.BeginExecutionContextScope()) {
var parentContext = MyContainer.Container.GetInstance<DbContext>();
TestParentAndChildContext(parentContext);
}
}
private void TestParentAndChildContext(MyContext parentContext)
{
var childContext = MyContainer.Container.GetInstance<DbContext>();
Assert.AreEqual(parentContext, childContext);
}
Do note that Simple Injector usually detects these kinds of mistakes. In case you register MyContext by its DbContext base type, but inject MyContext directly in a constructor of a type, Simple Injector will throw a Short-Circuited Dependency error when calling Verify().
The reason you didn't get warned about this, is because you've called Verify() before the resolve action (you should typically not call GetInstance from within your application; instead you should build all object graphs up front). But when you'd call Verify (again) after resolving MyContext you would see the exception popping up:
[TestMethod]
public void TestContainer()
{
var container = MyContainer.Container.GetInstance<DbContext>();
var parentContext = container.GetInstance<MyContext>();
var childContext = container.GetInstance<MyContext>();
// This call will fail
container.Verify();
}

Autofac/FluentValidation: No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested

Attempting to inject data into a FluentValidation validator:
public class MyFormValidator : AbstractValidator<MyForm>
{
private readonly IQueryable<Models.User> _users;
public MyFormValidator(IQueryable<Models.User> users)
{
_users = users;
...
}
}
My validator factory:
public class DependencyResolverValidatorFactory : ValidatorFactoryBase
{
private readonly IContainer container;
public DependencyResolverValidatorFactory(IContainer container)
{
this.container = container;
}
public override IValidator CreateInstance(Type validatorType)
{
return container.ResolveOptionalKeyed<IValidator>(validatorType);
}
}
My Autofac configurator:
public class AutofacConfigurator
{
public static void Configure()
{
var builder = new ContainerBuilder();
...
builder.RegisterType<MyFormValidator>()
.Keyed<IValidator>(typeof(IValidator<MyForm>))
.As<IValidator>()
// 2nd parameter returns IQueryable<User>
.WithParameter("users", new SqlRepository<User>(dataContext))
.InstancePerRequest();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Register the validator factory with FluentValidation, and register
// FluentValidation as the model validator provider for the MVC framework.
// see http://www.jerriepelser.com/blog/using-fluent-validation-with-asp-net-mvc-part-3-adding-dependency-injection
var fluentValidationModelValidatorProvider =
new FluentValidationModelValidatorProvider(
new DependencyResolverValidatorFactory(container));
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
fluentValidationModelValidatorProvider.AddImplicitRequiredValidator = false;
ModelValidatorProviders.Providers.Add(fluentValidationModelValidatorProvider);
}
}
Getting the following exception:
No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.
I have other validators, most of which will not need data injected into them.
This is largely new ground for me (in both Autofac and FluentValidation) and am still trying to understand what I am doing here. I suspect I'm simply registering my type incorrectly. How do I fix this and properly register my type?
(My apologies if this is too similar to other questions that were already asked.)
I have zero experience with FluentValidation, but I doubt it's the cause of your issues anyway, so I'll plow forward regardless.
The exception you're getting means that Autofac can't resolve your service as 'instance per request'. There's a lot of documentation as to what this means on the Autofac documentation page. To summarize, it means that Autofac will attempt to resolve the service from a lifetime scope that is automatically created for each request sent to the webserver. When you register something as .InstancePerRequestScope() but then attempt to resolve that service outside of that scope, you'll get the DependencyResolutionException you see.
So we've established that your MyFormValidator isn't being resolved from a 'Request' scope. Why?
The custom DependencyResolverValidatorFactory you've written takes the actual IContainer that was built by Autofac, and resolves from that. This is a special type of ILifetimeScope, the 'root scope'. There's no request lifetime scope directly associated with this, so you get your exception. You need to to resolve from an ILifetimeScope that is began from the 'request' scope, or a sub-scope that is contained within the request scope.
The Autofac/MVC integration already automatically hosts a request scope (within the AutofacDependencyResolver, see the source), but your custom DependencyResolverValidatorFactory doesn't resolve from it. If you want to do that, I suppose you could modify your DependencyResolverValidatorFactory to accept the AutofacDependencyResolver instance instead, and use that to resolve.
It would look something like this:
public class DependencyResolverValidatorFactory : ValidatorFactoryBase
{
private readonly AutofacDependencyResolver resolver;
public DependencyResolverValidatorFactory(AutofacDependencyResolver resolver)
{
this.resolver = resolver;
}
public override IValidator CreateInstance(Type validatorType)
{
return resolver.RequestLiftimeScope.ResolveOptionalKeyed<IValidator>(validatorType);
}
}
Note the RequestLifetimeScope stuck in there.
Then you create this in your .Configure() method using
var resolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
var fluentValidationModelValidatorProvider =
new FluentValidationModelValidatorProvider(
new DependencyResolverValidatorFactory(resolver));
That should get rid of the exception, assuming that this factory does indeed have a request to work from when creating instances of IValidators. If not, You might need to register using the default behavior (.InstancePerDependency(), where it creates a new instance every time it's requested) or a singleton (.SingleInstance()), depending on how/if validators can or should be shared.
Good luck.

Exception is: InvalidOperationException - The current type, is an interface and cannot be constructed. Are you missing a type mapping?

In my bootstrapper:
namespace Conduit.Mam.ClientServices.Common.Initizliaer
{
public static class Initializer
{
private static bool isInitialize;
private static readonly object LockObj = new object();
private static IUnityContainer defaultContainer = new UnityContainer();
static Initializer()
{
Initialize();
}
public static void Initialize()
{
if (isInitialize)
return;
lock (LockObj)
{
IUnityContainer container = defaultContainer;
//registering Unity for MVC
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
//registering Unity for web API
// GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
#region managers
container.RegisterType<ISettingsManager, SettingsManager>();
container.RegisterType<IMamDataManager, MamDataManager>();
container.RegisterType<IAppsDataManager, AppsDataManager>();
#endregion
if (!isInitialize)
{
isInitialize = true;
}
}
}
}
}
in my controller's code:
ISettingsManager sm = mUnityContainer.Resolve<ISettingsManager>();
hovering on mUnityContainer I see ISettingsManager is mapped to SettingsManager
but then I get the error:
Exception is: InvalidOperationException - The current type, is an
interface and cannot be constructed. Are you missing a type mapping?
I have also tried
ISettingsManager sm = (ISettingsManager)mUnityContainer.Resolve<>(typeof(ISettingsManager));
but no use
Just for others (like me) who might have faced the above error. The solution in simple terms.
You might have missed to register your Interface and class (which implements that inteface) registration in your code.
e.g if the error is
"The current type, xyznamespace. Imyinterfacename, is an interface and cannot be constructed. Are you missing a type mapping?"
Then you must register the class which implements the Imyinterfacename in the UnityConfig class in the Register method. using code like below
container.RegisterType<Imyinterfacename, myinterfaceimplclassname>();
You are incorrectly using Dependency Injection. The proper way is to have your controllers take the dependencies they need and leave to the dependency injection framework inject the concrete instances:
public class HomeController: Controller
{
private readonly ISettingsManager settingsManager;
public HomeController(ISettingsManager settingsManager)
{
this.settingsManager = settingsManager;
}
public ActionResult Index()
{
// you could use the this.settingsManager here
}
}
As you can see in this example the controller doesn't know anything about the container. And that's how it should be.
All the DI wiring should happen in your Bootstraper. You should never use container.Resolve<> calls in your code.
As far as your error is concerned, probably the mUnityContainer you are using inside your controller is not the same instance as the one constructed in your Bootstraper. But since you shouldn't be using any container code in your controllers, this shouldn't be a problem anymore.
In my case, I was getting this error despite registering an existing instance for the interface in question.
Turned out, it was because I was using Unity in WebForms by way of the Unity.WebForms Nuget package, and I had specified a Hierarchical Lifetime manager for the dependency I was providing an instance for, yet a Transient lifetime manager for a subsequent type that depended on the previous type - not usually an issue - but with Unity.WebForms, the lifetime managers work a little differently... your injected types seem to require a Hierarchical lifetime manager, but a new container is still created for every web request (because of the architecture of web forms I guess) as explained excellently in this post.
Anyway, I resolved it by simply not specifying a lifetime manager for the types/instances when registering them.
i.e.
container.RegisterInstance<IMapper>(MappingConfig.GetMapper(), new HierarchicalLifetimeManager());
container.RegisterType<IUserContext, UserContext>(new TransientLifetimeManager());
becomes
container.RegisterInstance<IMapper>(MappingConfig.GetMapper());
container.RegisterType<IUserContext, UserContext>();
So that IMapper can be resolved successfully here:
public class UserContext : BaseContext, IUserContext
{
public UserContext(IMapper _mapper) : base(_mapper)
{
}
...
}
In my case, I have used 2 different context with Unitofwork and Ioc container so i see this problem insistanting while service layer try to make inject second repository to DI. The reason is that exist module has containing other module instance and container supposed to gettng a call from not constractured new repository.. i write here for whome in my shooes
May be You are not registering the Controllers.
Try below code:
Step 1.
Write your own controller factory class
ControllerFactory :DefaultControllerFactory by implementing defaultcontrollerfactory
in models folder
public class ControllerFactory :DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
throw new ArgumentNullException("controllerType");
if (!typeof(IController).IsAssignableFrom(controllerType))
throw new ArgumentException(string.Format(
"Type requested is not a controller: {0}",
controllerType.Name),
"controllerType");
return MvcUnityContainer.Container.Resolve(controllerType) as IController;
}
catch
{
return null;
}
}
public static class MvcUnityContainer
{
public static UnityContainer Container { get; set; }
}
}
Step 2:Regigster it in BootStrap:
inBuildUnityContainer method
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
//RegisterTypes(container);
container = new UnityContainer();
container.RegisterType<IProductRepository, ProductRepository>();
MvcUnityContainer.Container = container;
return container;
}
Step 3:
In Global Asax.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
Bootstrapper.Initialise();
ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));
}
And you are done
I had this problem, and the cause was that I had not added the Microsoft.Owin.Host.SystemWeb NuGet package to my project. Although the code in my startup class was correct, it was not being executed.
So if you're trying to solve this problem, put a breakpoint in the code where you do the Unity registrations. If you don't hit it, your dependency injection isn't going to work.
Below code will be helpful for you
public static IUnityContainer Initialise(IUnityContainer container = null)
{
if (container == null)
{
container = new UnityContainer();
}
container.RegisterType<ISettingsManager, SettingsManager>();
container.Resolve<SettingsManager>();
container.RegisterType<SettingsManagerController>(new InjectionProperty("_SettingManagerProvider", new ResolvedParameter<ISettingManager>()));
return container;
}

C# Dependency Injection and the Strategy Pattern

I'm using Munq as the DI container in a MVC3 project. I have a service layer that retrieves a DTO from a repository. Depending on a property in that DTO I need to use one of two strategies to perform calculations on the DTO. I can register a named type in the container e.g.
Container.Register<ICalculation>("Type1", c => new Type1Calculation);
Container.Register<ICalculation>("Type2", c => new Type2Calculation);
Then I can refer directly to the container when trying to instantiate the appropriate strategy e.g.
var calc = Container.Resolve<ICalculation>(dto.ServiceType);
But this leaves me with a dependency on the container itself with the associated coupling and testing issues. What do I need to pass into the service constructor that would allow me to get the correct calculation but without the dependency on the container? Should I pass in a factory instead so the dependency is in the factory and not in the service class?
Not sure about Munq, but Autofac allows you to pass around Funcs, so that you can skip all factories altogether:
public class Foo
{
public Foo(Func<string, IBar> barFactory) { }
}
Check if Munq allows for such behavior.
Otherwise -- yes, you'll have to resort to hand-written factories to provide yet another level of indirection.
I've added the Munq solution to this. First the factory which includes the IDependencyResolver interface to allow the factory to use the container to resolve dependencies in the factory method:
public class CalculationFactory
{
private readonly IDependencyResolver _resolver;
public CalculationFactory(IDependencyResolver resolver)
{
ThrowIfNullArgument(resolver, "resolver", typeof(IDependencyResolver));
_resolver = resolver;
}
public static ICalculation CreateCalculator(int serviceType)
{
switch (serviceType)
{
case 1: return _resolver.Resolve<ICalculation>("Type1");
case 2: return _resolver.Resolve<ICalculation>("Type2");
default: return _resolver.Resolve<ICalculation>("InvalidType");
}
}
}
Then in Global.asax.cs register the appropriate interfaces/classes, passing in the container to the factory. So now I can set up my tests and the only extra dependency is IDependencyResolver within the factory:
ioc.Register(c => new CalculationFactory(c));
ioc.Register<ICalculation>("Type1", c => c.Resolve<Type1Calculation>());
ioc.Register<ICalculation>("Type2", c => c.Resolve<Type2Calculation>());
ioc.Register<ICalculation>("InvalidType", c => c.Resolve<InvalidCalculation>());

Categories