I'm wiring up Autofac dependency injection within my ASP.NET MVC 5 web application using OWIN middleware (so using startup.cs instead of global.asax), and trying to use property injection to set a public variable within a Controller.
I'm playing around with property injection to have Autofac automatically set the Test property in the LoginController.
public interface ITest
{
string TestMethod();
}
public class Test : ITest
{
public string TestMethod()
{
return "Hello world!";
}
}
public class LoginController : Controller
{
public ITest Test { get; set; }
public LoginController()
{
var aaa = Test.TestMethod();
// Do other stuff...
}
}
Here's what my startup.cs looks like. I have been playing around, so some of this code might not be needed (or causing my issue?).
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.RegisterType<Test>().As<ITest>().SingleInstance();
builder.Register(c => new Test()).As<ITest>().InstancePerDependency();
builder.RegisterType<ITest>().PropertiesAutowired();
builder.RegisterType<LoginController>().PropertiesAutowired();
builder.RegisterModelBinderProvider();
builder.RegisterFilterProvider();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
app.UseAutofacMiddleware(container);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
// Some other stuff...
}
}
So, the 'Test' public property is always null, and therefore breaks on runtime.
Any ideas what could be my issue? Thanks advance for your help! :)
So, the 'Test' public property is always null, and therefore breaks on runtime.
It's not always null. It's null in the constructor because Autofac (actually ALL code) cannot set properties until the constructor is finished.
public class LoginController : Controller
{
public ITest Test { get; set; }
public LoginController()
{
// Test is null, will always be null here
var aaa = Test.TestMethod();
}
}
A super dummied down version of autofac does something like:
var controller = new LoginController();
controller.Test = new Test();
If you need to execute code after the property is set you could do something hacky like the following (but really you should just be using constructor injection):
public class LoginController : Controller
{
private ITest _test;
public ITest Test
{
get { return _test; }
set
{
var initialize = (_test == null);
_test = value;
if (initialize)
{
Initialize();
}
}
}
public LoginController()
{
}
private void Initialize()
{
var aaa = Test.TestMethod();
}
}
Again the more logical way would be to just do:
public class LoginController : Controller
{
private readonly ITest _test;
public LoginController(ITest test)
{
_test = test;
var aaa = _test.TestMethod();
// Do other stuff...
}
}
Related
So I am using Unity MVC-4 for achieving Dependency Injection and it works great with my Controller classes but as soon as I try to inject in my non controller class, I get the NullReferenceException and I can see that my injected objects are not initialized by the framework. I will give you the corresponding classes that I am using:
Controller class (DI works):
public class HomeController : Controller
{
IMyService _myService;
#region CTOR
public HomeController(IMyService myService)
{
_myService = myService;
}
#endregion
public string GetMyString()
{
string mystring=string.Empty;
try
{
mystring = _myService.GetMyStringFromDLL();
}
catch (Exception ex)
{
StringBuilder str = new StringBuilder();
str.AppendLine("Exception in method GetMyString, Error msg: " + ex.Message);
WriteLog(sb);
}
return mystring;
}
}
And if I do the same thing in a non controller method (DI does not work here), I get a NullReferenceException:
public inteface IMyLogic
{
string GetMyString();
}
public class MyLogic: IMyLogic
{
IMyService _myService;
#region CTOR
public MyLogic(IMyService myService)
{
_myService = myService;
}
#endregion
public string GetMyString()
{
string mystring=string.Empty;
try
{
mystring = _myService.GetMyStringFromDLL(); //Getting NullReferenceException here
}
catch (Exception ex)
{
StringBuilder str = new StringBuilder();
str.AppendLine("Exception in method GetMyString, Error msg: " + ex.Message);
WriteLog(sb);
}
return mystring;
}
}
My BootStrapper.cs class looks like:
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
container.RegisterType<IMyService , MyService>();
container.RegisterType<IMyLogic, MyLogic>(new HierarchicalLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
}
}
If you see above in the line container.RegisterType<IMyService , MyService>();, the interface and its concrete implementation is in a separate module.
And my Global.asax.cs is:
protected void Application_Start()
{
Bootstrapper.Initialise();
AreaRegistration.RegisterAllAreas();
GlobalFilters.Filters.Add(new OfflineActionFilter());
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
How can I inject the IMyService in MyLogic class?
Use attribute [InjectionConstructor] to tell the Unity that the MyLogic class is dependent on an object which is to be injected in the constructor:
[InjectionConstructor]
public MyLogic(IMyService myService)
{
_myService = myService;
}
Actually the [InjectionConstructor] is recommended to use when the injected class contains more than one constructor. Therefore, the attribute is used to resolve the disambiguation. It was just my hypothesis why the Unity cannot resolve the required type, because of the question code does not contain all part of the code. But in the test code below the [InjectionConstructor] attribute not needed, because of only one constructor is declared.
Here it is the test code.
The IMyService interface definition:
public interface IMyService
{
string GetMyStringFromDLL();
}
The ILogic interface definition:
public interface IMyLogic
{
string GetMyString();
}
The MyLogic implementation:
public class MyLogic : IMyLogic
{
IMyService _myService;
public MyLogic(IMyService myService)
{
_myService = myService;
}
public string GetMyString()
{
var mystring = string.Empty;
try
{
mystring = "MyLogic.GetMyString() -> " + _myService.GetMyStringFromDLL();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception in method MyLogic.GetMyString(): " + ex.Message);
}
return mystring;
}
}
The MyService implementation:
public class MyService : IMyService
{
public string GetMyStringFromDLL()
{
return "MyService.GetMyStringFromDLL() is called.";
}
}
The Bootstrapper initialization:
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = new UnityContainer();
container.RegisterType<IMyService, MyService>();
container.RegisterType<IMyLogic, MyLogic>(new HierarchicalLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
}
The Home controller implementation:
public class HomeController : Controller
{
private readonly IMyService _myService;
private readonly IMyLogic _myLogic;
#region CTOR
public HomeController(IMyService myService, IMyLogic myLogic)
{
_myService = myService;
_myLogic = myLogic;
}
#endregion
public ActionResult Index()
{
// Obtaining string directly from the IMyService
var sService = _myService.GetMyStringFromDLL();
// Obtaining string through the IMyLogic
var sLogic = _myLogic.GetMyString();
return View(new List<string>() { sService, sLogic} );
}
}
And finally when default action method of the Home controller executed the following two lines are displayed:
MyService.GetMyStringFromDLL() is called.
MyLogic.GetMyString() -> MyService.GetMyStringFromDLL() is called.
I haven't worked with Unity since I last did some work on NopCommerce V1.90, however I do remember that whatever you register in your container does come back if you use Resolve on an instance implementing IUnityResolver.
So basically, you registered IMyService, but you'd also have to register IMyLogic - then instead of doing "var logic = new MyLogic();", you'd do "var logic = resolver.Resolve(typeof(IMyLogic));" and then your injected parameters will be resolved according to the dependency injector (or you'd get the proper errors if they were missing).
Is there any way to resolve the instance of a class at the controller level? I would like to override the previous instance created by unity and assign this new value via the controller.
Problem is I am not sure how to access the unity container in the web app controller.
Here is my code:
Repository:
public class UserRepository: IUserRepository
{
private UserInformation _userInfo;
public UserRepository(string headerValue)
{
_userInfo = LoadUserData(headerValue);
}
public UserInformation GetUserInfo()
{
return _userInfo;
}
}
public class UserInformation
{
public string FirstName;
public string LastName;
}
Unity Configuration:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//Some code omitted
config.DependencyResolver = new UnityDependencyResolver(UnityConfig.RegisterComponents());
}
}
public static class UnityConfig
{
public static UnityContainer RegisterComponents()
{
//Unity Configuration
var container = new UnityContainer();
container.RegisterType<IUserRepository, UserRepository>(new InjectionConstructor("DummyHeaderValue"));
return container;
}
}
Controller:
public class CustomerController : ApiController
{
public CustomerController()
{
//Something like this
container.Resolve<UserRepository>(new InjectionConstructor(Request.GetHeader("RealHeaderValueFromHttpRequest")));
}
}
Then I should be able to use the updated UserRepository instance throughout the application.
Any thoughts on how to achieve this?
Edit: As pointed out by #Nkosi I don't have access to Request in controller constructor. So let me rephrase my question again:
How would I initialise UserRepository with UserInformation object which contains details about the current user? The reason I want to do this is that throughout my application I want user details and I don't want to pass User Id from each method
Something like this: From any method throughout application
UserInformation obj = _userRepository().GetUserInfo();
Create an abstraction to get access to the request
public interface IHeaderService {
string RealHeaderValueFromHttpRequest();
}
Its Implementation will have access to the context and request to get the desired functionality
public class HeaderService : IHeaderService {
public string RealHeaderValueFromHttpRequest() {
return HttpContext.Current.Request.Headers["RealHeaderValueFromHttpRequest"];
}
}
The service will now be explicitly injected into the dependent repository
public class UserRepository: IUserRepository {
private readonly IHeaderService headerService;
public UserRepository(IHeaderService headerService) {
this.headerService = headerService;
}
public UserInformation GetUserInfo() {
var headerValue = headerService.RealHeaderValueFromHttpRequest();
var _userInfo = LoadUserData(headerValue);
return _userInfo;
}
//...
}
The repository will then also be explicitly injected into dependent controllers.
public class CustomerController : ApiController {
private readonly IUserRepository repositoty;
public CustomerController(IUserRepository repositoty) {
this.repository = repository;
}
public IHttpActionResult SomeAction() {
//NOTE: Only access user info in a controller action
var userInfo = repository.GetUserInfo();
//... use user info.
}
//...
}
Now all that is left is to make sure all abstractions and their implementations are registered with the dependency container
public static class UnityConfig {
public static UnityContainer RegisterComponents() {
//Unity Configuration
var container = new UnityContainer();
container.RegisterType<IUserRepository, UserRepository>();
container.RegisterType<IHeaderService, HeaderService>();
return container;
}
}
I have class to setup Autofac in my WebApi project, it's look like:
public static class Bootstraper
{
private static IContainer _container { get; set; }
public static void Init()
{
var configuration = GlobalConfiguration.Configuration;
var builder = new ContainerBuilder();
// register WebAPI
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(configuration);
builder.RegisterHttpRequestMessage(configuration);
// register MVC
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterModule(new AutofacWebTypesModule());
// register model class
builder.RegisterType<Foo>().AsSelf().InstancePerRequest();
builder.RegisterType<FooMessage>().AsSelf().InstancePerRequest();
var container = builder.Build();
// setup WebApi Autofac resolver
var resolverAPI = new AutofacWebApiDependencyResolver(container);
configuration.DependencyResolver = resolverAPI;
// setup MVC Autofac resolver
var resolverMVC = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolverMVC);
_container = container;
}
public static T Resolve<T>()
{
return _container.Resolve<T>();
}
public static T ResolveInScope<T>()
{
// where scope shoud by disposed?
var scope = _container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
return scope.Resolve<T>();
}
}
Foo and FooMessage class:
public class FooMessage
{
public HttpRequestMessage Request {get; private set; }
public Foo Foo { get; private set; }
public FooMessage(HttpRequestMessage requestMessage, Foo fooo)
{
Request = requestMessage;
Foo = fooo;
}
}
public class Foo
{
public static int counter = 0;
public int Index { get; private set; }
public Foo()
{
Index = counter++;
}
}
To show my problem, I prepared example controller
public class ValuesController : ApiController
{
private FooMessage fooMessage;
private Foo foo;
public ValuesController(FooMessage fm, Foo fooo)
{
fooMessage = fm;
foo = fooo;
}
// GET api/values
public bool Get()
{
// it works but create new Foo object (resolvedFoo.Index > foo.Index)
var resolvedFoo = DependencyResolver.Current.GetService<Foo>();
// throw the exception
var resolvedFoo2 = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(Foo)) as Foo;
// throw the Exception
var resolvedFoo3 = Bootstraper.Resolve<Foo>();
// it works but create new Foo object (resolvedFoo.Index > foo.Index)
// and I don't now when dispose created scope
var resolvedFoo4 = Bootstraper.ResolveInScope<Foo>();
if (resolvedFoo.Index != foo.Index || resolvedFoo.Index != fooMessage.Foo.Index)
return false;
return true;
}
}
When the ValuesController is created the Index properties in foo and fooMessage.Foo has the same value, it works greate. I need resolve Foo, FooMessage and especially HttpRequestMessage in my bussines logic (InstancePerRequest) but I can't use bussines constructor to do this. For my purpose best solution would by use Bootstraper.Resolve() but it throw the exception:
An exception of type 'Autofac.Core.DependencyResolutionException'
occurred in Autofac.dll but was not handled in user code
Additional information: 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.
Please, help me to understand how it shoud work.
I have used singleton pattern a using static property, constructor
public class MyClass
{
private readonly MemoryCacheManager _cacheManager;
private static readonly Lazy<MyClass> _Lazy = new Lazy<MyClass>(() => new MyClass());
public static MyClass Language { get { return _Lazy.Value; } }
private MyClass()
{
_cacheManager = new MemoryCacheManager();
LoadCacheData();
}
// Rest of class
}
I have tried like following using Autofac in global.asax:
protected void Application_Start()
{
var builder = new ContainerBuilder();
builder.RegisterType<MemoryCacheManager>().SingleInstance();
}
And inside MyClass constructor:
private MyClass(MemoryCacheManager cache)
{
_cacheManager = cache;
LoadCacheData();
}
public string Translate(string language)
{
var translation = _cacheManager.GetValueFromCache();
}
And I want to call this method in Index.cshtml
Previously I did it directly like this:
<h4>#MyClass.Language.Translate("Welcome", Model.Language)</h4>
As I had Language as follow in MyClass:
public static Localizer Language { get { return _Lazy.Value; } }
But now I do not have this property.
How can I call Translate method in Index.cshtml as I do not have static property like previous.
You just need to register MyClass as a SingleInstance with your container:
var builder = new ContainerBuilder();
builder.RegisterType<MyClass>().As<IMyClass>().SingleInstance();
Then inject where ever you need:
public AnotherClass(IMyClass myClass)
{
_myClass = myClass;
}
Although it's probably the cache you want a single instance of. In that case:
builder.RegisterType<MemoryCacheManager>().SingleInstance();
And then:
public MyClass(MemoryCacheManager cache)
{
_cacheManager = cache;
LoadCacheData();
}
EDIT:
The first thing you need to do is set the DependencyResolver in your Application_Start class (you'll need to get the Autofac MVC Integration NuGet package for this):
protected void Application_Start()
{
this.RegisterAutoFac();
// Rest of method
}
private void RegisterAutoFac()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<MyClass>().As<IMyClass>();
builder.RegisterType<MyCache>().As<IMyCache>().SingleInstance();
var container = builder.Build();
// Setup the dependency resolver
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
Then you need to inject MyClass into the constructor of your controller:
public class TestController : Controller
{
private readonly IMyClass _myClass;
public TestController(IMyClass myClass)
{
_myClass = myClass;
}
// Rest of the controller
}
Then when your creating model for you get the data you need from MyClass:
public ActionResult Index()
{
var model = new ExampleModel
{
WelcomeMessage = _myClass.Translate("Welcome")
};
return View(model);
}
And in your view:
<h4>#Model.WelcomeMessage</h4>
public interface ILog
{
void Write(string msg);
}
public class MyLog : ILog
{
public void Write(string msg)
{
Console.WriteLine(msg);
}
}
public interface ICanLog
{
ILog Log { get; set; }
}
public interface IMyClass
{
void Test();
}
public class MyClass : IMyClass, ICanLog
{
public ILog Log { get; set; }
public void Test()
{
Log.Write("Test");
}
}
I am using Autofac with Castle DynamicProxy,
and try to let MyClass Test Method output "BEGIN"/"END" automatic.
public class MyLogInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("BEGIN");
invocation.Proceed();
Console.WriteLine("END");
}
}
The following is test code:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<MyLog>().As<ILog>();
builder.Register(c =>
{
ProxyGenerator g = new ProxyGenerator();
object proxy = g.CreateClassProxy(typeof(MyClass), new MyLogInterceptor());
ICanLog proxyICanLog = (ICanLog)proxy;
proxyICanLog.Log = c.Resolve<ILog>();
return proxy;
}).As<IMyClass>();
using (var container = builder.Build())
{
objectContext.Container = container;
IMyClass myclass = container.Resolve<IMyClass>();
myclass.Test();
}
But result no output "BEGIN"/"END", why ?
and if I create AutoLogModule that try build Log Property Instance automatic
public class AutoLogModule : Autofac.Module
{
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
var type = registration.Activator.LimitType;
if (HasPropertyDependencyOnClass(type))
{
registration.Activated += InjectClassViaProperty;
}
}
private bool HasPropertyDependencyOnClass(Type type)
{
return type.GetProperties().Any(property => property.CanWrite && property.PropertyType==typeof(ILog));
}
private void InjectClassViaProperty(object sender, ActivatedEventArgs<object> evt)
{
var type = evt.Instance.GetType();
var propertyInfo = type.GetProperties().First(x => x.CanWrite && x.PropertyType==typeof(ILog));
ILog log = new MyLog();
propertyInfo.SetValue(evt.Instance, log, null);
}
}
The following is test code:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<MyLog>().As<ILog>();
builder.RegisterModule(new AutoLogModule());
builder.Register(c =>
{
ProxyGenerator g = new ProxyGenerator();
object proxy = g.CreateClassProxy(typeof(MyClass), new MyLogInterceptor());
//ICanLog proxyICanLog = (ICanLog)proxy;
//proxyICanLog.Log = c.Resolve<ILog>();
return proxy;
}).As<IMyClass>();
using (var container = builder.Build())
{
objectContext.Container = container;
IMyClass myclass = container.Resolve<IMyClass>();
myclass.Test();
}
The result is Test Method throw
"Object reference not set to an instance of an object."
in Log.Write("Test")
How to write this feature?
I know this is a rather old post but as I was trying to accomplish the same thing with Autofac and I found the documentation that helped me to achieve it. I will answer just in case it helps someone else.
In my case I'm using Autofac 4.92 and and extra package for DynamicProxy called Autofac.Extras.DynamicProxy 4.5.0 as the documentations sates.
I see a difference where you register your Interceptors. Even though what you are doing is what I would have done initially; is not what Autofac Documentation currently says about how to Register Interceptors:
builder.RegisterType<MyClass>().As<IMyClass>().EnableInterfaceInterceptors();
// Typed registration
builder.Register(c => new MyLogInterceptor ();
Lastly, you need to Associate Interceptors with Types to be Intercepted:
[Intercept(typeof(MyLogInterceptor))]
public class MyClass : IMyClass, ICanLog
{
public ILog Log { get; set; }
public void Test()
{
Log.Write("Test");
}
}
I hope this answer may help. In any case, the Autofac documentation explains step by step how to do it just in case my code may mistakenly skip some relevant part.