I recently created a solution and thought I would try out the DryIoC container to handle dependency injection. Now, as with many other DI solutions that I have used, the default scope for object reuse is transient. This however seems to be posing a problem for the implementation of the repository pattern that I am using since DryIoC (and many other solutions) cannot register a binding as transient if the referenced class implements IDisposable. As a result, I have temporarily resorted to registering my repositories with Reuse.Singleton. This is definitely a code smell for me, so I was hoping that someone might have some advice on how to avoid this situation - it might be that I am doing a poor job of creating a repository for example.
Here is the code that I use to create the IoC container:
private static Container ConstructNewContainer()
{
var container = new Container(Rules.Default);
container.Register(Made.Of(() => SettingsFactory.CreateSettings()));
container.Register<IRepository<tblMailMessage>, MailMessageRepository>(Reuse.Singleton);
container.Register<IRepository<ProcessedMailMessages>, ProcessedMailMessageRepository>(Reuse.Singleton);
container.Register<IParser, EmailParser>();
container.Register<IMonitor, DatabaseMonitor>();
return container;
}
...and an example repository implementation:
public interface IRepository<T>
{
void Insert(T objectToInsert);
void Delete(int id);
void Update(T objectToUpdate);
void Save();
T GetById(long id);
IEnumerable<T> Get();
T Last();
bool Exists(int id);
}
public class MailMessageRepository : IRepository<tblMailMessage>, IDisposable
{
private bool _disposed;
private readonly CoreDataModel _model;
public MailMessageRepository()
{
_model = new CoreDataModel();
}
public void Delete(int id)
{
var objectToDelete = _model.tblMailMessages.Find(id);
if (objectToDelete != null) _model.tblMailMessages.Remove(objectToDelete);
}
public void Update(tblMailMessage objectToUpdate) => _model.Entry(objectToUpdate).State = EntityState.Modified;
public void Save() => _model.SaveChanges();
public IEnumerable<tblMailMessage> Get() => _model.tblMailMessages.ToList();
public tblMailMessage Last() => _model.tblMailMessages.OrderByDescending(x => x.DateSubmitted).FirstOrDefault();
public bool Exists(int id) => _model.tblMailMessages.SingleOrDefault(x => x.MailMessageID == id) != null;
public void Insert(tblMailMessage objectToInsert) => _model.tblMailMessages.Add(objectToInsert);
public tblMailMessage GetById(long id) => _model.tblMailMessages.SingleOrDefault(x => x.MailMessageID == id);
#region Dispose
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (!disposing)
{
_model.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
The documentation here explains why disposable transient is the problem and why DryIoc default behavior was selected this way. Basically, the behavior is to inform you about the problem and not just silently go with it.
Regarding other containers, there is no strong preference to particular disposable transients handling. Here is discussion related to Microsoft.Extensions.DependencyInjection with participation of Autofac, StructureMap and other containers developers.
Btw, DryIoc error message contains the tip how to opt-in the problem.
According to the documentation, you have 3 options:
Disallow to register disposable transient service. The default DryIoc behavior.
container.Register<X>(); // will throw exception
Allow to register disposable transient, but delegate the responsibility of disposing the service to container User.
container.Register<X>(setup: Setup.With(allowDisposableTransient: true));
// or allow globally for all container registrations:
var container = new Container(rules => rules.WithoutThrowOnRegisteringDisposableTransient());
container.Register<X>(); // works, but dispose is up to User
To track (store) disposable transient dependency in its owner reuse scope (if any), or to track resolved disposable transient in current Open Scope (if any).
container.Register<X>(setup: Setup.With(trackDisposableTransient: true));
// or track globally for all container registrations:
var container = new Container(rules => rules.WithTrackingDisposableTransients());
// will be tracked in XUser parent in singleton scope and disposed with container as all singletons
container.Register<XUser>(Reuse.Singleton);
container.Register<X>();
// or tracking in open scope
using (var scope = container.OpenScope())
scope.Resolve<X>; // will be disposed on exiting of using block
As you can see above, the default behavior expects you to explicitly dispose when using transient lifestyle.
But they left out the 4th option, which is to find another DI container. I have never used DryIoC, but this seems like too much to worry about that you don't have to with other containers. Normally, choosing the correct lifetime is what determines when to dispose an instance.
Related
I want use DryIoC as service locator, ie inject container instance to some service to get oppotunity create sub scope.
internal abstract class BaseService<TEntity, T> : IDisposable
where TEntity : class, IEntity<T>
where T : notnull
{
**protected bool isParallel = false;**
protected DataContext Context
=> **this.isParallel
? this.resolver.Value.Resolve<DataContext>()
:** this.current.Context;
**private readonly Lazy<IResolverContext> resolver;**
**private bool IsReadOnly => this.isParallel;**
protected BaseService(**IContainer container,** ICurrentDataContext current, IDeletingStrategy<TEntity> delete)
{
this.strategies =
new ()
{
Delete = delete
};
**this.resolver = new Lazy<IResolverContext>(() => container.OpenScope());**
this.current = current;
}
private void ReadOnlyCheck()
{
**if (this.IsReadOnly)
{
throw
new NotSupportedException("ToDo");
}**
}
protected virtual void Dispose(bool disposing)
{
if (this.wasDisposed)
{
return;
}
if (disposing)
{
**this.resolver.Value.Dispose();**
}
this.wasDisposed = true;
}
}
When I call any of CRUD methods, the following exception is thrown:
DryIoc.ContainerException: "code: Error.ContainerIsDisposed;
message: Container is disposed and should not be used: "container with scope {IsDisposed=true, Name=null}
with Rules with {TrackingDisposableTransients} and without {ThrowOnRegisteringDisposableTransient, VariantGenericTypesInResolvedCollection}
with FactorySelector=SelectLastRegisteredFactory
with Made={FactoryMethod=ConstructorWithResolvableArguments, PropertiesAndFields=} has been DISPOSED!
You may include Dispose stack-trace into the message via:
container.With(rules => rules.WithCaptureContainerDisposeStackTrace())""
But if I remove bold elements it works.
It is still hard to find the problem based on just the code provided.
I would guess that the container argument passed to BaseService constructor and used to lazily create the scoped resolver is disposed by the time when you're trying to create the actual resolver.
Also, the container passed to the BaseMessage is already the scoped container per the exception message, and maybe it is not what you are expecting.
You may add the rule container.With(rules => rules.WithCaptureContainerDisposeStackTrace()) mentioned in the exception to get the actual place where the container is being disposed.
I would like to initialize some dependencies resolved from the MassTransit serviceProvider in the same way Asp.Net Core does with the pipeline's middlewares.
In particular I would like to inspect the incoming message before the consumer is called and extract the tenant from it (I'm currently working on a multitenant web application with single database per tenant).
With this informations I need to initialize some scoped instances (Ef Core DbContext for example).
I know that I can inject them in the Consumer through constructor but this means that I must do that everytime I write a new one, so I suppose that a filter should be the right place (correct me if I'm wrong).
The problem raises when I need to access the current consumer scope to resolve the dependencies that I need. I was thinking that the behavior of the MassTransit' pipeline was similar to the Asp.Net one regarding middleware injection but I was probably wrong.
I haven't found any documentation on how to do that clearly without cluttering the code of the filter, so any suggestion is going to be really appreciated.
This is the filter that I need to modify:
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
public void Probe(ProbeContext context) { }
public async Task Send(T context, IPipe<T> next)
{
//Resolve scoped instance here and do something before Consumer is called
var connectionStringProvider = scope.GetService<IConnectionStringProvider>();
await next.Send(context);
}
}
public class RegistrationsDeliveredEventConsumer : IConsumer<IRegistrationsDelivered>
{
private readonly IConnectionStringProvider _connectionStringProvider;
public RegistrationsDeliveredEventConsumer(IConnectionStringProvider connectionStringProvider)
{
//This should be the same instance that has been resolved in the filter' Send() method
_connectionStringProvider = connectionStringProvider;
}
public async Task Consume(ConsumeContext<IRegistrationsDelivered> context)
{
}
}
This is a simplified example of my code but this should be enough
There's two facets to consider: 1) are filters registered as services/pulled from the service collection when using the ASP.NET Core integration and 2) what lifetime do the filters have if they are. I'm not familiar with the MassTransit ASP.NET Core integration, but it looks like you should be good based on a cursory review. You'll need to confirm that both of those requirements are met.
For dependency injection, in general, constructor injection is the way to go unless there's a very specific need to do something different, which does not seem to be the case here. In short, you need a constructor for your filter.
What exactly you need to inject is a function of the lifetime of the filter. If it has a transient lifetime, then you can inject your scoped dependencies directly. If it has a singleton lifetime, then you'll need to inject IServiceProvider instead, and do the following whenever you need to use one of those dependencies:
using (var scope = _serviceProvider.CreateScope())
{
var dep = scope.ServiceProvider.GetRequiredService<MyDependency>();
// do something with `dep`
}
Here's a draft... I'm sure there are missing pieces, so let me know if you have questions.
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
private readonly Func<string, IDbConnection> _dbContextAccessor;
public void Probe(ProbeContext context) { }
public TenantContextInitializerFilter(Func<string, IDbConnection> dbContextAccessor)
{
_dbContextAccessor = dbContextAccessor;
}
public async Task Send(T context, IPipe<T> next)
{
var tenantId = ""; // place holder
using (var dbContext = _dbContextAccessor(tenantId))
{
//... do db logic
}
await next.Send(context);
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IConnectionStringProvider>(
provider => null /* TODO figure out how to fetch scoped instance from a cache or some storage mechanism*/);
services.AddScoped(provider =>
{
IDbConnection Accessor(string tenantId)
{
if (provider.GetService<IConnectionStringProvider>()
.TryGetConnectionString(tenantId, out var connectionString, out var providerName))
return new SqlConnection(connectionString);
throw new Exception();
}
return (Func<string, IDbConnection>)Accessor;
});
}
}
Consider that I have configured EF with a .NET Core web app:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(...));
I can also download a package to support for example SQLite:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(...));
How can we allow a user to "select" the provider on app install? I mean - for example, in WordPress you can choose from a dropdown.
Is this possible in .NET Core? The only way I see is to restart the app only...
Here is an example on how you can implement a DbContextFactory or a DbContextProxy<T> which will create the correct provider and return it.
public interface IDbContextFactory
{
ApplicationContext Create();
}
public class DbContextFactory() : IDbContextFactory, IDisposable
{
private ApplicationContext context;
private bool disposing;
public DbContextFactory()
{
}
public ApplicationContext Create()
{
if(this.context==null)
{
// Get this value from some configuration
string providerType = ...;
// and the connection string for the database
string connectionString = ...;
var dbContextBuilder = new DbContextOptionsBuilder();
if(providerType == "MSSQL")
{
dbContextBuilder.UseSqlServer(connectionString);
}
else if(providerType == "Sqlite")
{
dbContextBuilder.UseSqlite(connectionString);
}
else
{
throw new InvalidOperationException("Invalid providerType");
}
this.context = new ApplicationContext(dbContextBuilder);
}
return this.context;
}
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing){
if (disposing){
disposing?.Dispose();
}
}
}
Also make sure you implement the disposable pattern as show above, so the context gets disposed as soon as the factory gets disposed, to prevent the DbContext remaining in memory longer than necessary and free unmanaged resources as soon as possible.
Finally register the factory as scoped, as you would the context itself:
services.AddScopedd<IDbContextFactory, DbContextFactory>();
A more advanced and generic/extendable approach is by creating a IDbContextProxy<T> class which uses a bit of reflection to get the correct constructor and the DbContextOptionsBuilder to it.
Also possible to create a IDbContextBuilder which abstracts the provider creation.
public class SqlServerDbContextBuilder IDbContextBuilder
{
public bool CanHandle(string providerType) => providerType == "SqlServer";
public T CreateDbContext<T>(connectionString)
{
T context = ... // Create the context here
return context;
}
}
Then you can pick the correct provider w/o a hard coded if/else or switch block just by doing
// Inject "IEnumerable<IDbContextBuilder> builders" via constructor
var providerType = "SqlServer";
var builder = builders.Where(builder => builder.CanHandle(providerType)).First();
var context = builder.CreateDbContext<ApplicationContext>(connectionString);
and adding new types of provider is as easy as adding the dependencies and an XxxDbContextBuilder class.
See here, here or here for more information about this and similar approaches.
I think you can use repositories which are using a db context you specified and you can pass a parameter to context constructor to choose the endpoint. I am not sure on this but it might work for your situation.
I followed this article for repository pattern, I recommend to read it :)
http://cpratt.co/generic-entity-base-class/
I've posted a general guideline question when it comes to IDisposable objects and using Autofac here: Dependency Injection and IDisposable. Unfortunately, I did not account for one particular scenario in our project and it's really a separate question that stands on its own, so will ask it here:
I have a Repository object that manages the life of a session object inside it. Thus, Repository object is IDisposable and destroys session (Repository is injected with a factory delegate at construction, instantiates session during first usage, and destroys session in IDisposable if session is not null). Per reference to StackOverflow question above, I understand that any object that is injected with my Repository object should not be implementing IDisposable since Autofac will handle disposing of my repositories, if it is injecting them.
Per mentioned StackOverflow thread, I've started cleaning up IDisposable usage from my objects until I stumbled upon NotificationPublisher class shown below. There are a few places like it where classes are injected with implementation of IComponentContext that acts as a factory. Resolution happens manually in a function, because the codebase does not know what handler needs to be injected until the runtime.
public class NotificationPublisher : INotificationPublisher
{
private readonly IComponentContext _container;
private readonly INotificationManager _notificationManager;
public NotificationPublisher(IComponentContext container,
INotificationManager notificationManager)
{
_container = container;
_notificationManager = notificationManager;
}
public IEnumerable<IAlertSubscription> Publish(Account account,
INotificationInitiator owner, INotificationEntity entity,
Int32 severity, CheckCycleContext monitoringContext)
{
var alertSubscriptions =
_notificationManager.GetAlertSubscriptions(account, owner, severity);
foreach (var alertSubscription in alertSubscriptions)
{
var destination = alertSubscription.GetConsumer();
Type handlerType = typeof (INotificationHandler<,>)
.MakeGenericType(entity.GetType(), destination.GetType());
using (var handler =
(INotificationCustomHandler)_container.ResolveOptional(handlerType))
{
if (handler == null) continue;
try
{
Retry.Execute(() => (handler).Send(entity, destination), 3, 500);
monitoringContext.Record(CheckCycleContext.CycleSeverity.Information,
string.Format("NotificationPublisher.Publish:{0}/{1}",
entity.GetType().Name, destination.GetType().Name), "Success");
}
catch (Exception ex)
{
monitoringContext.Record(CheckCycleContext.CycleSeverity.Error,
string.Format("NotificationPublisher.Publish:{0}/{1}",
entity.GetType().Name, destination.GetType().Name), ex.Message, ex,
new {entity, destination});
}
}
}
return alertSubscriptions;
}
}
I'm assuming that since INotificationCustomHandler is manually resolved, it must be manually disposed with the using statement, becuase implementations of INotificationCustomHandler are injected with implementations of IManager that is injected with implementations of IRepository.
Thus, in this situation I need to propagate IDisposable throughout my codebase which goes against what I was suggested in the prior SO question.
How do I manually resolve objects via factories when needed and yet let Autofac handle disposing?
When Autofac resolve a component that implements IDisposable this one will be linked with scope that has been configured when you registered it. When this scope will be disposed, all linked components will be disposed too. See http://autofac.readthedocs.org/en/latest/lifetime/disposal.html for more information.
In your case, if INotificationCustomHandler is registered as InstancePerDependency (default) or InstancePerLifetimeScope, INotificationCustomHandler resolved by _container, will be disposed when _container will be disposed too.
If this is what you want, you don't have to call .Dispose on these components.
If you want to manually control the lifetime of your objects, you can create your own lifetime scope.
using(ILifetimeScope scope = this._container.BeginLifetimeScope())
{
var handler = (INotificationCustomHandler)scope.ResolveOptional(handlerType);
if(handler != null)
{
Retry.Execute(() => handler.Send(entity, destination));
}
} // handler will be disposed here if needed
you should also have a look to owned instance which acts like a mini factory.
if(!container.ComponentRegistry.IsRegistered(new TypedService(handlerType)))
{
continue;
}
Type handlerFactoryType = typeof(Func<>).MakeGenericType(
typeof(Owned<>).MakeGenericType(handlerType));
var handlerFactory = (Func<Owned<INotificationCustomHandler>>)container
.Resolve(handlerFactoryType);
using(Owned<INotificationCustomHandler> ownedHandler = handlerFactory())
{
INotificationCustomHandler handler = ownedHandler.Value;
Retry.Execute(() => handler.Send(entity, destination), 3, 500);
} // handler will be disposed here
I'm unsure of how to fix my current situation. I'm attempting to create a task:
public class whatever
{
[Dependency]
public IReportingBL ReportingBL { get; set; }
private whatever()
{
...task factory creation, etc.
}
private readonly static Lazy<whatever> _instance = new Lazy<whatever>(() => new whatever());
public static whatever Instance { get { return _instance.Value; }
public Task GetStuff()
{
return _taskFactory.StartNew(() =>
{
return ReportingBL.Method1;
});
}
}
ReportingBL doesn't get resolved. If I create a new instance of ReportingBL inside the thread then the layers below it don't get resolved.
How do I go about getting unity to work in this situation?
You are applying the Singleton Design Pattern. This is a pattern that is frown upon and considered an anti-pattern by some. In Dependency Injection terminology the Singleton pattern can be considered an Ambient Context, which is a pattern that should hardly ever be used in the context of Dependency Injection.
The Singleton Design Pattern does not work well with Dependency Injection, because:
With Dependency Injection it is the application's Composition Root who is in control of creating instances and caching them; not the instance itself.
Having consumers depend on the public Instance field, causes the consumers to violate the Dependency Inversion Principle and disallows the instance from being replaced, mocked, decorated or intercepted. This hinders maintainability and testability of your application.
Further more, in your code I don't see any calls to the Unity DI framework. Please remember that a DI container is not a magical tool that will allow classes to be initialized 'by them selves'. In your code you new up whatever directly; Unity is not involved in this. Unity (or any DI library for that matter) can only auto-wire the object if it is in control of it. In other words, you will have to call container.Resolve<whatever>() for Unity to build up your instance.
Although you could call container.Resoolve from within the Lazy<T> factory delegate, this forces the class to take a dependency on the container itself, which is commonly referred to as the Service Locator anti-pattern.
Instead, I propose the following changes to your design:
Use constructor injection instead of property injection. Property injection leads to Temporal Coupling.
Make the Composition Root and the container responsible for wiring up object graphs.
Stay away from the Singleton design pattern; use the container's Singleton Lifestyle instead.
This results in the following code:
public interface IWhatever
{
Task GetStuff();
}
public class Whatever : IWhatever
{
private readonly IReportingBL reportingBL;
public whatever(IReportingBL reportingBL) {
this.reportingBL = reportingBL;
}
public Task GetStuff() {
return _taskFactory.StartNew(() => {
return ReportingBL.Method1;
});
}
}
// Some consumer of whatever
public class MyController : Controller
{
private readonly IWhatever whatever;
public MyController(IWhatever whatever) {
this.whatever = whatever;
}
public ActionResult Index() {
return View(this.whatever.GetStuff());
}
}
In your composition root, you can configure the class as follows:
var container = new UnityContainer();
container.RegisterType<IReportingBL, ReportingBL>(
new ContainerControlledLifetimeManager());
container.RegisterType<IWhatever, Whatever>(
new ContainerControlledLifetimeManager());
var controller = container.Resolve<MyController>();
controller.Index();