【问题标题】:What are best practices for managing DataContext?管理 DataContext 的最佳实践是什么?
【发布时间】:2011-12-31 01:09:59
【问题描述】:

为了使我的实体持久无知并使我的存储库可测试,我实现了一个存储库模式,如下所示:

public interface IJobRepository : IRepository<Job>
{
    Job GetJobById(int jobId); //Special case where I'm eager loading other entities
    void SaveJob(Job job, Job originalJob);
}

public class JobRepository : IJobRepository
{
    private readonly IContext _context;

    public JobRepository()
    {
        _context = new CustomObjectContext();
    }

    public JobRepository(UnitOfWork unitOfWork)
    {
        _context = unitOfWork.Context;
    }

    //Basic GetAll, GetById, Add and Delete methods from IRepository<T> Interface here
    //omitted for brevity

    public Job GetJobById(int jobId)
    {
        var job = _context.Jobs.Include("Company").Include("Location").
            Include("PlantInfo").Where(j => j.Jobid == jobId).SingleOrDefault();

        _context.DisposeContext();

        return job;
    }

    public void SaveJob(Job job, Job originalJob)
    {
        if (job.Jobid > 0)
        {
            // Update
            _context.Jobs.Attach(originalJob);
            _context.PlantInfoes.Attach(originalJob.PlantInfo);
            _context.Jobs.ApplyCurrentValues(job);
            _context.PlantInfoes.ApplyCurrentValues(job.PlantInfo);

        Note: ApplyCurrentValues is an extension method I'm using on the ObjectSet

        }
        else
        {
            // Create
            _context.Jobs.AddObject(job);
        }

        _context.Save();
    }
}

public class UnitOfWork
{
    private readonly IContext _context;

    public UnitOfWork()
    {
        _context = new CustomObjectContext();
    }

    public UnitOfWork(IContext context)
    {
        _context = context;
    }

    public string Save()
    {
        return _context.Save();
    }

    internal IContext Context
    {
        get { return _context; }
    }
}

public interface IContext
{
    IObjectSet<Job> Jobs { get; }
    IObjectSet<Company> Companies { get; }
    IObjectSet<Location> Locations { get; }
    IObjectSet<PlantInfo> PlantInfoes { get; }
    string Save();
}

我的 ObjectContext 继承自 IContext...所以我的理解是我只会在存储库上使用重载的构造函数以方便单元测试或在我想使用相同上下文的情况下使用它(基于这篇我在 SO "Entity Framework and Connection Pooling" 上找到的帖子——对吗?

另外,假设上下文仅在存储库被垃圾收集时被释放,我必须显式地释放上下文以避免“一个实体对象不能被多个 IEntityChangeTracker 实例引用”。在保存之前附加实体时出现异常。

也就是说,以保持实体持久无知和存储库可测试的方式管理 DataContext 的最佳实践是什么?

注意:这是一个 asp.net 网络应用程序; UnitOfWork 和 IContext 的实现基于 Julia Lerman Ch24 第二版的“Programming Entity Framework”中的示例。

提前致谢!

【问题讨论】:

    标签: asp.net entity-framework web-applications entity-framework-4 entity-framework-4.1


    【解决方案1】:

    首先,我会确保无论我的“消耗性”对象是什么(存储库或工作单元,取决于您的设置)实现IDisposable。当你的消费对象被释放后,你就会释放你的底层上下文。

    例如,如果您使用 UnitOfWork 作为消耗对象(在您的应用程序中创建和调用的对象),它看起来像:

    public class UnitOfWork : IDisposable
    {
        // All the other stuff you had before plus:
        public void Dispose ()
        {
            if (_context != null)
            {
                _context.Dispose ();
            }
        }
    }
    

    (注意:如果它们是直接使用的存储库,也可以在您的存储库上完成)

    然后,您的应用程序中有几个选项。如果你打算直接使用UnitOfWork,你可以这样使用:

    public void SomeMethodThatAccessesYourData ()
    {
        using (var unitOfWork = new UnitOfWork (/*Load in the context*/))
        {
            // Access your data here.
        }
    }
    

    或者,在 Web 窗体或 MVC 对象中,您可以使用构造函数注入并在 Web 窗体或 MVC 对象被释放时释放它:

    //  If you're using MVC:
    public class MyController : Controller
    {
        private UnitOfWork _unitOfWork;
    
        public MyController (UnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }
    
        public override Dispose (bool Disposing)
        {
            if (Disposing && _unitOfWork != null)
            {
                _unitOfWork.Dispose ();
            }
        }
    }
    

    同样的想法代表网络表单Page

    使用构造函数重载的主要原因是Inversion of Control (IOC)。与IoC Container 一起使用时,它有助于单元测试和生产代码。 WebForms 不适合 IoC,但使用 MVC 真的很容易。

    编辑

    我并没有真正看到与您的存储库和工作单元的联系。通常您从一个工作单元访问存储库,或者在其他实现中,您从目标存储库请求一个工作单元。在您的实现中(我理解这不是您自己的),似乎两者都不需要。

    编辑 2

    如果 UoW 对您的应用程序来说太过分了,并且您知道可以使用 IoC 来注入您的 IContext,并且您没有很多存储库,您可以执行以下操作:

    public IRepository<T> : IDisposable { }
    public IJobRepository : IRepository<Job> { /* All the stuff you put here */ }
    public JobRepository : IJobRepository
    {
        private IContext _context;
    
        ...
    
        public void Dispose ()
        {
            if (_context != null)
            {
                _context.Dispose ();
            }
        }
    
        public JobRepository (IContext context)
        {
             _context = context;
        }
    }
    

    然后,您如何使用它取决于您的具体任务。我不喜欢直接使用 IRepository,但这个答案太长了。

    【讨论】:

    • 有人建议使用 IDisposable 但我不完全理解如何实现它——我现在明白了,谢谢@James。关于编辑;我也没有看到让我感到困惑的连接,所以我只是把它留在那里以使 repo 可测试。我以前使用过这个构造函数,但不确定:public Job(){ var unitOfWork = new UnitOfWork(); _context = unitOfWork.Context; } 这应该强制 repo 使用工作单元,对吗?如果是这样,最好将实现 IDisposable 放在 uow 或 repo 中?
    • BTY - 我使用基于 MVP 的 UI 模式框架。所以我使用 IoC w 构造函数注入来从我的演示者中的 repos 调用方法,并希望避免直接从我的演示者调用 uow。
    • 这是一个有点复杂的答案主题。有很多不错的博客讨论如何做这样的事情。如果您有更多问题,请随时通过 thegeekco dot com 的 james 给我发送电子邮件,或在 Twitter 上联系我(在我的个人资料中找到)。
    • 我同意。感谢您提供额外的帮助。我认为您的回答尽可能简洁。我会对此子进行更多研究并通过电子邮件联系您。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2011-02-07
    • 2013-06-12
    • 1970-01-01
    • 1970-01-01
    • 2017-04-20
    • 2017-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多