这是我在EF4上使用POCO的存储库模式和工作单元模式的体系结构。我相信我犯了一些错误。

我有5个项目的解决方案:


MyApp.Common
MyApp.Data.EF4

此外,我在此应用程序中使用MyApp.Domain.Company来注入数据层。

现在我将描述每个项目包含的内容-来源:

MyApp.Domain.Transmission-包含常见的类和接口

存储库接口:

public interface IRepository<T> where T : class
{
    IQueryable<T> GetQuery(IEnumerable<Expression<Func<T, object>>> includes);

    IPaged<T> GetQuery(IQueryable<T> query,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, int pageNumber, int pageSize);

    IPaged<T> GetQuery(IEnumerable<T> query,
        Func<IEnumerable<T>, IOrderedEnumerable<T>> orderBy, int pageNumber, int pageSize);

    IEnumerable<T> GetObjectStateManagerChanges();

    void Insert(T entity);
    void MarkModified(T entity);
    void Delete(T entity);
    void Attach(T entity);
    void Detach(T entity);
    T GetOriginalEntity(Func<T, bool> predicate);
}


工作单元接口:

public interface IUnitOfWork : IDisposable
{
    int Commit();
}


工作单位工厂界面:

public interface IUnitOfWorkFactory
{
    IUnitOfWork Create();
}


工作单位:

  public static class UnitOfWork
    {
        private const string HTTPCONTEXTKEY = "MyApp.Common.HttpContext.Key";

        private static IUnitOfWorkFactory _unitOfWorkFactory;
        private static readonly Hashtable _threads = new Hashtable();

        public static void Commit()
        {
            IUnitOfWork unitOfWork = GetUnitOfWork();

            if (unitOfWork != null)
            {
                unitOfWork.Commit();
            }
        }

        public static IUnitOfWork Current
        {
            get
            {
                IUnitOfWork unitOfWork = GetUnitOfWork();

                if (unitOfWork == null)
                {
                    _unitOfWorkFactory = ObjectFactory.GetInstance<IUnitOfWorkFactory>();
                    unitOfWork = _unitOfWorkFactory.Create();
                    SaveUnitOfWork(unitOfWork);
                }

                return unitOfWork;
            }
        }

        public static void Dispose()
        {
            IUnitOfWork unitOfWork = GetUnitOfWork();

            if (unitOfWork != null)
            {
                unitOfWork.Dispose();
            }
        }

        private static IUnitOfWork GetUnitOfWork()
        {
            if (HttpContext.Current != null)
            {
                if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
                {
                    return (IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY];
                }

                return null;
            }
            else
            {
                Thread thread = Thread.CurrentThread;
                if (string.IsNullOrEmpty(thread.Name))
                {
                    thread.Name = Guid.NewGuid().ToString();
                    return null;
                }
                else
                {
                    lock (_threads.SyncRoot)
                    {
                        return (IUnitOfWork)_threads[Thread.CurrentThread.Name];
                    }
                }
            }
        }

        private static void SaveUnitOfWork(IUnitOfWork unitOfWork)
        {
            if (HttpContext.Current != null)
            {
                HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork;
            }
            else
            {
                lock (_threads.SyncRoot)
                {
                    _threads[Thread.CurrentThread.Name] = unitOfWork;
                }
            }
        }
    }


所有实体的基础存储库:

public abstract class BaseRepository<T> where T : class
{
    protected IRepository<T> Repository { get; set; }

    public IEnumerable<Expression<Func<T, object>>> Includes { private get; set; }


    public BaseRepository()
    {
        Repository = ObjectFactory.GetInstance<IRepository<T>>();
        Includes = null;
    }

    protected virtual IQueryable<T> GetQuery()
    {
        return Repository.GetQuery(Includes).AsEnumerable().AsQueryable();
    }

    protected virtual IQueryable<T> GetQuery(params Expression<Func<T, object>>[] includes)
    {
        return Repository.GetQuery(includes);
    }

