What are best practices for managing DataContext?

﹥>﹥吖頭↗ 提交于 2019-12-10 12:15:36

问题


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!


回答1:


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.



来源:https://stackoverflow.com/questions/8685571/what-are-best-practices-for-managing-datacontext

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!