简体   繁体   English

EF6,工作单元和存储库模式-这是同步服务的错误模式吗?

[英]EF6, Unit of Work and Repository Pattern - Is this the wrong pattern for a sync service?

I'm writing a sync service between our Salesforce environment and our local environment. 我正在编写Salesforce环境和本地环境之间的同步服务。 My use of the Salesforce API is purely on a batch level due to limitations on # of API requests per day, although I do have details on failures at the atomic level. 由于每天对API请求数量的限制,我对Salesforce API的使用纯粹是在批处理级别,尽管我确实在原子级别上有关于失败的详细信息。 However, I would like to save changes on a atomic level in my local environment as I don't want an entire transaction to fail if one entity fails. 但是,我想在本地环境中保存原子级别的更改,因为如果一个实体发生故障,我不希望整个事务失败。

I am using Entity Framework 6 with a Unit of Work and Repository pattern. 我正在使用带有工作单元和存储库模式的Entity Framework 6。 Here is my relevant code (Implementation Details Below): 这是我的相关代码(下面的实现详细信息):

IUnitOfWork IUnitOfWork

public interface IUnitOfWork: IDisposable
{
    IReadUpdateRepository<EntityType1> EntityType1Repository { get; }
    ISyncRepository<EntityType2> EntityType2Repository { get; }
    ISyncRepository<EntityType3> EntityType3Repository { get; }
...other repos
    void SaveChanges();
}

Implementation of IUnitOfWork IUnitOfWork的实现

public class UnitOfWork : IUnitOfWork
{
    private bool _isDisposed;
    private DbContext _context;
    private ISyncRepository<Entity> _entityRepo;

...other private repos
    public UnitOfWork(DbContext context)
    {
        _context = context;
    }


    public ISyncRepository<Entity> EntityRepository
    {
        get
        {
            if (_entityRepo == null)
                _entityRepo = new GenericSyncRepository<Entity>(_context);

            return _entityRepo ;
        }
    }

...other getters for other repos

    public void SaveChanges()
    {
        //client classes handle all errors here
        _context.SaveChanges();
    }

    private void dispose(bool isDisposing)
    {
        if (!_isDisposed)
        {
            if (isDisposing)
                _context.Dispose();
        }

        _isDisposed = true;
    }

    public void Dispose()
    {
        dispose(true);
    }
}

ISyncRepository ISyncRepository

public interface ISyncRepository<T> where T : BaseEntity
{
    void DeleteItems(IEnumerable<T> items);
    void DeleteItemsById(IEnumerable<int> ids);
    void DeleteItem(T item);
    void InsertItems(IEnumerable<T> items);
    void Insert(T item);
    T GetItemById(int id);
    List<T> GetItems(Expression<Func<T, bool>> predicate = null,     Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "");

}

Implementation of ISyncRepository ISyncRepository的实现

public class GenericSyncRepository<T> : ISyncRepository<T> where T : BaseEntity
{
    private readonly DbContext _context;
    private readonly DbSet<T> _set;

    public GenericSyncRepository(DbContext context)
    {
        _context = context;
        _set = _context.Set<T>();
    }

    public T GetItemById(int id)
    {
        var result = _set.Find(id);

        return result;
    }

    public List<T> GetItems(Expression<Func<T, bool>> predicate = null, Func<IQueryable<T>,IOrderedQueryable<T>>  orderBy = null ,string includeProperties = "")
    {
        IQueryable<T> query = _set.AsExpandable();

        if (predicate != null)
        {
            query = query.Where(predicate);
        }


        if (!String.IsNullOrEmpty(includeProperties))
        {
            var splitProps = includeProperties.Split(',');
            foreach (var prop in splitProps)
            {
                query = query.Include(prop);
            }
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }

        return query.ToList();
    }

    public void DeleteItemsById(IEnumerable<int> ids)
    {

        var items = ids.Select(i => _set.Find(i));

        DeleteItems(items);
    }

    public void DeleteItem(T item)
    {
        _set.Remove(item);
    }

    public void DeleteItems(IEnumerable<T> items)
    {
        _context.Set<T>().RemoveRange(items);
    }

    public void Insert(T item)
    {
        _set.Add(item);
    }

    public void InsertItems(IEnumerable<T> items)
    {
        _set.AddRange(items);
    }

    public List<T> GetViewItems(Expression<Func<T, bool>> predicate)
    {
        IQueryable<T> query = _set.AsExpandable();

        return query.Where(predicate).ToList();
    }
}

I am using this repository in services that relate to each object group involved in the sync, into which I inject the IUnitOfWork via the constructor: 我在与同步中涉及的每个对象组相关的服务中使用此存储库,我通过构造函数将IUnitOfWork注入其中:

 private readonly IUnitOfWork _uow;

