使用依赖项注入时,请避免使用单例存储库(DryIoc)

时间:2017-09-26 11:05:14

标签: c# dependency-injection singleton repository-pattern dryioc

我最近创建了一个解决方案,并且认为我会尝试使用DryIoC容器来处理依赖注入。现在,与我使用的许多其他DI解决方案一样,对象重用的默认范围是瞬态。然而,这似乎对我正在使用的存储库模式的实现造成问题,因为如果引用的类实现IDisposable,DryIoC(以及许多其他解决方案)无法将绑定注册为瞬态。因此,我暂时使用 Reuse.Singleton 注册我的存储库。这绝对是我的代码味道,所以我希望有人可能会对如何避免这种情况提出一些建议 - 例如,我可能在创建存储库方面表现不佳。

以下是我用来创建IoC容器的代码:

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;
}

...以及示例存储库实现:

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
}

2 个答案:

答案 0 :(得分:2)

根据the documentation,您有3个选项:

  1. 禁止注册一次性瞬态服务。 默认的DryIoc行为。

    container.Register<X>(); // will throw exception  
    
  2. 允许注册一次性瞬态,但将处理服务的责任委托给容器用户。

    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
    
  3. 在其所有者重用范围(如果有)中跟踪(存储)一次性瞬态依赖关系,或跟踪当前开放范围(如果有)中已解决的一次性瞬态。

    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
    
  4. 正如您在上面所看到的,默认行为要求您在使用瞬态生活方式时明确处置。

    但他们遗漏了第四个选项,即找到另一个DI容器。我从未使用过DryIoC,但这似乎太过于担心您不必使用其他容器。通常,选择正确的生命周期决定何时处置实例。

答案 1 :(得分:1)

文档here解释了为什么一次性瞬态是问题以及为什么以这种方式选择DryIoc默认行为。基本上,行为是告知您有关问题的信息,而不是默默地使用它。

关于其他容器,对特定的一次性瞬态处理没有强烈的偏好。以下是与Microsoft.Extensions.DependencyInjection相关的讨论,其中包括Autofac,StructureMap和其他容器开发人员。

顺便说一句,DryIoc错误消息包含如何选择加入问题的提示。