How to call class with dependency injection (DI) - c#

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.

Related

ASP.NET Core blazor dependency injection error: System.NullReferenceException?

Using SqlSugar ORM, based on blazor, dependency injection business service, an error is reported when calling, and it is empty。
SqlSugarService:
public static class SqlSugarService
{
private static readonly ILog log = LogManager.GetLogger(typeof(SqlSugarService));
public static void AddSqlSugarSevice(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddScoped<ISqlSugarClient>(o =>
{
var listConfig = new List<ConnectionConfig>();
listConfig.Add(new ConnectionConfig
{
DbType = DbType.SqlServer,
ConnectionString = "Server=.\\SQLEXPRESS;DataBase=Test;Uid=sa;Pwd=123456",
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
});
var dbContext = new SqlSugarClient(listConfig);
return dbContext;
});
}
}
The interface:
public interface IReportRepository
{
public DataTable GetTest(string sql);
}
Interface implementation:
public class ReportRepository : IReportRepository
{
private ISqlSugarClient _dbBase;
public ReportRepository(ISqlSugarClient sqlSugar)
{
_dbBase = sqlSugar;
}
public DataTable GetTest(string sql)
{
return _dbBase.Ado.GetDataTable(sql);
}
}
Injection:
services.AddSqlSugarSevice();
services.TryAddTransient<IReportRepository, ReportRepository>();
Used in component code:
public partial class Report
{
[Inject]
public IReportRepository ReportService { get; set; }
public Report()
{
ReportService.GetTest("select * from test");
}
}
ERROR :
System.NullReferenceException,HResult=0x80004003,Message=Object reference not set to an instance of an object, Source=MyReport

Register multiple singletons with same interface but different constructor parameters values

I got stuck and need some advice or pointer to a solution.
A web API with ASP.NET Core 3.1
Startup.cs
services.AddSingleton<ITopicClient>(s => new TopicClient({connectionstring},{topic}));
TopicRepository.cs
public class TopicRepository : ITopicRepository
{
private readonly ITopicClient _topicClient1;
private readonly ITopicClient _topicClient2;
public TopicRepository (ITopicClient topicClient1, ITopicClient topicClient2)
{
_topicClient1 = topicClient1;
_topicClient2 = topicClient2;
}
public async Task<Response> SendToTopicAsync(string message, string topic)
{
if( topic == "topic1")
await _topicClient1.send(message);
else if (topic == "topic2")
await _topicClient2.send(message);
}
}
TopicClient.cs in a shared library
public TopicClient(string serviceBusConnectionString, string topicName)
{
_topicClient = new TopicClient(_serviceBusConnectionString,topicName);
}
I need to send message to different topics. I would like to register services with different topic names in startup.cs. I want to reuse topicClient connection.
services.AddSingleton(s => new TopicClient({connectionstring},{topic1}));
services.AddSingleton(s => new TopicClient({connectionstring},{topic2}));
How can I achieve this by registering singleton instances of same type using same interface ?
Thank you in advance!
You could use a client resolver that holds your registered clients with a wrapper around the client.
First create a wrapper around your client with a name or enum for how to resolve it. As I'm not a fan of magic strings I decided to go with an enum in the example.
// Wrapper for your TopicClients
public interface ICustomTopicClient
{
public ITopicClient TopicClient { get; }
public TopicName TopicName { get; }
}
// Implement the ICustomTopicClient interface
public class CustomTopicClient : ICustomTopicClient
{
public ITopicClient TopicClient { get; }
public TopicName TopicName { get; }
public CustomTopicClient(ITopicClient topicClient, TopicName topicName)
{
TopicClient = topicClient;
TopicName = topicName;
}
}
// Enum for how to resolve the requested TopicClient
public enum TopicName
{
Topic1 = 0,
Topic2 = 1
}
// Register all ICustomTopicClients in your container
services.AddSingleton<ICustomTopicClient>(s => new CustomTopicClient(new TopicClient({connectionstring},{topic}), TopicName.Topic1));
services.AddSingleton<ICustomTopicClient>(s => new CustomTopicClient(new TopicClient({connectionstring},{topic2}), TopicName.Topic2));
Then you create a resolver that holds all custom clients.
You inject the collection of clients from the container and create a dictionary with a public method to resolve the clients.
public interface IMessageClientResolver
{
ITopicClient ResolveClient(TopicName name);
}
public class MessageClientResolver : IMessageClientResolver
{
private readonly Dictionary<TopicName, ITopicClient> topicClients;
public MessageClientResolver(IEnumerable<ICustomTopicClient> clients)
{
topicClients = clients.ToDictionary(k => k.TopicName, v => v.TopicClient);
}
public ITopicClient ResolveClient(TopicName name)
{
topicClients.TryGetValue(name, out var client);
if (client is null)
throw new ArgumentException(nameof(client));
return client;
}
}
Register the resolver to the container.
services.AddSingleton<IMessageClientResolver, MessageClientResolver>();
And then use it like this:
public class Foo
{
private readonly ITopicClient topicClient;
private readonly ITopicClient topicClient2;
public Foo(IMessageClientResolver clientResolver)
{
topicClient = clientResolver.ResolveClient(TopicName.Topic1);
topicClient2 = clientResolver.ResolveClient(TopicName.Topic2);
}
}
You can use the same pattern and extend the resolver with IQueueClients. And add a resolve method to return the IQueueClient by a QueueName enum.
You can already register multiple instances as the same interface, so when you do:
services.AddSingleton<ITopicClient>(_ => new TopicClient("topic1"));
services.AddSingleton<ITopicClient>(_ => new TopicClient("topic2"));
you already added two instances to the container.
It is just when you resolve interface ITopicClient, you always get the last added instance. For example, if you resolve:
// instance = topic2
var instance = container.GetService<ITopicClient>();
If you need all instances, you should resolve / inject IEnumerable<ITopicClient>.
class TopicRepository
{
public TopicRepository(IEnumerable<ITopicClient> clients)
{
// clients contains topic1 and topic2
}
}