    protected virtual IPaged<T> GetQuery(IEnumerable<T> query,
        Func<IEnumerable<T>, IOrderedEnumerable<T>> orderBy, int pageNumber, int pageSize)
    {
        return Repository.GetQuery(query, orderBy, pageNumber, pageSize);
    }


    protected virtual IPaged<T> GetQuery(IQueryable<T> query,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, int pageNumber, int pageSize)
    {
        return Repository.GetQuery(query, orderBy, pageNumber, pageSize);
    }

    protected virtual IEnumerable<T> GetObjectStateManagerChanges()
    {
        return Repository.GetObjectStateManagerChanges();
    }

    protected virtual void Insert(T entity)
    {
        Repository.Insert(entity);
    }

    protected virtual void MarkModified(T entity)
    {
        Repository.MarkModified(entity);
    }

    protected virtual void Delete(T entity)
    {
            Repository.Delete(entity);
    }

    protected virtual void Attach(T entity)
    {
        Repository.Attach(entity);
    }

    protected virtual void Detach(T entity)
    {
        Repository.Detach(entity);
    }

    protected virtual T GetOriginalEntity(Func<T, bool> predicate)
    {
        return Repository.GetOriginalEntity(predicate);
    }
}


MyApp.Data.EF4-保存MyApp.Web的引用并使用EF方式。它还包括MyAppModel.edmx,其中包含系统中所有实体的模型。 MyAppModel.edmx的“代码生成策略”设置为默认。实体容器的名称为StructureMap

存储库实现:

public class Repository<T> : IRepository<T> where T : class
{
    ObjectContext _context;
    IObjectSet<T> _objectSet;

    protected ObjectContext Context
    {
        get
        {
            if (_context == null)
            {
                _context = GetCurrentUnitOfWork<EFUnitOfWork>().Context;
            }

            return _context;
        }
    }

    protected IObjectSet<T> ObjectSet
    {
        get
        {
            if (_objectSet == null)
            {
                _objectSet = this.Context.CreateObjectSet<T>();
            }

            return _objectSet;
        }
    }

    public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork
    {
        return (TUnitOfWork)UnitOfWork.Current;
    }

    public virtual IQueryable<T> GetQuery(IEnumerable<Expression<Func<T, object>>> includes)
    {
        return ObjectSet.IncludeMultiple(includes);
    }

    public virtual IPaged<T> GetQuery(IQueryable<T> query,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, int pageNumber, int pageSize)
    {
        if (orderBy != null)
        {
            query = orderBy(query);
        }

        IPaged<T> page = new Paged<T>(query, pageNumber, pageSize);

        return page;
    }

    public virtual IPaged<T> GetQuery(IEnumerable<T> query,
        Func<IEnumerable<T>, IOrderedEnumerable<T>> orderBy, int pageNumber, int pageSize)
    {
        if (orderBy != null)
        {
            query = orderBy(query);
        }

        IPaged<T> page = new Paged<T>(query, pageNumber, pageSize);

        return page;
    }

    public virtual IEnumerable<T> GetObjectStateManagerChanges()
    {
        return this.Context.ObjectStateManager.
            GetObjectStateEntries(EntityState.Added | EntityState.Modified).
            Select(e => e.Entity).
            OfType<T>();
    }

    public virtual void Insert(T entity)
    {
        this.ObjectSet.AddObject(entity);
    }

    public virtual void Delete(T entity)
    {
        this.ObjectSet.DeleteObject(entity);
    }

    public virtual void MarkModified(T entity)
    {
        this.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    }

    public virtual void Attach(T entity)
    {
        ObjectStateEntry entry = null;
        if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == false)
        {
            this.ObjectSet.Attach(entity);
        }
    }

    public virtual void Detach(T entity)
    {
        ObjectStateEntry entry = null;
        if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == true)
        {
            this.ObjectSet.Detach(entity);
        }
    }

    public virtual T GetOriginalEntity(Func<T, bool> predicate)
    {
        T originalEntity = null;
        EFUnitOfWorkFactory factory = new EFUnitOfWorkFactory();
        using (EFUnitOfWork uow = (EFUnitOfWork)factory.Create())
        {
            originalEntity = uow.Context.CreateObjectSet<T>().Single(predicate);
        }
        return originalEntity;
    }
}


