Web APi之EntityFramework【CRUD】(三)

前言

以前咱們系統學習了EntityFramework,我的以爲有些東西不能學了就算完了,必需要學以至用,在Web API上也少不了增(C)、刪(D)、改(U)、查(R)。鑑於此,咱們經過EF來實現Web API上的增刪改查。sql

 

以前對於EF的基本操做都是很零散的,咱們應該對於CRUD都是經過完整封裝來實現,而且也顯得比較專業,接下來首先對EF利用Responsitory倉儲模式進行完整封裝。

 

EntityFramework完整封裝

咱們創建一個Core(核心類庫),裏面存放有關EF的完成封裝。數據庫

第一步

創建全部實體的基類,將實體的公共屬性放入其中,取爲BaseEntity

    public class BaseEntity<T>
    {
        public T Id { get; set; }
    }

第二步

創建倉儲接口IRepository,包括基本的增、刪、改、查等方法

    public interface IRepository<TEntity> where TEntity : class
    {
        /// <summary>
        /// 得到數據列表
        /// </summary>
        /// <returns></returns>
        IQueryable<TEntity> GetList();

        /// <summary>
        /// 經過id得到實體
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        TEntity GetById(object id);

        /// <summary>
        /// 添加實體
        /// </summary>
        /// <param name="entity"></param>
        int Insert(TEntity entity);


        /// <summary>
        /// 添加實體集合
        /// </summary>
        /// <param name="entities"></param>
        int Insert(IEnumerable<TEntity> entities);

        /// <summary>
        /// 刪除實體
        /// </summary>
        /// <param name="entity"></param>
        int Delete(TEntity entity);


        /// <summary>
        /// 根據條件刪除實體
        /// </summary>
        /// <param name="entities"></param>
        int DeleteByRequirement(Expression<Func<TEntity, bool>> func);


        /// <summary>
        /// 更新實體
        /// </summary>
        /// <param name="entity"></param>
        int Update(TEntity entity);



        /// <summary>
        /// 更新實體集合
        /// </summary>
        /// <param name="entities"></param>
        int Update(IEnumerable<TEntity> entities);

    }

 

 第三步

利用倉儲服務RepositoryService實現上述倉儲接口IRepository

    public class RepositoryService<TEntity> : IRepository<TEntity> where TEntity : class
    {
        private IDbContext Context;
        private bool IsNoTracking;

        /// <summary>
        /// 獲取實體集合
        /// </summary>
        private IDbSet<TEntity> Entities
        {
            get
            {

                return this.Context.Set<TEntity>();
            }
        }

        private DbEntityEntry Entry(TEntity entity)
        {
            return this.Context.Entry<TEntity>(entity);
        }

        public RepositoryService(IDbContext context, bool isNoTracking)
        {

            this.Context = context;
            this.IsNoTracking = isNoTracking;
        }

        /// <summary>
        /// 獲取全部數據
        /// </summary>
        /// <returns></returns>
        public IQueryable<TEntity> GetList()
        {
            if (!IsNoTracking)
                return this.Entities.AsQueryable();
            else
                return this.Entities.AsNoTracking().AsQueryable();
        }

        /// <summary>
        /// 經過id獲取實體
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public TEntity GetById(object id)
        {
            return Entities.Find(id);

        }

        /// <summary>
        /// 添加實體
        /// </summary>
        /// <param name="entity"></param>
        public int Insert(TEntity entity)
        {
            Entities.Add(entity);
            return this.Context.SaveChanges();
        }

        public int Insert(IEnumerable<TEntity> entities)
        {
            if (entities == null)
                throw new ArgumentNullException("entities");
            foreach (var entity in entities)
            {
                Entities.Add(entity);
            }
            return this.Context.SaveChanges();
        }

        /// <summary>
        /// 刪除實體
        /// </summary>
        /// <param name="entity"></param>
        public int Delete(TEntity entity)
        {
            if (!IsNoTracking)
                this.Entities.Remove(entity);
            else
                this.Entities.Attach(entity);
            this.Entities.Remove(entity);
            return this.Context.SaveChanges();
        }

        public int DeleteByRequirement(Expression<Func<TEntity, bool>> func)
        {
            var list = GetList().Where(func).ToList();
            list.ForEach(e =>
            {
                if (!IsNoTracking)
                    this.Entities.Remove(e);
                else
                    this.Entities.Attach(e);
                this.Entities.Remove(e);
            });

            return this.Context.SaveChanges();
        }

        /// <summary>
        /// 更新實體
        /// </summary>
        /// <param name="entity"></param>
        public int Update(TEntity entity)
        {
            if (!IsNoTracking)
                return this.Context.SaveChanges();
            else
                this.Context.Entry(entity).State = EntityState.Modified;
            return this.Context.SaveChanges();
        }

        public int Update(IEnumerable<TEntity> entities)
        {
            if (entities == null)
                throw new ArgumentNullException("enetities");
            if (!IsNoTracking)
                return this.Context.SaveChanges();
            else
                foreach (var t in entities)
                {
                    this.Context.Entry(t).State = EntityState.Modified;
                }

            return this.Context.SaveChanges();
        }


        /// <summary>
        /// 釋放資源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (Context != null)
                {
                    this.Context.Dispose();
                    this.Context = null;
                }
            }
        }

    }

