Autofac resolve instace in current web request scope - c#

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.

Related

Inject Dependency without using Constructor injection using Ninject - Unit Tests

I am creating Unit Tests for existing .NET Framework 4.5 API project. The existing project has parameterless constructor by design and dependency injection was implemented as per the class below using Ninject.
I would like to Mock the interface and create an instance of the class for testing as shown below but the constructor is parameterless. I can't figure out how to inject my Mock member.Object. The main goal is I don't want to change the design of existing classes unless there is no other way.
public class MemberController : ApiController
{
StandardKernel DependencyKernel;
private IMember member;
public MemberController()
{
DependencyKernel = new StandardKernel();
DependencyKernel.Load(Assembly.GetExecutingAssembly());
member = DependencyKernel.Get<IMember>();
}
[HttpPost]
public HttpResponseMessage AddMember(MemberRequest model)
{
try
{
int memberRecords;
member.SaveMember(model, out memberRecords);
if (memberRecords > 0)
return new HttpResponseMessage { StatusCode = HttpStatusCode.Conflict, Content = new StringContent("Member with same reference exists") };
else
return new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent("Member added successfully") };
}
catch (Exception ex)
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.InternalServerError, Content = new StringContent(ex.Message) };
}
}
}
//Test Class
public class MemberControllerTests
{
StandardKernel DependencyKernel;
private MemberController memberController;
Mock<IMember> member = new Mock<IMember>();
public MemberControllerTests()
{
DependencyKernel = new StandardKernel();
DependencyKernel.Load(Assembly.GetExecutingAssembly());
}
[SetUp]
public void Setup()
{
}
[Test]
public void should_AddMember()
{
//Arrange
MemberRequest member = new MemberRequest{ };
int memberRecords;
member.Setup(x => x.SaveMember(member, out memberRecords));
memberController = new MemberController(member.Object); //This will obviously not work, the MemberController has parameterless constructor
//Act
var result = MemberController.AddMember(member);
//Assert
Assert.AreEqual(result.StatusCode, HttpStatusCode.OK);
}
}
This post has the answer to the question. If IMember member property is made public in MemberController, I can new up the MemberController class and set the value of its member property to the MOCK.

How to call class with dependency injection (DI)