存储库实现中使用的扩展名:

public static class Extensions
    {
        public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query,
            IEnumerable<Expression<Func<T, object>>> includes)
            where T : class
        {
            if (includes != null)
            {
                query = includes.Aggregate(query,
                          (current, include) => current.Include(include));
            }

            return query;
        }
    }


MyApp.Common的实现:

public class EFUnitOfWork : IUnitOfWork, IDisposable
{
    public ObjectContext Context { get; private set; }
    public int Id { get; private set; }

    public EFUnitOfWork(ObjectContext context, int id)
    {
        Id = id;
        Context = context;
        Context.ContextOptions.LazyLoadingEnabled = false;
    }

    public int Commit()
    {
        return Context.SaveChanges();
    }

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

        GC.SuppressFinalize(this);
    }
}


MyApp.Common的实现:

public class EFUnitOfWorkFactory : IUnitOfWorkFactory
    {
        private static int Counter = 0;
        private static Func<ObjectContext> _objectContextDelegate;
        private static readonly Object _lockObject = new object();

        public static void SetObjectContext(Func<ObjectContext> objectContextDelegate)
        {
            _objectContextDelegate = objectContextDelegate;
        }

        public IUnitOfWork Create()
        {
            ObjectContext context;

            lock (_lockObject)
            {
                Counter++;
                context = _objectContextDelegate();
            }

            return new EFUnitOfWork(context, Counter);
        }
    }


/> MyAppEntitiesIUnitOfWork-包含POCO对象,这些对象应保存查询结果并成为解决方案的业务逻辑。每个项目都反映了系统的其他需求和不同需求。但是,来自IUnitOfWorkFactory的实体在数据库中以及具有MyApp.Domain.Company实体的.edmx中具有关系。

例如:MyApp.Domain.Transmission中存在的MyApp.Domain.CompanyMyApp.Domain.Transmission中存在的Vehicle引用。另外,MyApp.Domain.Company保留VehicleTransmission的引用,因此MyApp.Domain.Transmission包含VehicleTransmission的引用。这两个项目均参考Vehicle。重要的是要记住,MyApp.Domain.TransmissionMyApp.Domain.Company(及其之间的连接)在同一.edmx文件中表示。这些项目下的每个.cs文件都包含POCO实体和存储库管理器。

Vehicle.cs(来自MyApp.Common

public class Vehicle
    {
        public virtual long Id { get; set; }
        public virtual string VehicleNumber { get; set; }

        public virtual DateTime? PurchaseDate { get; set; }
        public virtual string InsuranceAgency { get; set; }
        public virtual DateTime? InsuranceValidity { get; set; }
        public virtual string Comments { get; set; }
        public virtual IList<Worker> Workers { get; set; }        

        public string GetPhoneNumber()
        {
            string phoneNumber = null;
            if (PhoneId.HasValue)
            {
                phoneNumber = Phone.PhoneNumber1;
                if (string.IsNullOrEmpty(phoneNumber))
                {
                    phoneNumber = Phone.PhoneNumber2;
                }
            }
            return phoneNumber;
        }

        public string GetManufacturerName()
        {
            string name = null;
            if (ManufacturerId.HasValue)
            {
                name = Manufacturer.Name;
            }
            return name;
        }
    }

    public class VehicleRepository : BaseRepository<Vehicle>
    {
        private bool IsCounterExists(Guid companyId, long counter)
        {
            return GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false).
                AsEnumerable().
                Any(x => x.Code.ToNullable<long>() == counter);
        }

        public void Attach(Vehicle entity)
        {
            base.Attach(entity);
        }

        public void MarkModified(Vehicle entity)
        {
            base.MarkModified(entity);
        }

        public void Delete(Vehicle entity)
        {
            base.Delete(entity);
        }

        public void Insert(Vehicle entity)
        {
            if (entity.VehicleNumber != null) { entity.VehicleNumber.Trim(); }
            if (entity.VehicleNumber == null || entity.VehicleNumber == string.Empty)
            {
                throw new MissingFieldException(
                    CompanyExceptionMessage.MissingFieldException_Vehicle_Number.Message);
            }
            if (string.IsNullOrEmpty(entity.Code))
            {
                StaffCounterRepository rep = new StaffCounterRepository();
                entity.Code = rep.GetAndAdvanceCounter(entity.CompanyId,
                    StaffCounterEnum.VEHICLE,
                    counter => IsCounterExists(entity.CompanyId, counter)).ToString();
            }

            int equals = GetQuery().
                 Where(x => x.CompanyId == entity.CompanyId).
                 Where(x => x.IsDeleted == false).
                 Where(x =>
                     (x.Code != null && x.Code == entity.Code) ||
                     (x.VehicleNumber == entity.VehicleNumber)
                     ).Count();
            if (equals > 0)
            {
                throw new ExistsNameOrCodeException(
                    CompanyExceptionMessage.ExistsNumberOrCodeException_Vehicle);
            }
            base.Insert(entity);
        }

        public Vehicle Get(Guid companyId, long vehicleId)
        {
            return GetQuery().Where(x => x.CompanyId == companyId).
                Single(x => x.Id == vehicleId);
        }

        public IQueryable<Vehicle> Get(Guid companyId)
        {
            return GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false).
                OrderBy(x => x.VehicleNumber);
        }

        public Vehicle Get(Guid companyId, string code)
        {
            return GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false).
                FirstOrDefault(x => x.Code == code);
        }

        public IQueryable<Vehicle> Search(Guid companyId, string vehicleNumber)
        {
            var query = GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false);
            if (vehicleNumber != null)
            {
                query = query.Where(x => x.VehicleNumber.Contains(vehicleNumber));
            }
            return query.OrderBy(x => x.VehicleNumber);
        }

        public IEnumerable<Vehicle> Get(Guid companyId, bool? freeVehicles,
            bool includeContractorVehicles)
        {
            IEnumerable<Vehicle> query = GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false);

            if (freeVehicles == true)
            {
                query = query.Where(x => x.Workers.Count == 0);
            }
            else if (freeVehicles == false)
            {
                query = query.Where(x => x.Workers.Count > 0);
            }
            query = query.AsEnumerable();

            if (includeContractorVehicles == true)
            {
                WorkerRepository rep = new WorkerRepository();
                IEnumerable<Vehicle> contractorsVehicles = rep.Get(companyId).
                    Where(x => x.ContractorVehicleNumber != null &&
                        x.ContractorVehicleNumber != string.Empty).
                    AsEnumerable().Select(x => new Vehicle()
                    {
                        VehicleNumber = x.ContractorVehicleNumber
                    });
                query = query.Union(contractorsVehicles);
            }

            return query;
        }        

        public IQueryable<Vehicle> Get(Guid companyId, VehicleStatusEnum? status,
            string code, string vehicleNumber)
        {
            var query = GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false);

            if (status.HasValue)
            {
                long? statusId = VehicleStatusWrapper.GetId<VehicleStatusWrapper>(status.Value);
                query = query.Where(x => x.StatusId == statusId);
            }
            if (!string.IsNullOrEmpty(code))
            {
                query = query.Where(x => x.Code.Contains(code));
            }
            if (!string.IsNullOrEmpty(vehicleNumber))
            {
                query = query.Where(x => x.VehicleNumber.Contains(vehicleNumber));
            }
            return query;
        }
    }


VehicleTransmission。 cs(来自MyApp.Domain.Transmission)

public class VehicleTransmission
    {
        public long? VehicleId { get; set; }
        public string VehicleNumber { get; set; }
        public long? SubcontractorId { get; set; }
        public bool WorkerOnHold { get; set; }

        public Vehicle Vehicle { get; set; }

        public void SetVehicleId(long? vehicleId)
        {
            VehicleId = vehicleId;
        }

        //Possibility to set vehicle that is not exists on the db
        public void SetVehicleNumber(string vehicleNumber)
        {
            VehicleId = null;
            VehicleNumber = vehicleNumber;
        }
    }

    public class VehicleTransmissionRepository : BaseRepository<VehicleTransmission>
    {
        public IQueryable<VehicleTransmission> Get()
        {
            return GetQuery();
        }
}


T_TBL_VEHICLE-包含Web应用程序。保存对所有其他项目的引用。

在global.asax中,在T_TBL_VEHICLE_TRANSMISSION中,我配置MyApp.Domain.Company

,在
    ObjectFactory.Configure(x =>
    {
        x.For<IUnitOfWorkFactory>().Use<EFUnitOfWorkFactory>();
        x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
        x.For(typeof(IEnumRepository<,>)).Use(typeof(EnumRepository<,>));
    });
    EFUnitOfWorkFactory.SetObjectContext(() => new MyAppEntities());
中,
do:

UnitOfWork.Dispose();


我也有Web服务,可以在其中使用存储库并在需要时调用MyApp.Web。建筑。我真的很想知道我做对的事情以及我的陷阱在哪里。

评论

有什么方法可以缩短代码片段并提供指向较大示例的链接?

@IAbstract:“链接到较大示例”是什么意思?

@Naor如果您可以在每个字符上加上额外的空格,并为每行添加5行注释。那应该使代码更大。

好了,您可以使用UnitOfWorkScope来管理UnitOfWork的生命周期,并采用其他UnitOfWorkScope,例如WCFUnitOfWorkScope,WebUnitOfWorkScope。它们的功能不适用于依赖项注入容器。

在客户那里,他们有类似的东西。我花了80%的时间编写抽象层,而真正的产品实现却花了很多时间。我看不到有一半的东西有什么好处。

#1 楼

在不了解太多情况的情况下,这里有一些指示信息:

我对Entity Framework不太熟悉,但是UoW的通用接口通常具有以下行为:

public interface UnitOfWork : IDisposable {
  bool IsInTransaction { get; }
  bool IsDirty { get; } // Same as your MarkModified()
  void BeginTransaction();
  void Commit();
  void Rollback();
}


错误和日志记录处理在哪里?以下是您可以使用的简单包装器的示例。

// Example call
ExceptionHandler.Try("Could not load Vechicle", () => _vehicleRepository.Insert(vehicle));

 // Example class
 public class ExceptionHandler
    public void Try(string errorMessage, Action func)
    {
        try
        {
           func();
        }
        catch (Exception e)
        {
           Handle(e, errorMessage); // handle and log exception
        }
    }


并将Common-project的名称更改为不太常见的名称。.))

#2 楼

这实际上与我的实现非常相似。一个主要区别是我没有实现UOW的生命周期管理。我使用依赖项注入(使用Request Scope进行Ninject)来处理该问题,该注入会产生相同的结果。

实现中的另一个主要区别是我还具有ObjectContextResolverObjectContextFactory。工作单元和存储库使用ObjectContextResolver获得对当前上下文的访问权限,其中ObjectContextFactory使用ObjectContextResolver创建不存在的新上下文实例。 ObjectContextFactory还使用了ObjectContextActivationInfo,它是连接字符串和设置的包装器,使我可以轻松地更改上下文的创建方式。

我也有两种类型的存储库...我有一个EntityRepository这很像您的实现,使我可以查询和使用特定实体。我还有一个ContextRepository,它公开了GetSet<T>方法,因此我可以查询模型中的任何实体类型。工作单元的开始和结束。典型用法如下:可能会创建其他东西...当外部服务或旧数据存储与EF结合使用时,通常会发生这种情况。

因为将代码包装在using块中,一旦执行完成,

我在UnitOfWork上还有一个Rollback()方法,该方法允许在某些代码失败并且您不想保存更改的情况下还原更改。 />
这种架构(您和我的)的局限性之一是无法使用UnitOfWork并行执行操作。最近,我需要通过我的框架并行执行两个EF查询,但由于我的体系结构是围绕单个Http Request设计的,因此代码实际上替代了当前的UnitOfWork,因此无法执行。

