我有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);
}
}
/>
MyAppEntities
和IUnitOfWork
-包含POCO对象,这些对象应保存查询结果并成为解决方案的业务逻辑。每个项目都反映了系统的其他需求和不同需求。但是,来自IUnitOfWorkFactory
的实体在数据库中以及具有MyApp.Domain.Company
实体的.edmx中具有关系。例如:
MyApp.Domain.Transmission
中存在的MyApp.Domain.Company
由MyApp.Domain.Transmission
中存在的Vehicle
引用。另外,MyApp.Domain.Company
保留VehicleTransmission
的引用,因此MyApp.Domain.Transmission
包含VehicleTransmission
的引用。这两个项目均参考Vehicle
。重要的是要记住,MyApp.Domain.Transmission
和MyApp.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
。建筑。我真的很想知道我做对的事情以及我的陷阱在哪里。#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)来处理该问题,该注入会产生相同的结果。实现中的另一个主要区别是我还具有
ObjectContextResolver
和ObjectContextFactory
。工作单元和存储库使用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
评论
有什么方法可以缩短代码片段并提供指向较大示例的链接?@IAbstract:“链接到较大示例”是什么意思?
@Naor如果您可以在每个字符上加上额外的空格,并为每行添加5行注释。那应该使代码更大。
好了,您可以使用UnitOfWorkScope来管理UnitOfWork的生命周期,并采用其他UnitOfWorkScope,例如WCFUnitOfWorkScope,WebUnitOfWorkScope。它们的功能不适用于依赖项注入容器。
在客户那里,他们有类似的东西。我花了80%的时间编写抽象层,而真正的产品实现却花了很多时间。我看不到有一半的东西有什么好处。