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
}