public EntityDataService(IUnitOfWork uow, ICamsSfDTOMapper mapper)
    {
        _uow = uow;
    }

Then use the UoW to fetch repositories to performs queries: 然后使用UoW来获取存储库以执行查询:

var repo = _unitOfWork.PartyRepository;
var p = repo.GetItems(p => Ids.Contains(someValue));

And when inserts or updates are made, I can call SaveChanges: 当进行插入或更新时,我可以调用SaveChanges:

_unitOfWork.SaveChanges();

This works great for data retrieval, however for data persistence, I run into a snag. 这对于数据检索非常有用,但是对于数据持久性,我遇到了麻烦。 On the local domain side, I want to iterate through objects one by one, saving changes or inserting and calling SaveChanges to persist. 在本地域方面,我想一个接一个地遍历对象,保存更改或插入并调用SaveChanges以保持持久性。 If an error occurs, I store it in a result object that I log at the end of each sync step and continue onto the next object to do work on. 如果发生错误,则将其存储在结果对象中,并在每个同步步骤结束时记录该结果对象,然后继续处理下一个对象。

However, as my app is currently structured, if a database exception occurs on SaveChanges, that validation error remains in the context until it is disposed. 但是,由于我的应用程序当前已构建,因此如果SaveChanges上发生数据库异常,该验证错误将保留在上下文中,直到将其释放为止。 Since this context is injected into each repository in the UoW class, it sticks around for what I presume is the life of the entire sync process. 由于此上下文已注入到UoW类中的每个存储库中,因此我认为这是整个同步过程的生命周期。

I am using Autofac for DI in a WindowsService hosted using TopShelf and I'm registering my UoW as such: 我在使用TopShelf托管的WindowsService中将Autofac用于DI,并且正在这样注册我的UoW:

builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().WithParameter("context", new EntityModel());

Here are my questions: 这是我的问题:

  1. Should this question be posted to Code Review :) 该问题是否应该发布到代码审查中:)
  2. I feel like I should invert my repository with UoW, with UoW being passed into each repository and my repositories being passed into each service (a service can make use of more than one repository) 我觉得我应该使用UoW转换我的存储库,将UoW传递到每个存储库中,并将我的存储库传递到每个服务中(一个服务可以使用多个存储库)
  3. Does it make sense to spin up a new DbContext for each SaveChanges and to have one DbContext (the one passed into UoW) as my read context and all other contexts new'ed up when SavingChanges? 为每个SaveChanges旋转一个新的DbContext并让一个DbContext(传递给UoW的那个)作为我的读取上下文以及在SaveChanges时创建所有其他上下文是否有意义?

Any help would be appreciated. 任何帮助,将不胜感激。

  1. Doesn't bother me, I only use SO and don't intend to go anywhere else, and don't mind having questions like these on SO. 不会打扰我,我只使用SO,并且不打算去其他任何地方,也不介意在SO上遇到这样的问题。
  2. I would highly recommend that your service as for IRepository and your repository ask for IUoW . 我强烈建议您针对IRepository的服务以及您的存储库要求IUoW I even like to think of IRepository as more than just a barrier to EF. 我什至喜欢将IRepository视为不仅仅是EF的障碍。 I normally only use a repository if either 我通常只使用一个存储库
    • I know for a fact in advance I will be implementing a non EF repository as well otherwise 我事先知道一个事实,否则我还将实现一个非EF存储库
    • I want to implement some actual logic which I want abstracted from my service and would not make it generic. 我想实现一些实际的逻辑,这些逻辑要从我的服务中抽象出来,而不要使其泛泛。 Eg I would make a UserProfileRepository, which would have methods such as SyncCurrentUserProfile rather than exposing Add/Insert/Update . 例如,我将创建一个UserProfileRepository,它具有SyncCurrentUserProfile方法,而不是公开Add/Insert/Update Exposing Add/Insert/Update adds nothing to the equation if I don't plan to use a non EF based model. 如果我不打算使用基于非EF的模型,则公开Add/Insert/Update不会对方程式添加任何内容。
  3. It depends. 这取决于。 If you don't use tracking, and have a lot of unrelated changes over time. 如果您不使用跟踪,并且随着时间的推移进行了许多无关的更改。 Than yes, as each time you add/update something it will add it to the context, which causes future additions/modifications become slower. 比是,每次添加/更新某些内容时,它将添加到上下文中,这会导致以后的添加/修改变慢。 But if you are adding/modifying 10 things here and there, probably wouldn't worry about it not unless you need a fresh context each time. 但是,如果您要在这里到处添加/修改10件事,除非每次都需要一个新的上下文,否则可能不必担心。

I am actually writing a blog post about this. 我实际上是在撰写有关此内容的博客文章。 Will try to post a link later. 稍后将尝试发布链接。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM