什么是管理DataContext的最佳做法? [英] What are best practices for managing DataContext?

查看:102
本文介绍了什么是管理DataContext的最佳做法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了使我的实体永远无知,使我的存储库可以测试,我已经实现了一个存储库模式,如下所示:

  public interface IJobRepository:IRepository< Job> 
{
作业GetJobById(int jobId); //特殊情况,我渴望加载其他实体
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;
}

//来自IRepository< T>的基本GetAll,GetById,添加和删除方法接口这里
//为简洁省略

public Job GetJobById(int jobId)
{
var job = _context.Jobs.Include(Company)。 (位置)。
Include(PlantInfo)。其中(j => j.Jobid == jobId).SingleOrDefault();

_context.DisposeContext();

返回工作;
}

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

注意:ApplyCurrentValues是我在ObjectSet上使用的扩展方法

}
else
{
//创建
_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();
}

内部IContext上下文
{
get {return _context; }
}
}

public interface IContext
{
IObjectSet< Job>工作{get; }
IObjectSet< Company>公司{get; }
IObjectSet< Location>地点{get; }
IObjectSet< PlantInfo> PlantInfoes {get; }
string Save();
}

我的ObjectContext继承自IContext ...所以我的理解是,我只会使用存储库中的重载构造函数来促进单元测试或在我想使用相同的上下文的情况下使用它(根据我在SO 实体框架和连接池 - 是对吗?





那么说,以一种使您的实体持续无知和存储库可测试的方式管理DataContext的最佳做法是什么?



注意:这是一个asp.net webappli阳离子; UnitOfWork和IContext实现是基于编程实体框架的例子,第二版由Julia Lerman Ch24。



提前感谢!

解决方案

首先,我将确保无论我的可消耗对象是(存储库或工作单位,具体取决于您的设置)实现 IDisposable 。当您的消耗对象被处置时,您将处理您的底层环境。



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

  public class UnitOfWork:IDisposable 
{
//所有其​​他的东西你以前加:
public void Dispose()
{
if(_context!= null )
{
_context.Dispose();
}
}
}

(注意:如果直接被消费者使用,那么请在您的存储库中完成)



然后,您的应用程序中有几个选项。如果要直接使用 UnitOfWork ,可以使用它:

  public void SomeMethodThatAccessesYourData()
{
using(var unitOfWork = new UnitOfWork(/ *在上下文中加载*))
{
//访问您的数据。
}
}

或者,在您的Web窗体或MVC对象中,您可以使用构造函数注入并处理Web窗体或MVC对象时处理:

  //如果您使用MVC :
public class MyController:Controller
{
private UnitOfWork _unitOfWork;

public MyController(UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}

public override Dispose(bool Disposing)
{
如果(Dispose&&uni _ _unitOfWork.Dispose();
}
}
}

同样的想法代表一个web表单页面



使用构造函数重载的主要原因是反转控制(IOC)。当与 IoC容器一起使用时,它可以帮助单元测试和生产代码。 WebForms不适合IoC,但是MVC真的很容易。



编辑



我没有真正看到与您的存储库和工作单元的连接。通常,您可以从工作单位访问存储库,或者在其他实现中,从目标存储库中请求一个工作单元。在你的实现中(我理解的不是你自己的)似乎没有必要。



编辑2



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

  public IRepository< T> :IDisposable {} 
public IJobRepository:IRepository< Job> {/ *所有你放在这里的东西* /}
public JobRepository:IJobRepository
{
private IContext _context;

...

public void Dispose()
{
if(_context!= null)
{
_context .Dispose();
}
}

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

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


In an effort to make my entities persistent ignorant and make my repositories testable, I've implemented a repository pattern like so:

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

My ObjectContext inherits from IContext...So my understanding is that I will only use the overloaded constructor on the repository to facilitate unit tests or to use it in the case that I want to use the same context (not desirable based on this post I found on SO "Entity Framework and Connection Pooling" -- Is this right?

Also, assuming the context only gets disposed when the repository is garbage collected, I have to dispose the context explicitly to avoid the "An entity object cannot be referenced by multiple instances of IEntityChangeTracker." exception when attaching the entity prior to a save.

That said, what is the best practice for managing the DataContext in a manner that keeps your entities persistent ignorant and repositories testable?

Note: This is an asp.net webapplication; UnitOfWork and IContext implementation was based on examples from "Programming Entity Framework", Second Edition by Julia Lerman Ch24.

Thanks in advance!

解决方案

Firstly, I would ensure that whatever my "consumable" object is (either repository or unit of work, depending on your setup) implements IDisposable. When your consumbed object is disposed of, then you would dispose your underlying context.

For instance, if you're using your UnitOfWork as the consumable object (the one that gets created and called in your application), it would look something like:

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

(Note: This can also be done on your repositories if they're the ones being consumed directly)

And then, you have a few options in your application. If you are going to use the UnitOfWork directly, you can use it like:

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

Or, in your Web Forms or MVC object you can use constructor injection and dispose of it when the Web Forms or MVC object is disposed of:

//  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 ();
        }
    }
}

The same idea stands for a web forms Page.

The main reason for using the constructor overload is for Inversion of Control (IOC). It helps with both unit testing and with production code when used with an IoC Container. WebForms doesn't lend itself well to IoC, but it is really easy with MVC.

Edit

I don't really see the connection with your repositories and the unit of work. Usually you access the repositories from a unit of work or, in other implementations, you request a unit of work from your target repository. In your implementation (which I understand is not your own) there seems to be no need for both.

Edit 2

If the UoW is overkill for your application, and you know you can use IoC to inject your IContext, and you don't have very many repositories, you can do something like:

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

Then, how you use it depends on your specific task. I'm not a fan of this direct use of IRepository, but this answer is getting too long.

这篇关于什么是管理DataContext的最佳做法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