Description
I want to create an object of a class with dependency injection. If I set the parameter manually I got the exception Cannot access a disposed of the object..
This Application is a Blazor wasm with Dotnet core 3.1. I´ve created a Middleware that should connect to a query console. So I have a static list that contains all query clients. If a client is missing it will be created.
Invoke Async in the middleware:
public async Task InvokeAsync(HttpContext context,
IConfiguration configuration,
IInstanceControlRepository instanceControlRepository,
IServiceProvider serviceProvider)
{
_configuration = configuration;
_instanceControlRepository = instanceControlRepository;
long timestamp = new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
var instances = _instanceControlRepository.GetAllInstances();
if (_time + 3 <= timestamp)
{
_time = timestamp;
// Remove
foreach(var client in TeamspeakInstanceQueryClients.ToList())
{
var cl = instances.ToList().Find(el => el.Id == client.Instance.Id);
if(cl == null)
{
client.Dispose();
TeamspeakInstanceQueryClients.RemoveAll(el => el.Instance.Equals(client.Instance));
}
}
// Create & Update
foreach (var instance in instances)
{
var queryClient = TeamspeakInstanceQueryClients.Find(el => el.Instance.Id == instance.Id);
if(queryClient == null)
{
//var test = ActivatorUtilities.CreateInstance<ApplicationDbContext>(serviceProvider);
//var dbContext = serviceProvider.GetService<ApplicationDbContext>();
//queryClient = new TeamspeakInstanceQueryClient(new InstancesControlRepository(ActivatorUtilities.CreateInstance<ApplicationDbContext>(serviceProvider)));
queryClient = new TeamspeakInstanceQueryClient(serviceProvider);
_ = queryClient.Connect(instance);
TeamspeakInstanceQueryClients.Add(queryClient);
}
else
{
_ = queryClient.CheckInstanceData(instance);
}
}
}
await _next(context);
}
TeamspeakInstanceQueryClient.cs
public partial class TeamspeakInstanceQueryClient : ITeamspeakInstanceQueryClient
{
private IInstanceControlRepository _instanceControlRepository;
private const short MAX_RETRYS = 3;
private const short TIME_TO_RETRY = 10;
private EventHandler OnConnected;
public Instance Instance { get; internal set; }
public TeamSpeakClient Client { get; internal set; }
public bool IsSelected { get; internal set; }
private short _connectionTrys = 0;
public TeamspeakInstanceQueryClient(IServiceProvider serviceProvider)
{
_instanceControlRepository = new InstancesControlRepository(ActivatorUtilities.CreateInstance<ApplicationDbContext>(serviceProvider));
Init();
}
}
InstancesControlRepository.cs
public class InstancesControlRepository : IInstanceControlRepository
{
private readonly ApplicationDbContext _applicationDbContext;
public InstancesControlRepository(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(option =>
option.UseMySql(
Configuration.GetConnectionString("DefaultConnection"),
mySqlOptions => mySqlOptions.ServerVersion(new System.Version(10, 4, 13), ServerType.MariaDb)
)
);
services.AddScoped<IInstanceControlRepository, InstancesControlRepository>();
services.AddScoped<IServerQueryRepository, ServerQueryRepository>();
What I´ve tried
I´ve tried to create the class with the service provider but it comes to the same error
I´ve tried to create the missing parameters with the service provider in the created class. But I need to inject the service provider which also creates the exception Cannot access a disposed of the object. Object name: 'IServiceProvider'.
I´ve tried to make the service provider static so it can´t be disposed but it is disposed.
It seems that instance of IServiceProvider is a scoped one and it is disposed when the scope ends (in the end of request I assume). You can try define singleton factory for your TeamspeakInstanceQueryClient and use it:
class ClientFactory
{
private IServiceProvider _sp { get; set; }
private IServiceScope _scope { get; set; }
public MyClass(IServiceProvider sp)
{
_sp = sp;
_scope = sp.CreateScope();
}
public TeamspeakInstanceQueryClient Create() => new TeamspeakInstanceQueryClient(_scope.ServiceProvider);
}
// register it as singleton
services.AddSingleton<ClientFactory>();
and use it in InvokeAsync:
var factory = serviceProvider.GetRequiredService<ClientFactory>();
queryClient = factory.Create();
P.S. this code can be improved vastly and is used only for demonstration purposes.

Bug in Autofac MEF integration: Instantiates component when only querying for metadata

There appears to be a bug in how Autofac handles service instantiation when integrating with MEF
The following test show that MEF does not instantiate the services before it have to. (In this case, I'm only querying for metadata)
[TestMethod]
public void Mef_DoesNotInstantiateService_WhenOnlyQueryingForMetadata()
{
var aggregateCatalog = CreateMefCatalog();
var container = new CompositionContainer(aggregateCatalog, true);
var serviceConsumer = container.GetExportedValue<ServiceConsumer>();
serviceConsumer.Services.Any(x => x.Metadata.Name == "Service1").Should().BeTrue();
}
The following test is failing, since Autfac is trying to create an instance of Service1 - which throws an exception in the constructor.
[TestMethod]
public void Autofac_DoesNotInstantiateService_WhenOnlyQueryingForMetadata()
{
var aggregateCatalog = CreateMefCatalog();
var builder = new ContainerBuilder();
builder.RegisterComposablePartCatalog(aggregateCatalog);
var container = builder.Build();
//Next line will throw an exception. (Autofac is instantiating the service, but should not)
var serviceConsumer = container.Resolve<ServiceConsumer>();
//Note: This test will never get here..
serviceConsumer.Services.Any(x => x.Metadata.Name == "Service1").Should().BeTrue();
}
Other code required by the tests
static AggregateCatalog CreateMefCatalog()
{
return new AggregateCatalog(new List<ComposablePartCatalog>
{
new AssemblyCatalog(Assembly.GetExecutingAssembly())
});
}
[Export]
class ServiceConsumer
{
[ImportMany]
public IEnumerable<Lazy<IService, INameMetadata>> Services { get; set; }
}
public interface IService { }
[Export(typeof (IService))]
[ExportMetadata("Name", "Service1")]
public class Service1 : IService
{
public Service1()
{
throw new Exception("This service should never be created");
}
}
public interface INameMetadata
{
string Name { get; }
}
BTW: I'm using the currently stable versions: Autofac 3.5.2 and Autofac.Mef 3.0.3

How do I use Autofac instead singleton in c#?

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>

Using Autofac with Dynamic Proxy that output message automatic

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.

Categories