Autofac not resolving interfaces in other project

I'm trying to write a generic command bus (Part of class library) that uses different commands and handlers in each of my services.
The following code produces the following exception:
System.Exception: Command does not have any handler RegisterUserCommand
I was under the impression passing the the ExecutingAssemblies of my UserService would allow the Container to resolve the handler in my UserService but apparently not.
Am I doing something wrong?
CommandBus:
public interface ICommandBus
{
void Send<T>(T Command) where T : ICommand;
}
public class CommandBus : ICommandBus
{
private IContainer Container { get; set; }
public CommandBus(Assembly assembly)
{
Container = new CommandBusContainerConfig().Configure(assembly);
}
public void Send<TCommand>(TCommand command) where TCommand : ICommand
{
var handlers = Container.Resolve<IEnumerable<ICommandHandler<TCommand>>>().ToList();
if (handlers.Count == 1)
{
handlers[0].Handle(command);
}
else if (handlers.Count == 0)
{
throw new System.Exception($"Command does not have any handler {command.GetType().Name}");
}
else
{
throw new System.Exception($"Too many registred handlers - {handlers.Count} for command {command.GetType().Name}");
}
}
}
ContainerBuilder:
public class CommandBusContainerConfig : IContainerConfig
{
public IContainer Configure(Assembly executingAssembly)
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(executingAssembly)
.Where(x => x.IsAssignableTo<ICommandHandler>())
.AsImplementedInterfaces();
builder.Register<Func<Type, ICommandHandler>>(c =>
{
var ctx = c.Resolve<IComponentContext>();
return t =>
{
var handlerType = typeof(ICommandHandler<>).MakeGenericType(t);
return (ICommandHandler)ctx.Resolve(handlerType);
};
});
return builder.Build();
}
}
In my UserService(ASP.Net Core 3), which is a different project that references the above CommandBus:
public class RegisterUserCommand : ICommand
{
public readonly string Name;
public readonly Address Address;
public string MobileNumber;
public string EmailAddress;
public RegisterUserCommand(Guid messageId, string name, string mobileNumber, string emailAddress, Address address)
{
Name = name;
Address = address;
MobileNumber = mobileNumber;
EmailAddress = emailAddress;
}
CommandHandler:
public class RegisterUserComnmandHandler : ICommandHandler<RegisterUserCommand>
{
public void Handle(RegisterUserCommand command)
{
Console.WriteLine($"Create user {command.Name} {command.MobileNumber} - handler");
}
}
Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ICommandBus>(new CommandBus(Assembly.GetExecutingAssembly()));
}
Controller:
private readonly ICommandBus _commandBus;
public UsersController(ICommandBus commandBus) {
_commandBus = commandBus;
}
// POST api/values
[HttpPost]
public async Task<IActionResult> Post([FromBody]RegisterUserCommand command)
{
if (ModelState.IsValid)
{
CommandBus commandBus = new CommandBus(Assembly.GetExecutingAssembly());
commandBus.Send(command);
_commandBus.Send(Command); //Same result as above
// return result
return Ok(command);
}
return BadRequest();
}
Thanks,
The main error is here :
builder.RegisterAssemblyTypes(executingAssembly)
.Where(x => x.IsAssignableTo<ICommandHandler>())
.AsImplementedInterfaces();
RegisterUserComnmandHandler is not a ICommandHandler but a ICommandHandler<RegisterUserCommand>. Instead of IsAssignableTo<> method you can use the IsClosedTypeOf which is an Autofac extension which do exactly what you can.
builder.RegisterAssemblyTypes(executingAssembly)
.Where(x => x.IsClosedTypeOf(typeof(ICommandHandler<>)))
.AsImplementedInterfaces();
By the way, in your code sample you are using another Container. Most of the time it is always simple to have a single container for the whole application. To get things organised you can use autofac module. You are also resolving straight from the container and not using scope this means that your instance graph won't be disposed at the end of the operation but will stay for the whole lifetime of the container.
In your controller, I saw that you are building a new CommandBus for each request, which will create a new container. Building a new container is a heavy operation and you should avoid doing it often but only once of the startup of the application.
Also I don't get the point of this registration :
builder.Register<Func<Type, ICommandHandler>>(c =>
{
var ctx = c.Resolve<IComponentContext>();
return t =>
{
var handlerType = typeof(ICommandHandler<>).MakeGenericType(t);
return (ICommandHandler)ctx.Resolve(handlerType);
};
});
It doesn't looks you need it and it seems useless to me
This took me a while to figure out. But my CommandHandler interface was incorrectly defined. It should look like:
public interface ICommandHandler { }
public interface ICommandHandler<T> : ICommandHandler where T : ICommand
{
void Handle(T command);
}
}
When trying to resolve the CommandHandler in the Autofac configuration class, the .Where(x => x.IsAssignableTo<ICommandHandler>()) was failing because the class was assignable to ICommandHandle<T> not ICommandHandler

