如何使用依赖注入来处理资源

26 浏览
0 Comments

如何使用依赖注入来处理资源

我正在使用StructureMap来解决对我的存储库类的引用。我的存储库接口实现了IDisposable,例如。

public interface IMyRepository : IDisposable
{
  SomeClass GetById(int id);
}

使用Entity Framework实现接口:

public MyRepository : IMyRepository
{
    private MyDbContext _dbContext;
    public MyDbContext()
    {
        _dbContext = new MyDbContext();
    }
    public SomeClass GetById(int id)
    {
        var query = from x in _dbContext
                    where x.Id = id
                    select x;
        return x.FirstOrDefault();
    }
    public void Dispose()
    {
        _dbContext.Dispose();
    }
}

无论如何,正如我所提到的,我正在使用StructureMap来解决IMyRepository。因此,何时,在哪里以及如何调用我的dispose方法?

admin 更改状态以发布 2023年5月24日
0
0 Comments

如果你想做得正确,我建议你做一些改变:

1-不要在仓库中有数据上下文的私有实例。如果你正在使用多个仓库,则最终会有多个上下文。

2-为了解决上述问题-在工作单元中包装上下文。通过ctor将工作单元传递给仓库:public MyRepository(IUnitOfWork uow)

3-让工作单元实现IDisposable。工作单元应在请求开始时“newed up”,因此应在请求完成时进行处置。仓库不应实现IDisposable,因为它不直接使用资源-它只是在减轻资源。DataContext /工作单元应实现IDispoable。

4-假设您正在使用Web应用程序,则不需要显式调用dispose-我重复一遍,您不需要显式调用dispose方法。StructureMap有一个名为HttpContextBuildPolicy.DisposeAndClearAll();的方法。它的作用是调用实现IDisposable的任何HTTP范围对象的“Dispose”方法。将此调用放入Application_EndRequest(Global.asax)。此外-我相信还有一种更新的方法,称为ReleaseAllHttpScopedObjects或其他名称-无法记住名称。

0
0 Comments

警告:请注意,我的观点已经改变,您应该认为以下建议已过时。请查看此答案以获取更新后的观点:https://stackoverflow.com/a/30287923/264697


虽然 DI 框架可以为您管理对象的生命周期,一些框架甚至可以在您使用完对象后为您销毁对象,但这使得对象的处理过于隐式化。因此,IDisposable 接口的创建是因为有必要对资源进行确定性清理。因此,在 DI 上下文中,我个人喜欢使这种清理非常明确化。当您明确指定时,基本上有两个选择: 1. 配置 DI 来返回瞬态对象,并自己处理这些对象的释放。2. 配置工厂并指示工厂创建新实例。

我更喜欢第二种方法,因为特别是在进行依赖注入时,您的代码并不像它本来应该那么干净。例如,请参考以下代码:

public sealed class Client : IDisposable
{
    private readonly IDependency dependency;
    public Client(IDependency dependency)
    {
        this. dependency = dependency;
    }
    public void Do()
    {
        this.dependency.DoSomething();
    }
    public Dispose()
    {
        this.dependency.Dispose();
    }
}

虽然此代码明确释放了依赖项,但读者可能会感到疑惑,因为通常情况下,只有资源的所有者才能处理资源的释放。显然,当注入它时,Client 成为了资源的所有者。

因此,我更喜欢使用工厂。例如,请看以下示例:

public sealed class Client
{
    private readonly IDependencyFactory factory;
    public Client(IDependencyFactory factory)
    {
        this.factory = factory;
    }
    public void Do()
    {
        using (var dependency = this.factory.CreateNew())
        {
            dependency.DoSomething();
        }
    }
}

此示例与以前的示例具有完全相同的行为,但请看一下 Client 类不再需要实现 IDisposable,因为它在 Do 方法中创建并处理资源的释放。

注入工厂是最明确的方式(最少惊讶的路径)来做到这一点。这就是我为什么更喜欢这种样式的原因。其中的一个缺点是,您通常需要定义更多的类(用于工厂),但我个人并不介意。


RPM1984 要求提供一个更具体的示例。

我不会让存储库实现 IDisposable,但我会让工作单元实现 IDisposable,控制/包含存储库,并拥有一个知道如何创建新工作单元的工厂。有了这个想法,上面的代码看起来像这样:

public sealed class Client
{
    private readonly INorthwindUnitOfWorkFactory factory;
    public Client(INorthwindUnitOfWorkFactory factory)
    {
        this.factory = factory;
    }
    public void Do()
    {
        using (NorthwindUnitOfWork db = 
            this.factory.CreateNew())
        {
            // 'Customers' is a repository.
            var customer = db.Customers.GetById(1);
            customer.Name = ".NET Junkie";
            db.SubmitChanges();
        }
    }
}