第四步

用接口IDbContext封裝EF上下文DbContext中的公共方法

    public interface IDbContext
    {

        /// <summary>
        /// 得到實體集合
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        IDbSet<TEntity> Set<TEntity>() where TEntity : class;


        /// <summary>
        /// 執行存儲過程
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
        where TEntity : class;


        /// <summary>
        /// 執行SQL語句查詢
        /// </summary>
        /// <typeparam name="TElement"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters);


        DbEntityEntry Entry<TEntity>(TEntity entity) where TEntity : class;

        /// <summary>
        /// 保存數據
        /// </summary>
        /// <returns></returns>
        int SaveChanges();

       

        /// <summary>
        /// 變動追蹤代碼
        /// </summary>
        bool ProxyCreationEnabled { get; set; }


        /// <summary>
        /// DetectChanges方法自動調用
        /// </summary>
        bool AutoDetectChangesEnabled { get; set; }

        /// <summary>
        /// 調用Dispose方法
        /// </summary>
        void Dispose();

    }

第五步

實現EF上下文中的數據庫鏈接、模型初始化以及映射等(也能夠手動關閉全局變動追蹤相對比較靈活)

    public class EFDbContext : DbContext, IDbContext
    {
        public EFDbContext(string connectionString)
            : base(connectionString)
        { }


        static EFDbContext()
        {
            Database.SetInitializer<EFDbContext>(new DropCreateDatabaseIfModelChanges<EFDbContext>());
        }

        /// <summary>
        /// 一次性加載全部映射
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {

            var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
            .Where(type => !String.IsNullOrEmpty(type.Namespace))
            .Where(type => type.BaseType != null && type.BaseType.IsGenericType &&
                type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
            foreach (var type in typesToRegister)
            {
                dynamic configurationInstance = Activator.CreateInstance(type);
                modelBuilder.Configurations.Add(configurationInstance);
            }


            base.OnModelCreating(modelBuilder);
        }

        /// <summary>
        /// 得到實體集合
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
        {
            return base.Set<TEntity>();

        }

        /// <summary>
        /// 實體狀態
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>

        public new DbEntityEntry Entry<TEntity>(TEntity entity) where TEntity : class
        {
            return base.Entry<TEntity>(entity);
        }

        /// <summary>
        /// 執行存儲過程
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : class
        {

            if (parameters != null && parameters.Length > 0)
            {
                for (int i = 0; i <= parameters.Length - 1; i++)
                {
                    var p = parameters[i] as DbParameter;
                    if (p == null)
                        throw new Exception("Not support parameter type");

                    commandText += i == 0 ? " " : ", ";

                    commandText += "@" + p.ParameterName;
                    if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output)
                    {

                        commandText += " output";
                    }
                }
            }

            var result = this.Database.SqlQuery<TEntity>(commandText, parameters).ToList();


            bool acd = this.Configuration.AutoDetectChangesEnabled;
            try
            {
                this.Configuration.AutoDetectChangesEnabled = false;

                for (int i = 0; i < result.Count; i++)
                    result[i] = this.Set<TEntity>().Attach(result[i]);
            }
            finally
            {
                this.Configuration.AutoDetectChangesEnabled = acd;
            }

            return result;
        }


        /// <summary>
        /// SQL語句查詢
        /// </summary>
        /// <typeparam name="TElement"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
        {
            return this.Database.SqlQuery<TElement>(sql, parameters);
        }


        /// <summary>
        /// 當查詢或者獲取值時是否啓動建立代理
        /// </summary>
        public virtual bool ProxyCreationEnabled
        {
            get
            {
                return this.Configuration.ProxyCreationEnabled;
            }
            set
            {
                this.Configuration.ProxyCreationEnabled = value;
            }
        }


        /// <summary>
        /// 當查詢或者獲取值時指定是否開啓自動調用DetectChanges方法
        /// </summary>
        public virtual bool AutoDetectChangesEnabled
        {
            get
            {
                return this.Configuration.AutoDetectChangesEnabled;
            }
            set
            {
                this.Configuration.AutoDetectChangesEnabled = value;
            }
        }

    }

 以上就是對利用EntityFramework來實現基本操做的完整封裝。接下來就是相關類以及映射(場景:一個Student對應一個Flower,而一個Flower對應多個Student)ide

