Каковы лучшие практики для управления DataContext? - PullRequest
0 голосов
/ 31 декабря 2011

Стремясь сделать мои сущности постоянными невежественными и сделать свои репозитории тестируемыми, я реализовал шаблон репозитория следующим образом:

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 была основана на примерах из "Programming Entity Framework", второе издание, Джулия Лерман, Ch24.

Заранее спасибо!

1 Ответ

0 голосов
/ 31 декабря 2011

Во-первых, я бы позаботился о том, чтобы независимо от того, что является моим «расходуемым» объектом (хранилищем или единицей работы, в зависимости от вашей настройки), реализовано 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.
    }
}

Или в своем веб-формах или MVC-объекте вы можете использовать инжектор конструктора и утилизировать его, когда веб-формы илиОбъект 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.

Основная причина использования перегрузки конструктора - Инверсия управления (МОК) .Он помогает как с модульным тестированием, так и с рабочим кодом при использовании с IoC Container .WebForms плохо подходит для IoC, но с MVC это действительно просто.

Edit

Я не вижу связи с вашими репозиториями иединица работы.Обычно вы обращаетесь к репозиториям из единицы работы или, в других реализациях, запрашиваете единицу работы из вашего целевого репозитория.В вашей реализации (которая, как я понимаю, не ваша), кажется, нет необходимости в обоих.

Редактировать 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, но этот ответ становится слишком длинным.

...