在我使用的设计中,并在这里进行描述,我使用一个具体的 NorthwindUnitOfWork 类包装了一个作为底层 LINQ 提供程序的通道 (比如LINQ to SQL 或 Entity Framework) 的 IDataMapper。总之,该设计如下:

  1. 在客户端中注入一个 INorthwindUnitOfWorkFactory
  2. 那个工厂的特定实现创建一个具体的 NorthwindUnitOfWork 类,并将 O/RM 特定的 IDataMapper 类注入其中。
  3. NorthwindUnitOfWork 实际上是一个类型安全的 IDataMapper 包装器,NorthwindUnitOfWorkIDataMapper 请求存储库,并将请求转发到提交更改和处理程序的映射器。
  4. IDataMapper 返回 Repository 类,并且存储库实现 IQueryable,允许客户端在存储库上使用 LINQ。
  5. IDataMapper 的特定实现将引用传递给 O/RM 特定的工作单元(例如 EF 的 ObjectContext)。因此,IDataMapper 必须实现 IDisposable

这导致以下设计:

public interface INorthwindUnitOfWorkFactory
{
    NorthwindUnitOfWork CreateNew();
}
public interface IDataMapper : IDisposable
{
    Repository GetRepository() where T : class;
    void Save();
}
public abstract class Repository : IQueryable
    where T : class
{
    private readonly IQueryable query;
    protected Repository(IQueryable query)
    {
        this.query = query;
    }
    public abstract void InsertOnSubmit(T entity);
    public abstract void DeleteOnSubmit(T entity);
    // IQueryable members omitted.
}

NorthwindUnitOfWork是一个具体的类,包含一些特定仓库的属性,例如CustomersOrders等:

public sealed class NorthwindUnitOfWork : IDisposable 
{
    private readonly IDataMapper mapper;
    public NorthwindUnitOfWork(IDataMapper mapper)
    {
        this.mapper = mapper;
    }
    // Repository properties here:    
    public Repository Customers
    {
        get { return this.mapper.GetRepository(); }
    }
    public void Dispose()
    {
        this.mapper.Dispose();
    }
}

剩下的就是INorthwindUnitOfWorkFactoryIDataMapper的具体实现。下面是使用Entity Framework实现的一个实例:

public class EntityFrameworkNorthwindUnitOfWorkFactory
    : INorthwindUnitOfWorkFactory
{
    public NorthwindUnitOfWork CreateNew()
    {
        var db = new ObjectContext("name=NorthwindEntities");
        db.DefaultContainerName = "NorthwindEntities";
        var mapper = new EntityFrameworkDataMapper(db);
        return new NorthwindUnitOfWork(mapper);
    }
}

还有EntityFrameworkDataMapper

public sealed class EntityFrameworkDataMapper : IDataMapper
{
    private readonly ObjectContext context;
    public EntityFrameworkDataMapper(ObjectContext context)
    {
        this.context = context;
    }
    public void Save()
    {
        this.context.SaveChanges();
    }
    public void Dispose()
    {
        this.context.Dispose();
    }
    public Repository GetRepository() where T : class
    {
        string setName = this.GetEntitySetName();
        var query = this.context.CreateQuery(setName);
        return new EntityRepository(query, setName);
    }
    private string GetEntitySetName()
    {
        EntityContainer container =
            this.context.MetadataWorkspace.GetEntityContainer(
            this.context.DefaultContainerName, DataSpace.CSpace);
        return (
            from item in container.BaseEntitySets
            where item.ElementType.Name == typeof(T).Name
            select item.Name).First();
    }
    private sealed class EntityRepository
        : Repository where T : class
    {
        private readonly ObjectQuery query;
        private readonly string entitySetName;
        public EntityRepository(ObjectQuery query,
            string entitySetName) : base(query)
        {
            this.query = query;
            this.entitySetName = entitySetName;
        }
        public override void InsertOnSubmit(T entity)
        {
            this.query.Context.AddObject(entitySetName, entity);
        }
        public override void DeleteOnSubmit(T entity)
        {
            this.query.Context.DeleteObject(entity);
        }
    }
}

您可以在这里了解更多关于这个模型的信息。

2012年12月更新

这是我原始答案两年后的一个更新。过去两年中,我在处理工作单元模式时尝试设计系统的方式发生了许多变化。虽然过去很适合我,但现在我不再喜欢使用工厂方法来处理工作单元模式。相反,我直接将工作单元实例注入到消费者中。然而,是否可行取决于系统设计的方式。如果您想了解更多信息,请查看我最新的Stackoverflow答案:为什么每个Web请求一个DbContext?

0