我一直在思考扩展现有方法以允许这种方法的方法。

评论


\ $ \ begingroup \ $
更新,我解决了并行使用这些工作单元的问题,但仅在只读方案中有效。
\ $ \ endgroup \ $
–ctorx
13年2月11日在7:00

\ $ \ begingroup \ $
非常有趣。我喜欢最终使用它的方式。我想知道您是否无法以某种方式将UOW实例传递给服务中的方法吗?尽管我承认这可能会在代码方面更加笨拙,但这足以应付任何情况,因为它将需要传递给每个存储库方法。
\ $ \ endgroup \ $
– julealgon
2014年2月19日15:37

\ $ \ begingroup \ $
我认为将UOW传递给服务将无法达到目的,因为UOW范围可以跨越多个服务调用。假设您有一个逻辑UOW,该UOW进行了三个调用以分别服务。如果您通过了UOW,则这些服务方法中的任何一种都将能够提交或回滚UOW。这将迫使您不断检查UOW的状态,以便您知道是否应该开始下一个服务呼叫。我已经在应用内UOW范围内使用了一段时间,我发现它非常简单,直接并且相当灵活。
\ $ \ endgroup \ $
–ctorx
2014年2月24日在17:48

#3 楼

实体框架是系统中的工作单元,因此您无需重新发明轮子。您只需要创建一个:通用存储库:

interface IRepository<TEntity>
    where TEntity : class
{
    //Add<TEntity>, Remove<TEntity> ...
}

class /* don't have to be abstract */ BaseRepository<TObjectContext, TEntity> : IRepository<TEntity>
    where TObjectContext : ObjectContext
    where TEntity : class
{
    public RepositoryBase(TObjectContext context)
    {
        //... store context
    }

    //implementations
}


,然后您可以创建专用存储库。

评论


\ $ \ begingroup \ $
确实是工作单元,但是如果要允许控制工作单元的生命周期,则需要在EF之上创建由应用程序使用的层,或者您必须使用上下文直接在您的应用程序中。
\ $ \ endgroup \ $
–ctorx
2012-10-18 14:53

\ $ \ begingroup \ $
对于生命周期管理,请使用一些依赖项注入容器。 (例如,Ninject可以做到。)
\ $ \ endgroup \ $
– Peter Kiss
2012年10月18日15:06



\ $ \ begingroup \ $
我的实现使用Ninject来管理用于构建上下文的某些较低级别组件的生命周期,而不是上下文本身。这样,我的应用程序对上下文一无所知。它只知道有关服务的信息,而有关服务的信息库也有关上下文的信息。
\ $ \ endgroup \ $
–ctorx
2012年10月18日19:04

\ $ \ begingroup \ $
@PeterKiss确实是EF是一个UoW,但是默认情况下,EF会允许您使用它的上下文做任何事情,当您的对象不支持每种方法时会发生什么,比如您不想删除日志例。您始终可以使用虚拟方法进行通用服务包装,并使用NotSupportedException覆盖每个不受支持的方法,或者以其他方式处理此问题,但是再次,您如何隐藏不受支持的方法?我相信,这是在我诚实谦虚的观点中,存储库模式和uow结合的地方。
\ $ \ endgroup \ $
–埃斯特万
2013年9月26日下午3:16

\ $ \ begingroup \ $
如果创建在数据端实现EF却公开POCO的业务“ Manangers”,并将Manangers注入到控制器中怎么办?当前,我们有UoF,Repos,POCO,Manangers,所有这些血腥的抽象实际上都没有。是的,我知道它与EF紧密地结合了数据,但是如果他们想使用其他东西,就必须以任何方式编写新的实现,而DI可以很容易地将其替换掉而无需更改任何web / api层。只是目前的客户坚持要使用它,并且全力以赴。
\ $ \ endgroup \ $
– Piotr Kula
2015年2月11日在22:20