Entity framework 使用通用CRUD函数处理复杂实体(关系)的最佳方法
我曾尝试使用此通用函数插入更新实体,但我始终认为,可能我这样做完全错误,因此我想听听您的意见/建议 以下是我的插入和更新功能:Entity framework 使用通用CRUD函数处理复杂实体(关系)的最佳方法,entity-framework,generics,repository-pattern,crud,Entity Framework,Generics,Repository Pattern,Crud,我曾尝试使用此通用函数插入更新实体,但我始终认为,可能我这样做完全错误,因此我想听听您的意见/建议 以下是我的插入和更新功能: public static bool Insert<T>(T item) where T : class { using (ApplicationDbContext ctx = new ApplicationDbContext()) { try { ctx.Set<T>(
public static bool Insert<T>(T item) where T : class
{
using (ApplicationDbContext ctx = new ApplicationDbContext())
{
try
{
ctx.Set<T>().Add(item);
ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// ...
}
}
}
public static bool Update<T>(T item) where T : class
{
using (ApplicationDbContext ctx = new ApplicationDbContext())
{
try
{
Type itemType = item.GetType();
// switch statement to perform actions according which type we are working on
ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// ...
}
}
}
公共静态bool插入(T项),其中T:class
{
使用(ApplicationDbContext ctx=new ApplicationDbContext())
{
尝试
{
ctx.Set().Add(项);
ctx.SaveChanges();
返回true;
}
捕获(例外情况除外)
{
// ...
}
}
}
公共静态bool更新(T项),其中T:class
{
使用(ApplicationDbContext ctx=new ApplicationDbContext())
{
尝试
{
Type itemType=item.GetType();
//switch语句根据我们正在处理的类型执行操作
ctx.SaveChanges();
返回true;
}
捕获(例外情况除外)
{
// ...
}
}
}
我学会了使用ctx.Entry(item).State=EntityState.Modified
我已经看到了许多插入更新实体的方法,我非常好奇执行CRUD操作的最简单、最易管理的方法是什么
我知道等等,但我没有太多的接口经验,或者我似乎不完全理解使用了什么,所以我宁愿在完全理解之前不使用它 我的方法是使用IRepository模式来包装CRUD,并使依赖项注入在我的应用程序中更容易,下面是一个我如何做到这一点的示例: 按照以下方式定义您的合同: (我在简化示例,并承认您的所有表都有一个整数id-我的意思是它不是guid或字符串或其他-) 现在,在最后一部分中,我们转到业务服务层,并创建一个ServiceBase类,该类将由所有业务服务实现
public class ServiceBase : IServiceBase
{
private bool _disposed;
#region IServiceBase Implementation
[Dependency]
public IUnitOfWork UnitOfWork { protected get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
var disposableUow = UnitOfWork as IDisposable;
if (disposableUow != null)
disposableUow.Dispose();
}
_disposed = true;
}
#endregion
}
最后是业务服务类的一个示例,以及如何使用CRUD和处理业务规则(我使用的是属性注入,这不是最好的方法,因此我建议更改它并使用构造函数注入)
公共类票务服务:ServiceBase,ITicketService
{
#区域字段
专用IUserService(用户服务);
专用IAAuthorizationService(授权服务);
#端区
#区域属性
[依赖性]
公共IAAuthorizationService授权服务
{
设置{u authorizationService=value;}
}
[依赖性]
公共IUserService用户服务
{
设置{u userService=value;}
}
公共列表错误{get;set;}
#端区
#区域导体
公众票务服务()
{
错误=新列表();
}
#端区
#区域数据库实现
///
///描述
///
///TicketAnomalie数组
公共ICollection GetAll()
{
返回UnitOfWork.Tickets.GetAll();
}
///
///描述
///
///
///蒂克塔诺马利
公共票证GetTicketById(int id)
{
返回UnitOfWork.Tickets.GetById(id);
}
///
///描述在这里
///
///检票
公共ICollection GetAllTicketsWithDependencies()
{
返回UnitOfWork.Tickets.Query(tick=>true,tick=>tick.exceptions);
}
///
///描述在这里
///
///
///票
公共票证GetTicketWithDependencies(int id)
{
返回UnitOfWork.Tickets.Query(tick=>tick.Id==Id,tick=>tick.Exceptions).SingleOrDefault();
}
///
///将新票证添加到数据库
///
///
///布尔值
公共布尔添加(国际规范ID)
{
var anomalie=UnitOfWork.Exceptions.Query(ano=>ano.Id.Equals(anomalieId),ano=>ano.Tickets.FirstOrDefault();
var currentUser=WacContext.Current;
var superv=_userService.GetSupervisorUserProfile();
var sup=superv.FirstOrDefault();
if(异常值!=null)
{
var异常=新列表();
var anom=UnitOfWork.Exceptions.GetById(异常ID);
异常。添加(anom);
if(anomalie.Tickets.Count==0&&sup!=null)
{
var票证=新票证
{
用户=辅助Id,
CreatedBy=currentUser.GivenName,
异常=异常,
Path=UnitOfWork.SearchCriterias.GetById(anom.ParcoursId),
ContactPoint=UnitOfWork.ContactPoints.GetById(anom.ContactPointId)
};
工作单元。票证。添加(票证);
UnitOfWork.Commit();
}
}
其他的
{
添加(AnomaliseExceptions.AnonNullException);
}
如果(Errors.Count!=0)抛出新的BusinessException(Errors);
返回true;
}
公共bool更新(票务)
{
如果(票证==null)
{
添加(AnomaliseExceptions.AnonNullException);
}
其他的
如果(!存在(票证Id))
{
Errors.Add(anomalieseexceptions.AnoToUpdateNotExistException);
}
如果(Errors.Count!=0)抛出新的BusinessException(Errors);
工作单元。票证。更新(票证);
UnitOfWork.Commit();
返回true;
}
公共bool存在(int ticketId)
{
var操作本
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
#region Fields
protected DbContext CurrentContext { get; private set; }
protected DbSet<TEntity> EntitySet { get; private set; }
#endregion
#region Ctor
public GenericRepository(DbContext context)
{
CurrentContext = context;
EntitySet = CurrentContext.Set<TEntity>();
}
#endregion
#region IReadOnlyRepository Implementation
public virtual TEntity GetById(int id)
{
try
{
//use your logging method (log 4 net used here)
DomainEventSource.Log.Info(string.Format("getting entity {0} with id {1}", typeof(TEntity).Name, id));
return EntitySet.Find(id); //dbcontext manipulation
}
catch (Exception exception)
{
/// example of error handling
DomainEventSource.Log.Error(exception.Message);
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);// this is specific error formatting class you can do somthing like that to fit your needs
}
}
public virtual ICollection<TEntity> GetAll()
{
try
{
return EntitySet.ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
public virtual ICollection<TEntity> GetAll(params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = LoadProperties(includeProperties);
return query.ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
public virtual ICollection<TEntity> Query(Expression<Func<TEntity, bool>> expression, params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = LoadProperties(includeProperties);
return query.Where(expression).ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
// returning paged results for example
public PagedModel<TEntity> Query(Expression<Func<TEntity, bool>> expression,SortOptions sortOptions, PaginateOptions paginateOptions, params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = EntitySet.AsQueryable().Where(expression);
var count = query.Count();
//Unfortunatly includes can't be covered with a UT and Mocked DbSets...
if (includeProperties.Length != 0)
query = includeProperties.Aggregate(query, (current, prop) => current.Include(prop));
if (paginateOptions == null || paginateOptions.PageSize <= 0 || paginateOptions.CurrentPage <= 0)
return new PagedModel<TEntity> // specific pagination model, you can define yours
{
Results = query.ToList(),
TotalNumberOfRecords = count
};
if (sortOptions != null)
query = query.OrderByPropertyOrField(sortOptions.OrderByProperty, sortOptions.IsAscending);
var skipAmount = paginateOptions.PageSize * (paginateOptions.CurrentPage - 1);
query = query.Skip(skipAmount).Take(paginateOptions.PageSize);
return new PagedModel<TEntity>
{
Results = query.ToList(),
TotalNumberOfRecords = count,
CurrentPage = paginateOptions.CurrentPage,
TotalNumberOfPages = (count / paginateOptions.PageSize) + (count % paginateOptions.PageSize == 0 ? 0 : 1)
};
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
}
#endregion
#region IPersistRepository Repository
public bool Add(TEntity entity)
{
try
{
// you can do some extention methods here to set up creation date when inserting or createdBy etc...
EntitySet.Add(entity);
return true;
}
catch (Exception exception)
{
//DomainEventSource.Log.Failure(ex.Message);
//or
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
}
public bool AddRange(IEnumerable<TEntity> items)
{
try
{
foreach (var entity in items)
{
Add(entity);
}
}
catch (Exception exception)
{
//DomainEventSource.Log.Failure(ex.Message);
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool Update(TEntity entity)
{
try
{
CurrentContext.Entry(entity).State = EntityState.Modified;
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool Delete(TEntity entity)
{
try
{
if (CurrentContext.Entry(entity).State == EntityState.Detached)
{
EntitySet.Attach(entity);
}
EntitySet.Remove(entity);
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool DeleteById(TKey id)
{
var entityToDelete = GetById(id);
return Delete(entityToDelete);
}
#endregion
#region Loading dependancies Utilities
private IQueryable<TEntity> LoadProperties(IEnumerable<Expression<Func<TEntity, object>>> includeProperties)
{
return includeProperties.Aggregate<Expression<Func<TEntity, object>>, IQueryable<TEntity>>(EntitySet, (current, includeProperty) => current.Include(includeProperty));
}
#endregion
}
public class TicketRepository : GenericRepository<Ticket>, ITicketRepository
{
// the EntityRepository classes are made in case you have some ticket specific methods that doesn't
//have to be in generic repository
public TicketRepository(DbContext context)
: base(context)
{
}
// Add specific generic ticket methods here (not business methods-business methods will come later-)
}
public class UnitOfwork : IUnitOfWork
{
#region Fields
protected DbContext CurrentContext { get; private set; }
private ITicketRepository _tickets;
#endregion
#region ctor
public UnitOfwork(DbContext context)
{
CurrentContext = context;
}
#endregion
#region UnitOfWorkBaseImplementation
public void Commit()
{
try
{
CurrentContext.SaveChanges();
}
catch (Exception e)
{
/// catch
}
}
public void Rollback()
{
foreach (var entry in CurrentContext.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Deleted:
entry.State = EntityState.Modified; //Revert changes made to deleted entity.
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Detached:
break;
case EntityState.Unchanged:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
#region complete RollBack()
private void RejectScalarChanges()
{
foreach (var entry in CurrentContext.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Deleted:
entry.State = EntityState.Modified; //Revert changes made to deleted entity.
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Detached:
break;
case EntityState.Unchanged:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private void RejectNavigationChanges()
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var deletedRelationships = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted).Where(e => e.IsRelationship && !this.RelationshipContainsKeyEntry(e));
var addedRelationships = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Where(e => e.IsRelationship);
foreach (var relationship in addedRelationships)
relationship.Delete();
foreach (var relationship in deletedRelationships)
relationship.ChangeState(EntityState.Unchanged);
}
private bool RelationshipContainsKeyEntry(System.Data.Entity.Core.Objects.ObjectStateEntry stateEntry)
{
//prevent exception: "Cannot change state of a relationship if one of the ends of the relationship is a KeyEntry"
//I haven't been able to find the conditions under which this happens, but it sometimes does.
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var keys = new[] { stateEntry.OriginalValues[0], stateEntry.OriginalValues[1] };
return keys.Any(key => objectContext.ObjectStateManager.GetObjectStateEntry(key).Entity == null);
}
#endregion
public void Dispose()
{
if (CurrentContext != null)
{
CurrentContext.Dispose();
}
}
#endregion
#region properties
public ITicketRepository Tickets
{
get { return _tickets ?? (_tickets = new TicketRepository(CurrentContext)); }
}
#endregion
}
public class ServiceBase : IServiceBase
{
private bool _disposed;
#region IServiceBase Implementation
[Dependency]
public IUnitOfWork UnitOfWork { protected get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
var disposableUow = UnitOfWork as IDisposable;
if (disposableUow != null)
disposableUow.Dispose();
}
_disposed = true;
}
#endregion
}
public class TicketService : ServiceBase, ITicketService
{
#region fields
private IUserService _userService;
private IAuthorizationService _authorizationService;
#endregion
#region Properties
[Dependency]
public IAuthorizationService AuthorizationService
{
set { _authorizationService = value; }
}
[Dependency]
public IUserService UserService
{
set { _userService = value; }
}
public List<ExceptionDetail> Errors { get; set; }
#endregion
#region Ctor
public TicketService()
{
Errors = new List<ExceptionDetail>();
}
#endregion
#region IServiceBase Implementation
/// <summary>
/// desc
/// </summary>
/// <returns>array of TicketAnomalie</returns>
public ICollection<Ticket> GetAll()
{
return UnitOfWork.Tickets.GetAll();
}
/// <summary>
/// desc
/// </summary>
/// <param name="id"></param>
/// <returns>TicketAnomalie</returns>
public Ticket GetTicketById(int id)
{
return UnitOfWork.Tickets.GetById(id);
}
/// <summary>
/// description here
/// </summary>
/// <returns>Collection of Ticket</returns>
public ICollection<Ticket> GetAllTicketsWithDependencies()
{
return UnitOfWork.Tickets.Query(tick => true, tick => tick.Anomalies);
}
/// <summary>
/// description here
/// </summary>
/// <param name="id"></param>
/// <returns>Ticket</returns>
public Ticket GetTicketWithDependencies(int id)
{
return UnitOfWork.Tickets.Query(tick => tick.Id == id, tick => tick.Anomalies).SingleOrDefault();
}
/// <summary>
/// Add new ticket to DB
/// </summary>
/// <param name="anomalieId"></param>
/// <returns>Boolean</returns>
public bool Add(int anomalieId)
{
var anomalie = UnitOfWork.Anomalies.Query(ano => ano.Id.Equals(anomalieId), ano => ano.Tickets).FirstOrDefault();
var currentUser = WacContext.Current;
var superv = _userService.GetSupervisorUserProfile();
var sup = superv.FirstOrDefault();
if (anomalie != null)
{
var anomalies = new List<Anomalie>();
var anom = UnitOfWork.Anomalies.GetById(anomalieId);
anomalies.Add(anom);
if (anomalie.Tickets.Count == 0 && sup != null)
{
var ticket = new Ticket
{
User = sup.Id,
CreatedBy = currentUser.GivenName,
Anomalies = anomalies,
Path = UnitOfWork.SearchCriterias.GetById(anom.ParcoursId),
ContactPoint = UnitOfWork.ContactPoints.GetById(anom.ContactPointId)
};
UnitOfWork.Tickets.Add(ticket);
UnitOfWork.Commit();
}
}
else
{
Errors.Add(AnomaliesExceptions.AnoNullException);
}
if (Errors.Count != 0) throw new BusinessException(Errors);
return true;
}
public bool Update(Ticket ticket)
{
if (ticket == null)
{
Errors.Add(AnomaliesExceptions.AnoNullException);
}
else
if (!Exists(ticket.Id))
{
Errors.Add(AnomaliesExceptions.AnoToUpdateNotExistException);
}
if (Errors.Count != 0) throw new BusinessException(Errors);
UnitOfWork.Tickets.Update(ticket);
UnitOfWork.Commit();
return true;
}
public bool Exists(int ticketId)
{
var operationDbEntity =
UnitOfWork.Tickets.Query(t => t.Id.Equals(ticketId)).ToList();
return operationDbEntity.Count != 0;
}
#endregion
#region Business Implementation
//play with your buiness :)
#endregion
}