Student

    public class Student : BaseEntity<int>
    {

        public string Name { get; set; }


        public int FlowerId { get; set; }
        public virtual Flower Flower { get; set; }
    }

Flower

   public class Flower : BaseEntity<int>
    {

        public string Remark { get; set; }

        public virtual ICollection<Student> Students { get; set; }
    }

相關映射

    public class StudentMap:EntityTypeConfiguration<Student>
    {
        public StudentMap()
        {
            ToTable("Student");
            HasKey(p => p.Id);
            Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            HasRequired(p => p.Flower).WithMany(p => p.Students).HasForeignKey(p => p.FlowerId);
          
        }
    }


    public class FlowerMap:EntityTypeConfiguration<Flower>
    {
        public FlowerMap()
        {
            ToTable("Flower");
            HasKey(p => p.Id);
            Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        }
    }

CRUD 

接下來就是Web API控制器中執行增、刪等操做,咱們建立一個StudentController控制器,而後首先建立倉儲服務。(執行Action方法,依據默認約定,未添加特性)性能

        public IRepository<Student> _repository;
        public EFDbContext _ctx;
        public StudentController()
        {
            _ctx = new EFDbContext("DBByConnectionString");
            _repository = new RepositoryService<Student>(_ctx, true);  //關閉局部變動追蹤 }

執行R操做(即默認請求到HttpGet方法)

        public IEnumerable<Student> GetAllStudent()
        {

            return _repository.GetList().Select(d => new Student { Name = d.Name, Flower = d.Flower, Id = d.Id }).ToList();

        }

當執行此查詢操做時卻出錯了,真遺憾:學習

上述修改以下便可:ui

 return _repository.GetList().ToList().Select(d => new Student { Name = d.Name, Flower = d.Flower, Id = d.Id }).ToList();

不知道還有沒有更好的解決方案,用ToList直接將全部數據進行加載到內存中,在性能上消耗比較大。(期待你的解決方案)

特此記錄

在此感謝園友(_天光雲影)給出的解決方案,在GetList以後利用 Linq 進行Select,最後進行ToList便可!!!this

執行CUD等操做

       public Student GetStudentById(int id)
        {
            var student = _repository.GetById(id);
            if (student == null)
                throw new HttpResponseException(HttpStatusCode.NotFound);
            else
                return student;
        }

        //添加操做(HttpPost)
        public HttpResponseMessage PostStudent(Student stu)
        {
            var insertStudent = _repository.Insert(stu);
            var response = Request.CreateResponse<Student>(HttpStatusCode.Created, stu);

            string uri = Url.Link("DefaultApi", new { id = stu.Id });
            response.Headers.Location = new Uri(uri);
            return response;
        }

       //更新操做(HttpPut)
        public void PutStudent(int id, Student stu)
        {
            stu.Id = id;
            if (_repository.Update(stu) <= 0)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }
       
        //刪除操做(HttpDelete)
        public void DeleteStudent(int id)
        {
            Student stu = _repository.GetById(id);
            if (stu == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            _repository.Delete(stu);
        }
View Code

 總結

這節主要介紹了利用倉儲模式完整封裝EF來進行Web API基本操做,基本操做中關於返回狀態碼等信息,無非就是如下幾個對象spa

HttpResponseException  返回異常

HttpResponseMessage   返回信息(諸如狀態碼等)

HttpStatusCode       狀態碼枚舉(如頁面未找到等)

 源代碼下載

WebAPI之EntityFramework代理

相關文章
相關標籤/搜索