貼一個EF6 CodeFirst模式結合MVC5和Autofac(泛型註冊)的一個入門實例 網上類似的例子實在太少,最近自己也有用到這一塊的知識,總結了一下,不要讓後人踩了自己踩過的坑。 1:新建三個項目,Web(MVC)、EntityFramework類庫(EF框架)、Core類庫(核心框架),n ...
貼一個EF6 CodeFirst模式結合MVC5和Autofac(泛型註冊)的一個入門實例
網上類似的例子實在太少,最近自己也有用到這一塊的知識,總結了一下,不要讓後人踩了自己踩過的坑。
1:新建三個項目,Web(MVC)、EntityFramework類庫(EF框架)、Core類庫(核心框架),nuget EntityFramework,Autofac,AutofacMvc5
2:建立簡單對象:書籍(Book)Model,繼承主鍵為Int的基類
①:介面
namespace:Core.Domain.Interface
1 /// <summary> 2 /// 實體對象介面 3 /// </summary> 4 public interface IEntity<PrimaryKeyType> 5 { 6 /// <summary> 7 /// 主鍵 8 /// </summary> 9 PrimaryKeyType Id { get; } 10 }IEntity
②:基類
namespace:Core.Domain.Base
1 /// <summary> 2 /// Int抽象基類 3 /// </summary> 4 /// <typeparam name="Type"></typeparam> 5 public abstract class EntityBase : Interface.IEntity<int> 6 { 7 #region Properties 8 9 /// <summary> 10 /// 數據狀態 11 /// </summary> 12 public Enum.EntityEnumType Status { get; set; } 13 14 public DateTime CreateDate { get; set; } 15 16 public DateTime UpdateDate { get; set; } 17 18 /// <summary> 19 /// 主鍵 20 /// </summary> 21 [Key] 22 public int Id { get; set; } 23 #endregion 24 25 #region Constructor 26 public EntityBase() :this(Enum.EntityEnumType.Normal,DateTime.Now,DateTime.Now) 27 { } 28 29 /// <summary> 30 /// 帶參初始化 31 /// </summary> 32 public EntityBase(Enum.EntityEnumType _status,DateTime _createDate,DateTime _updateDate) 33 { 34 Status = _status; 35 CreateDate = _createDate; 36 UpdateDate = _updateDate; 37 } 38 #endregion 39 40 #region Methods 41 42 #endregion 43 44 }EntityBase
③:書籍類
namespace:Web.Models
1 [Table("Book")]//表名 2 /// <summary> 3 /// 實體—書籍 4 /// </summary> 5 public class Book:EntityBase 6 { 7 #region Constructor 8 public Book() 9 : base() 10 { } 11 #endregion 12 13 #region Fileds 14 /// <summary> 15 /// 書籍編號 16 /// </summary> 17 [DisplayName("書籍編號")] 18 public int BookCode { get; set; } 19 20 /// <summary> 21 /// 書名 22 /// </summary> 23 [DisplayName("書名")] 24 [MaxLength(50,ErrorMessage = "書名過長"),MinLength(2,ErrorMessage = "書名過短")] 25 public string BookName { get; set; } 26 27 /// <summary> 28 /// 作者 29 /// </summary> 30 [MaxLength(50,ErrorMessage = "作者姓名過長")] 31 [DisplayName("作者")] 32 public string Author { get; set; } 33 34 #endregion 35 36 #region 導航屬性 37 38 #endregion 39 40 41 #region IEntity成員 42 #endregion 43 }Book
解釋一下類的配置
CodeFirst 利用一種被稱為約定(Conventions)優於配置(Configuration)的編程模式允許你使用自己的 對象 來表示 EF 所依賴的模型去執行查詢、更改追蹤、以及更新功能
簡單的說,你創建的對象必須遵循EF給你指定的規則,比如最後兩位以Id為結尾就是主鍵啊什麼什麼什麼,反正我是沒遵循。
如果你不想遵循EF的規則,那麼——
Code First 提供了兩種方式來配置你的類:
- DataAnnotations, 使用簡單屬性;
- Fluent API, 以編程命令行式的方式來描述你的配置
有關於配置的文章,請參考
EF官方文檔:https://msdn.microsoft.com/en-us/library/ee712907(v=vs.113).aspx
另外,有問題記得F1看文檔,不要一直百度
3:倉儲Repository設計
①:IRepository
namespace:Core.Repository
1 public interface IRepository<TEntity> where TEntity:class 2 { 3 /// <summary> 4 /// 添加一個對象 5 /// </summary> 6 /// <param name="item"></param> 7 void Insert(TEntity item); 8 9 /// <summary> 10 /// 刪除一個對象 11 /// </summary> 12 void Delete(TEntity item); 13 14 /// <summary> 15 /// 更新一個對象 16 /// </summary> 17 void Update(TEntity item); 18 19 /// <summary> 20 /// 通過主鍵取對象 21 /// </summary> 22 /// <returns></returns> 23 TEntity Find(params object[] id); 24 25 /// <summary> 26 /// 拿到可查詢結果集 27 /// </summary> 28 /// <returns></returns> 29 System.Linq.IQueryable<TEntity> GetModel(); 30 31 /// <summary> 32 /// 設置數據上下文 33 /// </summary> 34 /// <param name="db"></param> 35 void SetDataContextByResloveName(string dbContext); 36 }IRepository
SetDataContextByResloveName這個方法,到下文的Autofac配置時再說
②:IExtentionRepository 擴展
namespace:Core.IExtentionRepository
1 /// <summary> 2 /// 擴展倉儲 3 /// </summary> 4 /// <typeparam name="TEntity"></typeparam> 5 public interface IExtentionRepository<TEntity>:IRepository<TEntity> where TEntity:class 6 { 7 #region 行為 8 /// <summary> 9 /// 添加集合 10 /// </summary> 11 /// <param name="item"></param> 12 void Insert(IEnumerable<TEntity> item); 13 /// <summary> 14 /// 修改集合 15 /// </summary> 16 /// <param name="item"></param> 17 void Update(IEnumerable<TEntity> item); 18 /// <summary> 19 /// 刪除集合 20 /// </summary> 21 /// <param name="item"></param> 22 void Delete(IEnumerable<TEntity> item); 23 #endregion 24 25 #region 查詢 26 /// <summary> 27 /// 根據指定lambda表達式,得到結果集 28 /// </summary> 29 /// <param name="predicate"></param> 30 /// <returns></returns> 31 IQueryable<TEntity> GetModel(Expression<Func<TEntity, bool>> predicate); 32 33 /// <summary> 34 /// 根據指定lambda表達式,得到第一個實體 35 /// </summary> 36 /// <param name="predicate"></param> 37 /// <returns></returns> 38 TEntity Find(Expression<Func<TEntity, bool>> predicate); 39 #endregion 40 }IExtentionRepository
③:EFRepository EF倉儲
namespace:EntityFramework
1 public class EFRepository<TEntity> : 2 IRepository<TEntity>, 3 IExtentionRepository<TEntity> 4 where TEntity:class 5 { 6 #region Properties 7 /// <summary> 8 /// EF上下文 9 /// </summary> 10 private DbContext EFDbContext; 11 #endregion 12 13 #region Constructors 14 public EFRepository() 15 : this(null) 16 { } 17 18 /// <summary> 19 /// 帶參構造函數 20 /// </summary> 21 public EFRepository(DbContext db) 22 { 23 EFDbContext = db; 24 } 25 #endregion 26 27 #region Methods 28 /// <summary> 29 /// 提交到資料庫 30 /// </summary> 31 public void SaveChanges() 32 { 33 try 34 { 35 EFDbContext.SaveChanges(); 36 } 37 catch (Exception ex) 38 { 39 //保存日誌 40 //拋出異常 41 throw new MithrilCommonException(ex.Message, ex); 42 } 43 } 44 45 46 #endregion 47 48 #region IReposiotry<TEntity> 49 50 /// <summary> 51 /// 少量數據插入 52 /// </summary> 53 /// <param name="item"></param> 54 public void Insert(TEntity item) 55 { 56 if (item != null) 57 { 58 EFDbContext.Entry<TEntity>(item as TEntity); 59 EFDbContext.Set<TEntity>().Add(item as TEntity); 60 this.SaveChanges(); 61 } 62 } 63 64 /// <summary> 65 /// 刪除一個實體 66 /// </summary> 67 /// <param name="id"></param> 68 public void Delete(TEntity item) 69 { 70 if (item != null) 71 { 72 //將實體以"UnChanged"的狀態放置到上下文中 73 EFDbContext.Set<TEntity>().Attach(item as TEntity); 74 //給定實體標記為“已刪除” 75 EFDbContext.Entry(item).State = EntityState.Deleted; 76 //EFDbContext.Set<TEntity>().Remove(item as TEntity); 77 this.SaveChanges(); 78 } 79 } 80 81 /// <summary> 82 /// 更新一個實體 83 /// </summary> 84 /// <param name="item"></param> 85 public void Update(TEntity item) 86 { 87 if(item != null) 88 { 89 //將實體以"UnChanged"的狀態放置到上下文中 90 EFDbContext.Set<TEntity>().Attach(item as TEntity); 91 //更新 92 EFDbContext.Entry(item).State = EntityState.Modified; 93 this.SaveChanges(); 94 } 95 } 96 97 /// <summary> 98 /// 根據id獲取實體 99 /// </summary> 100 /// <param name="id"></param> 101 /// <returns></returns> 102 public TEntity Find(params object[] id) 103 { 104 return EFDbContext.Set<TEntity>().Find(id); 105 } 106 107 /// <summary> 108 /// 拿到可查詢結果集 109 /// </summary> 110 /// <returns></returns> 111 System.Linq.IQueryable<TEntity> GetModel() 112 { 113 return null; 114 } 115 116 //設置數據上下文 117 public void SetDataContextByResloveName(string contextName) 118 { 119 try 120 { 121 EFDbContext = ServiceLocator.Instance.GetService<DbContext>(contextName); 122 } 123 catch (Exception) 124 { 125 throw new ArgumentException("設置EFContext出錯"); 126 } 127 128 } 129 130 /// <summary> 131 /// 拿到可查詢結果集 132 /// </summary> 133 /// <returns></returns> 134 IQueryable<TEntity> IRepository<TEntity>.GetModel() 135 { 136 throw new NotImplementedException(); 137 } 138 139 140 #endregion 141 142 #region IExtentionRepository 143 public void Insert(IEnumerable<TEntity> item) 144 { 145 foreach (var entity in item) 146 { 147 EFDbContext.Entry<TEntity>(entity as TEntity); 148 EFDbContext.Set<TEntity>().Add(entity as TEntity); 149 } 150 this.SaveChanges(); 151 } 152 153 public void Update(IEnumerable<TEntity> item) 154 { 155 #region 給每個對象改變狀態 156 foreach (var model in item) 157 { 158 EFDbContext.Set<TEntity>().Attach(model as TEntity); 159 EFDbContext.Entry(model).State = EntityState.Modified; 160 this.SaveChanges(); 161 } 162 #endregion 163 } 164 165 public void Delete(IEnumerable<TEntity> item) 166 { 167 throw new NotImplementedException(); 168 } 169 170 public IQueryable<TEntity> GetModel(Expression<Func<TEntity, bool>> predicate) 171 { 172 throw new NotImplementedException(); 173 } 174 175 public TEntity Find(Expression<Func<TEntity, bool>> predicate) 176 { 177 throw new NotImplementedException(); 178 } 179 #endregion 180 }EFRepository
其中拋出的Exception為自定義的異常類
4:CodeFirst遷移
vs17中:視圖 > 其他視窗 > 程式包管理控制台
預設項目欄中選擇相應的項目,
①:Enable-Migrations -ContextTypeName -Force
為你制定的上下文開始遷移工作,-ContextTypeName 為你的上下文名稱,-Force為可選項,是否覆蓋你所選的上下文的遷移
運行後,會生成Migration文件夾
②:Add-Migration -MigrationName -Force
當你新建資料庫,添加欄位、刪除、更新欄位時,輸入此命令,根據你DbContext實現類的策略以及之前對象的約束,CodeFirst會生成資料庫以及相應的表
③:Update-DataBase,將改動對應到實體庫上
貼一個測試DbContext類
1 public class EFTestContext : DbContext 2 { 3 4 public EFTestContext() 5 : base("name=EFTestContext") 6 { 7 //模型變更時更新資料庫 8 Database.SetInitializer<EFTestContext>(new CreateDatabaseIfNotExists<EFTestContext>()); 9 } 10 11 public DbSet<Book> Book { get; set; } 12 13 protected override void OnModelCreating(DbModelBuilder modelBuilder) 14 { 15 modelBuilder.Entity<Book>().MapToStoredProcedures(); 16 base.OnModelCreating(modelBuilder); 17 } 18 }EFTestContext
5:MVC5 Autofac配置
網上有很多例子,並不符合實際開發,這裡重點說一下一個介面有很多實現類的情況,應該如何配置Autofac
①:Global.asax配置
1 protected void Application_Start() 2 { 3 AreaRegistration.RegisterAllAreas(); 4 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 5 RouteConfig.RegisterRoutes(RouteTable.Routes); 6 BundleConfig.RegisterBundles(BundleTable.Bundles); 7 8 IoCConfig(); 9 } 10 11 /// <summary> 12 /// IoC註入 13 /// </summary> 14 private void IoCConfig() 15 { 16 var builder = new ContainerBuilder(); 17 #region Repository註冊 18 builder.RegisterGeneric(typeof(EFRepository<>)).Named("EFRepository",typeof(IRepository<>)).InstancePerLifetimeScope(); 19 #endregion 20 21 #region Service註冊 22 builder.RegisterType(typeof(EFTestContext)).Named("EF", typeof(DbContext)).InstancePerLifetimeScope(); 23 #endregion 24 25 //註冊 Controller 26 builder.RegisterControllers(typeof(MvcApplication).Assembly); 27 var container = builder.Build(); 28 //MVC擴展 29 DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 30 }泛型配置
泛型配置看官方文檔
http://docs.autofac.org/en/latest/advanced/adapters-decorators.html
②:ServiceLocator,用於實現同一介面不同實現的Autofac Reslove
1 public sealed class ServiceLocator 2 { 3 #region Constructor 4 public ServiceLocator() 5 { 6 7 } 8 #endregion 9 10 #region Singleton 11 private static readonly ServiceLocator _instance = new ServiceLocator(); 12 13 /// <summary> 14 /// 實例 15 /// </summary> 16 public static ServiceLocator Instance 17 { 18 get 19 { 20 return _instance; 21 } 22 } 23 #endregion 24 25 #region Public Methods 26 public TEntity GetService<TEntity>(string resloverName) 27 { 28 return AutofacDependencyResolver.Current.RequestLifetimeScope.ResolveNamed<TEntity>(resloverName); 29 } 30 #endregion 31 }ServiceLocator
6:MVC Web測試