Unity Container With Repository and UnitOfWork

I have an ASP.NET WEB API project that using the repository and unit of work patterns along with UnityContainer. I registered all my repositories and unit of work classes with PerRequestLifeTimeManager and everything is working great. Every client request to server work in a single transaction separated from other client requests. After the request is returned to the client, the DbContext is disposed.
My problem begins when I need to to perform heavy actions after I return a response to the client. The DbContext is disposed and I can't request the Unity Container for a new instance (Since no HttpContext exist ).
I read this post PerRequestLifetimeManager can only be used in the context of an HTTP request
And tried to implement 2 different UnityContainer
WebContainer - Register all classes using PerRequestLifeTimeManager
CorContainer - Register all classes using PerResolveLifetimeManager
I thought I solved the problem but I noticed that each repository I request from the CoreContainer using a different DbContext. Also the UnitOfWork has a different DbContext. This off course causing many error in my code.
This is the code I'm using to register my entities in UnityContainer. I have 2 Databases so some of the repository using the DbContext of the first database, and the other using the second DbContext. My UnitOfWork using both DbContexts
public static class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> perRequestContainer =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterCommonTypes<PerRequestLifetimeManager>(container);
return container;
});
private static Lazy<IUnityContainer> perResolveContainer =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterCommonTypes<PerResolveLifetimeManager>(container);
//CNSDeployerService can
container.RegisterType<ICNSDeployerService, CNSDeployerService>(new PerResolveLifetimeManager());
return container;
});
public static IUnityContainer WebContainer => perRequestContainer.Value;
public static IUnityContainer CoreContainer => perResolveContainer.Value;
#endregion
/// <summary>
/// Please notice this configuration exist only inside the scope of a single request
/// See UnityWebApiActivator
/// </summary>
/// <param name="container"></param>
public static void RegisterCommonTypes<T>(IUnityContainer container) where T: LifetimeManager
{
container.RegisterType<ApplicationDbContext, ApplicationDbContext>(Activator.CreateInstance<T>());
container.RegisterType<DbContext, ApplicationDbContext>(Activator.CreateInstance<T>());
container.RegisterType<capNsubContext, capNsubContext>(Activator.CreateInstance<T>());
container.RegisterType<IDataContextAsync, capNsubContext>("capNsubContext", Activator.CreateInstance<T>());
container.RegisterType<IDataContextAsync, LinetServerContext>("linnetDataContext", Activator.CreateInstance<T>());
container.RegisterType<IRepository<Entity>, Repository<Entity>>(Activator.CreateInstance<T>());
container.RegisterType<IRepositoryAsync<VideoTargetLanguage>, Repository<VideoTargetLanguage>>(Activator.CreateInstance<T>());
//Unity understand array by defaults so we just need to map it to IEnumerable
container.RegisterType<IEnumerable<IDataContextAsync>, IDataContextAsync[]>();
container.RegisterType<IUnitOfWorkAsync, UnitOfWork>(Activator.CreateInstance<T>());
container.RegisterType<UserManager<ApplicationUser>>(Activator.CreateInstance<T>());
container.RegisterType<RoleManager<IdentityRole>>(Activator.CreateInstance<T>());
container.RegisterType<AccountController>(Activator.CreateInstance<T>());
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(Activator.CreateInstance<T>());
container.RegisterType<IOrderService, OrderService>(Activator.CreateInstance<T>());
container.RegisterType<IFFMpegService, FFMpegService>(Activator.CreateInstance<T>());
container.RegisterType<IVideoService, VideoService>(Activator.CreateInstance<T>());
container.RegisterType<IOrderItemService, OrderItemService>(Activator.CreateInstance<T>());
container.RegisterType<ILanguageService, LanguageService>(Activator.CreateInstance<T>());
container.RegisterType<IUserService, UserService>(Activator.CreateInstance<T>());
container.RegisterType<ICNSCaptionsService, CNSCaptionsService>(Activator.CreateInstance<T>());
container.RegisterType<ICNSTranslationsService, CNSTranslationsService>(Activator.CreateInstance<T>());
container.RegisterType<ICNSCapMoviesService, CNSMovieService>(Activator.CreateInstance<T>());
container.RegisterType<HttpClient, HttpClient>(Activator.CreateInstance<T>());
container.RegisterType<SimpleRefreshTokenProvider, SimpleRefreshTokenProvider>(Activator.CreateInstance<T>());
var capNsubEntityTypes = GetEntityFrameworkEntityTypesByContext<capNsubContext>();
var linnetEntityTypes = GetEntityFrameworkEntityTypesByContext<LinetServerContext>();
RegisterEntitiesRepostiories<T>(container, capNsubEntityTypes, "capNsubContext");
RegisterEntitiesRepostiories<T>(container, linnetEntityTypes, "linnetDataContext");
}
private static void RegisterEntitiesRepostiories<T>(IUnityContainer container, IEnumerable<Type> entities, string contextName)
where T:LifetimeManager
{
var iGenericRepositoryTypes = new[] { typeof(IRepositoryAsync<>), typeof(IRepository<>) };
foreach (var iGenericRepositoryType in iGenericRepositoryTypes)
{
foreach (var entityType in entities)
{
var iSpecificRepositoryType = iGenericRepositoryType.MakeGenericType(entityType);
var genericRepositoryType = typeof(Repository<>);
var specificRepositoryType = genericRepositoryType.MakeGenericType(entityType);
container.RegisterType(iSpecificRepositoryType, Activator.CreateInstance<T>(), new InjectionFactory(c =>
{
return Activator.CreateInstance(specificRepositoryType, c.Resolve<IDataContextAsync>(contextName), c.Resolve<IUnitOfWorkAsync>());
}));
}
}
}
private static IEnumerable<Type> GetEntityFrameworkEntityTypesByContext<T>() where T : DataContext
{
var capNsubContextType = typeof(T);
var capNsubDataAssembly = Assembly.GetAssembly(capNsubContextType);
var ef6EntityType = typeof(Repository.Pattern.Ef6.Entity);
return capNsubDataAssembly.GetTypes()
.Where(t => String.Equals(t.Namespace, capNsubContextType.Namespace, StringComparison.Ordinal) &&
t.IsSubclassOf(ef6EntityType));
}
}
[System.Web.Http.Authorize(Roles = "admin")]
[System.Web.Http.RoutePrefix("api/job")]
public class JobController : BaseApiController {
[System.Web.Http.Route("Create", Name = "Create")]
[System.Web.Http.HttpPost]
public IHttpActionResult Create(JobBindingModel createJobModal)
{
//We have to use the CoreContainer since cnsDeployer scope runs outside of the request
var cnsDeployer = UnityConfig.CoreContainer.Resolve<ICNSDeployerService>();
if (!ModelState.IsValid)
{
return BadRequest();
}
try
{
//This runs in the backround after we return the response to client
cnsDeployer.Deploy(createJobModal.ItemIds);
return Ok();
}
catch(Exception err)
{
return InternalServerError(err);
}
}
}
public class CNSDeployerService : ICNSDeployerService
{
private readonly IOrderItemService orderItemService;
private readonly ICNSCapMoviesService cnsMoviesService;
private readonly ICNSTranslationsService cnsTranslationsService;
private readonly IFFMpegService ffMpegService;
private readonly IUnitOfWorkAsync unitOfWorkAsync;
private readonly IVideoService videoService;
public CNSDeployerService(IOrderItemService orderItemService,
ICNSCapMoviesService cnsCapMoviesService,
ICNSTranslationsService cnsTranslationsService,
IFFMpegService ffMpegService,
IUnitOfWorkAsync unitOfWorkAsync,
IVideoService videoService)
{
this.orderItemService = orderItemService;
this.cnsMoviesService = cnsCapMoviesService;
this.cnsTranslationsService = cnsTranslationsService;
this.ffMpegService = ffMpegService;
this.unitOfWorkAsync = unitOfWorkAsync;
this.videoService = videoService;
}
public void Deploy(IEnumerable<Guid> orderItemIds)
{
try
{
InnerDeploy(orderItemIds);
}
catch
{
unitOfWorkAsync.Dispose();
}
}
private void InnerDeploy(IEnumerable<Guid> orderItemIds)
{
var orderItems = orderItemService.Queryable()
.Where(orderItem => orderItemIds.Any(itemId => orderItem.Id == itemId)
&& orderItem.IsInProcessQueue == false
&& !orderItem.TranslationId.HasValue)
.ToList();
if (orderItems.Count == 0)
{
unitOfWorkAsync.Dispose();
throw new ArgumentNullException("No valid orders was provided");
}
foreach ( var orderItem in orderItems)
{
orderItem.IsInProcessQueue = true;
orderItemService.Update(orderItem);
}
unitOfWorkAsync.SaveChanges();
var translationId = Guid.NewGuid();
var movieId = Guid.NewGuid();
var connectedMoviePath = cnsMoviesService.GetMoviePath(movieId);
var videosUrlList = orderItems
.Select(orderItem => orderItem.VideosTable.VideoUrl)
.ToList();
//Don't await on this task since we want concat to continue after request is returned
Task.Run(async () =>
{
try
{
await ffMpegService.ConcatVideos(videosUrlList, connectedMoviePath);
VideoUtils.CreateVideoImageAndReturnPath(connectedMoviePath);
var videosTotalDuration = videoService.GetVideosTotalDuration(orderItemIds);
var durationInSeconds = Convert.ToInt32((int)(videosTotalDuration / 1000));
await cnsMoviesService.CreateMovieRecordAsync(movieId, durationInSeconds);
await cnsTranslationsService.CreateTranslationRecordAsync(movieId, translationId, language: 1);
var index = 0;
foreach (var orderItem in orderItems)
{
orderItem.TranslationId = translationId;
orderItem.TranslationIndex = index++;
orderItem.IsInProcessQueue = false;
orderItemService.Update(orderItem);
}
await unitOfWorkAsync.SaveChangesAsync();
}
catch (Exception err)
{
//TODO: Handle error
}
finally
{
//Dispose db context
unitOfWorkAsync.Dispose();
}
});
}
}

Autofac resolve instace in current web request scope

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.

Categories