Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid singleton repository (DryIoc) when using dependency injection

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
}
like image 595
spuriousGeek Avatar asked Sep 15 '25 20:09

spuriousGeek


1 Answers

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.

like image 75
dadhi Avatar answered Sep 17 '25 11:09

dadhi