using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace FB.CMS.MvcSite.App_Start { using Autofac; using Autofac... ...
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace FB.CMS.MvcSite.App_Start { using Autofac; using Autofac.Integration.Mvc; using System.Reflection; using System.Web.Mvc; /// <summary> /// 這個類是我自己定義的一個類,主要用初始化AutoFac容器的相關數據 /// </summary> public class AutoFacConfig { public static void Register() { //初始化AutoFac的相關功能 /* 1.0 告訴AutoFac初始化數據倉儲層FB.CMS.Repository.dll中所有類的對象實例。這些對象實例以其介面的形式保存在AutoFac容器中 2.0 告訴AutoFac初始化業務邏輯層FB.CMS.Services.dll中所有類的對象實例。這些對象實例以其介面的形式保存在AutoFac容器中 3.0 將MVC預設的控制器工廠替換成AutoFac的工廠 */ //第一步: 構造一個AutoFac的builder容器 ContainerBuilder builder = new Autofac.ContainerBuilder(); //第二步:告訴AutoFac控制器工廠,控制器類的創建去哪些程式集中查找(預設控制器工廠是去掃描bin目錄下的所有程式集) //2.1 從當前運行的bin目錄下載入FB.CMS.MvcSite.dll程式集 Assembly controllerAss = Assembly.Load("FB.CMS.MvcSite"); //2.2 告訴AutoFac控制器工廠,控制器的創建從controllerAss中查找(註意:RegisterControllers()方法是一個可變參數,如果你的控制器類的創建需要去多個程式集中查找的話,
那麼我們就再用Assembly controllerBss=Assembly.Load("需要的程式集名")載入需要的程式集,然後與controllerAss組成數組,然後將這個數組傳遞到RegisterControllers()方法中) builder.RegisterControllers(controllerAss); //第三步:告訴AutoFac容器,創建項目中的指定類的對象實例,以介面的形式存儲(其實就是創建數據倉儲層與業務邏輯層這兩個程式集中所有類的對象實例,然後以其介面的形式保存到AutoFac容器記憶體中,
當然如果有需要也可以創建其他程式集的所有類的對象實例,這個只需要我們指定就可以了) //3.1 載入數據倉儲層FB.CMS.Repository這個程式集。 Assembly repositoryAss = Assembly.Load("FB.CMS.Repository"); //3.2 反射掃描這個FB.CMS.Repository.dll程式集中所有的類,得到這個程式集中所有類的集合。 Type[] rtypes = repositoryAss.GetTypes(); //3.3 告訴AutoFac容器,創建rtypes這個集合中所有類的對象實例 builder.RegisterTypes(rtypes) .AsImplementedInterfaces(); //指明創建的rtypes這個集合中所有類的對象實例,以其介面的形式保存 //3.4 載入業務邏輯層FB.CMS.Services這個程式集。 Assembly servicesAss = Assembly.Load("FB.CMS.Services"); //3.5 反射掃描這個FB.CMS.Services.dll程式集中所有的類,得到這個程式集中所有類的集合。 Type[] stypes = servicesAss.GetTypes(); //3.6 告訴AutoFac容器,創建stypes這個集合中所有類的對象實例 builder.RegisterTypes(stypes) .AsImplementedInterfaces(); //指明創建的stypes這個集合中所有類的對象實例,以其介面的形式保存 //第四步:創建一個真正的AutoFac的工作容器 var container = builder.Build(); //我們已經創建了指定程式集的所有類的對象實例,並以其介面的形式保存在AutoFac容器記憶體中了。那麼我們怎麼去拿它呢? //從AutoFac容器內部根據指定的介面獲取其實現類的對象實例 //假設我要拿到IsysFunctionServices這個介面的實現類的對象實例,怎麼拿呢? //var obj = container.Resolve<IsysFunctionServices>(); //只有有特殊需求的時候可以通過這樣的形式來拿。一般情況下沒有必要這樣來拿,因為AutoFac會自動工作
(即:會自動去類的帶參數的構造函數中找與容器中key一致的參數類型,並將對象註入到類中,其實就是將對象賦值給構造函數的參數) //第五步:將當前容器中的控制器工廠替換掉MVC預設的控制器工廠。(即:不要MVC預設的控制器工廠了,用AutoFac容器中的控制器工廠替代)此處使用的是將AutoFac工作容器交給MVC底層 (需要using System.Web.Mvc;) DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); //我們知道控制器的創建是調用MVC預設的控制器工廠,預設的控制器工廠是調用控制器類的無參構造函數 //可是我們如果要使用AutoFac自動工廠,將對象通過構造函數註入類中,那麼這個構造函數就需要帶參數 //如果我們將控制器的無參構造函數刪除,保留帶參數的構造函數,MVC預設的控制器工廠來創建控制的時候 //就會去調用無參的構造函數,可是這時候發現沒有無參的構造函數於是就報“沒有為該對象定義無參數的構造函數”錯誤 //既然報錯,那我們如果保留無參的構造函數,同時在聲明一個帶參數的構造函數是否可行呢? //答案;行是行,但是創建控制器的時候,MVC預設的控制器工廠調用的是無參構造函數,它並不會去調用有參的構造函數 //這時候,我們就只能將AutoFac它的控制器工廠替換調用MVC預設的控制器工廠(控制器由AutoFac的控制器工廠來創建) //而AutoFac控制器工廠在創建控制的時候只會掃描帶參數的構造函數,並將對象註入到帶參數的構造函數中 //AutofacDependencyResolver這個控制器工廠是繼承了 IDependencyResolver介面的,而IDependencyResolver介面是MVC的東西 //MVC預設的控制器工廠名字叫:DefaultControllerFactory //具體參考:http://www.cnblogs.com/artech/archive/2012/04/01/controller-activation-032.html } } }
using FB.CMS.MvcSite.App_Start; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace FB.CMS.MvcSite { // 註意: 有關啟用 IIS6 或 IIS7 經典模式的說明, // 請訪問 http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //第一: 在網站一啟動的時候就初始化AutoFac的相關功能 /* 1.0 告訴AutoFac初始化數據倉儲層FB.CMS.Repository.dll中所有類的對象實例。這些對象實例以其介面的形式保存在AutoFac容器中 2.0 告訴AutoFac初始化業務邏輯層FB.CMS.Services.dll中所有類的對象實例。這些對象實例以其介面的形式保存在AutoFac容器中 3.0 將MVC預設的控制器工廠替換成AutoFac的工廠 */ //具體做法就是我們去App_Start文件夾下創建一個AutoFacConfig類,具體實現什麼功能去這個類中實現。然後再這裡調用下這個類 AutoFacConfig.Register(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace FB.CMS.MvcSite.Controllers { using FB.CMS.IServices; public class HomeController : Controller { IsysFunctionServices dal; public HomeController(IsysFunctionServices dal) //依賴構造函數進行對象註入 { this.dal = dal; //在構造函數中初始化HomeController控制器類的dal屬性 (這個dal屬性的類型是IsysFunctionServices) } public ActionResult Index() { var a = dal.QueryWhere(r => r.fID > 20).ToList(); //查詢 return View(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FB.CMS.Repository { using FB.CMS.IRepository; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Linq.Expressions; using System.Runtime.Remoting.Messaging; using System.Threading; public class BaseDal<TEntity> : IBaseDal<TEntity> where TEntity : class { //BaseDbContext db = new BaseDbContext(); //對創建上下文容器類對象進行優化(原理:一個線程下我們只創建一個上下文容器類對象,然後保存到線程緩存當中去,當同一個線程過來的時候,就從線程緩存當中取上下文容器類對象) public BaseDbContext db { get { //獲取BaseDbContext的完全限定名,其實這個名字沒什麼特別的意義,僅僅是一個名字而已,也可以取別的名字的 string threadName = typeof(BaseDbContext).FullName; //獲取key為threadName的這個線程緩存(CallContext就是線程緩存容器類) object dbObj = CallContext.GetData(threadName); //如果key為threadName的線程緩存不存在 if (dbObj == null) { //創建BaseDbContext類的對象實例 dbObj = new BaseDbContext(); //將這個BaseDbContext類的對象實例保存到線程緩存當中(以鍵值對的形式進行保存的,我這就將key設為當前線程的完全限定名了) CallContext.SetData(threadName, dbObj); return dbObj as BaseDbContext; } return dbObj as BaseDbContext; } } DbSet<TEntity> _dbset; public BaseDal() { this._dbset = db.Set<TEntity>(); //初始化 } #region 增加 public void AddEnity(TEntity model) { if (model == null) { throw new Exception("moddel不能為null"); } this._dbset.Add(model); } #endregion #region 物理刪除 /// <summary> /// 刪除 /// </summary> /// <param name="model">實體類</param> /// <param name="isaddedContext">是否物理刪除</param> public void DeleteEntity(TEntity model, bool isaddedContext) { if (model == null) { throw new Exception("DeleteEntity方法中的model不能為null"); } //如果僅僅是邏輯刪除的話,那我們只要調用編輯方法將標識為邏輯刪除的那個欄位修改為true就可以了。 if (isaddedContext == true) { this._dbset.Attach(model); } this._dbset.Remove(model); } #endregion #region 查尋 /// <summary> /// 普通帶條件查詢 /// </summary> /// <param name="where"></param> /// <returns></returns> public IQueryable<TEntity> QueryWhere(Expression<Func<TEntity, bool>> where) { return this._dbset.Where(where); } /// <summary> /// 連表查詢 /// </summary> /// <param name="where">連表查詢的條件篩選查詢</param> /// <param name="tablesName">要做連表查詢的所有表名集合</param> /// <returns></returns> public IQueryable<TEntity> QueryJoin(Expression<Func<TEntity, bool>> where, string[] tablesName) { if (tablesName == null || tablesName.Any() == false) { throw new Exception("連表查詢最少也要一個表,所有QueryJoin方法中tablesName中最少也需要有一個表名"); } DbQuery<TEntity> query = this._dbset; foreach (string tableName in tablesName) { //不斷的連表,直到把tablesName里的所有表都連完 query = query.Include(tableName); } return query.Where(where); //然後對連表進行條件篩選查詢 } /// <summary> /// 帶條件的分頁查詢 /// </summary> /// <typeparam name="TKey">按哪個欄位進行排序</typeparam> /// <param name="pageindex">當前頁</param> /// <param name="pagesize">頁大小</param> /// <param name="rowCount">數據總條數</param> /// <param name="order">排序</param> /// <param name="where">篩選條件</param> /// <returns></returns> public IQueryable<TEntity> QueryByPage<TKey>(int pageindex, int pagesize, out int rowCount, Expression<Func<TEntity, TKey>> order, Expression<Func<TEntity, bool>> where) { //獲取總條數 rowCount = this._dbset.Count(where); //建議將這個Where條件語句放在前面,如果你放到後面,分頁的時候可能存在問題。 return this._dbset.Where(where).OrderByDescending(order).Skip((pageindex - 1) * pagesize).Take(pagesize); } /// <summary> /// 調用存儲過程或執行SQL語句(但是我們不推薦執行sql語句) /// </summary> /// <typeparam name="TElement"> /// 因為存儲過程返回的數據不一定就是TEntity這個實體,因為存儲過返回的結果集有可能是自己拼接出來的,所以這個方法的返回結果 /// 為Lsit<TEntity>就不合適了。 這個 TElement是在調用的存儲過程的時候傳入的一個實體,此實體必須和調用的存儲過程的返回結集 /// 中的欄位名稱保存一致(你這個存儲過程返回有多個欄位,那麼你這個實體中就應該有多少個屬性) /// </typeparam> /// <param name="sql"> /// 假設我創建了這麼一個存儲過程: /// create proc proc_T_UserInfo_Paging2(@pageSize int,@currentPage int,@CountData ) /// 那現在我們調用這個存儲過程,那麼這個SQL語句的寫法就是: /// proc_T_UserInfo_Paging2 @pageSize int,@currentPage int,@CountData /// /// </param> /// <param name="prms">參數化查詢的參數數組</param> /// <returns></returns> public List<TElement> RunProc<TElement>(string sql, params object[] prms) { return db.Database.SqlQuery<TElement>(sql, prms).ToList(); } #endregion #region 編輯 /// <summary> /// 編輯 /// </summary> /// <param name="model">實體</param> /// <param name="propertyNames">要編輯的的所有屬性名稱序列</param> public void EditEntity(TEntity model, string[] propertyNames) { if (model == null) { throw new Exception("EditEntity方法中的參數model不能為null"); } if (propertyNames.Any() == false || propertyNames == null) { throw new Exception("EditEntity方法中的參數propertyNames最少需要一個屬性"); } System.Data.Entity.Infrastructure.DbEntityEntry entry = db.Entry(model); entry.State = System.Data.EntityState.Unchanged; foreach (string item in propertyNames) { entry.Property(item).IsModified = true; } db.Configuration.ValidateOnSaveEnabled = false; } #endregion #region 統一保存 public int SaveChanges() { return db.SaveChanges(); } #endregion } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace FB.CMS.Site.Controllers { using FB.CMS.IRepository; public class HomeController : Controller { IsysFunctionRepository fundal; IsysKeyValueRepository kvdal; //我們在通過AutoFac將IsysFunctionRepository和IsysKeyValueRepository的兩個實現類的對象實例註入到當前構造函數中 //那麼在註入IsysFunctionRepository這實現類的對象的時候就會執行到sysFunctionRepository類,而這個sysFunctionRepository類 //又是繼承了BaseDal類,然後就會去執行BaseDal類的構造函數,而在BaseDal這個類的構造函數中我們執行了 //this._dbset = db.Set<TEntity>()這段代碼,這段代碼中使用了db這個上下文容器類對象。既然使用了這個對象,所以就會首先初始化這個對象 //按照平常的寫法,一般都是直接new一個上下文容器對象,即:BaseDbContext db = new BaseDbContext(); //然後當我們再對構造函數的參數IsysKeyValueRepository kvdal進行對象註入的時候,同樣的原理又會執行一次 //BaseDbContext db = new BaseDbContext();即又創建了一個上下文容器類對象。所以說當我們通過構造函數一次性註入多個對象的時候 //會創建多個上下文容器類對象。所以這樣的做法是非常不好的。因為我們如果在同一個線程裡面用兩個上下文容器類對象分別去對數據增刪改 //的後,【最後統一執行db.SaveChanges()】;這時候會發現只有後面的那個上下文容器類對象對數據的增刪改操作成功,而前面的那個上下文容器類對象對數據的增刪 public HomeController(IsysFunctionRepository fundal, IsysKeyValueRepository kvdal) { this.fundal = fundal; this.kvdal = kvdal; } public ActionResult Index() { //var model = fundal.QueryWhere(r => r.fID == 20).FirstOrDefault(); //model.fName = "預設888"; //fundal.SaveChanges(); //執行保存修改其實就已經操作一次資料庫了 //var model2 = kvdal.QueryWhere(r => r.KID == 3).FirstOrDefault(); //model2.KName = "公司88"; //fundal.SaveChanges(); //這裡又執行一次保存修改操作,又執行了一個資料庫, //那我們就希望在同一個控制器中(同一個控制中意味著在同一個線程當中)不管操作多少個上下文容器類對象對數據的操作 //我們只希望最後只執行已給SaveChanges(),這樣就能保障最少次的對資料庫的操作。從而提高性能 //那我們怎麼做呢?其實很簡單,當我們執行構造函數對多個對象進行註入的時候,我們保證只有一個上下文容器類對象實例 //當我們在Home控制器中通過構造函數註入兩個對象,那麼這個Home控制器會有一個線程進行管理,那麼當這個線程過來的時候 //我們就創建先去一個線程緩存中去獲取一下這個線程名對應的緩存,如果緩存不存在,那麼我們就創建一個上下文容器類對象實例 //並將這個對象實例存儲到線程緩存當中,然後返回這個上下文容器類對象。如果緩存存在,那麼就直接獲取這個緩存進行返回 //這樣就保證了同一線程下,只有個上下文容器類實例對象,這樣我們就可以用任何上下文容器類對象對數據的增刪改後,最後只 //需要用任何一個上下文容器類對象執行一次SaveChanges()就可以了 var model = fundal.QueryWhere(r => r.fID == 20).FirstOrDefault(); model.fName = "預設888"; //fundal.SaveChanges(); //執行保存修改其實就已經操作一次資料庫了 var model2 = kvdal.QueryWhere(r => r.KID == 3).FirstOrDefault(); model2.KName = "公司88"; fundal.SaveChanges(); //通過對BaseDal中對創建上下文容器類的優化後,我們看到在同一個線程中 兩個上下文容器類對象(通過優化後其實這個兩個上下文容器類對象其實是同一個上下文容器類對象)
同時對數據進行改的操作,最後只執行了一個fundal.SaveChanges(); 方法,就將數據成功保存了。這樣就達到了最少操作資料庫。性能提升了 return View(model); } } }
public ActionResult Index() { ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<UserInfoSevices>(); //想拿到UserInfoSevices類的實例 builder.RegisterType<UserInfoRepository>().As<IUserInfoRepository>(); //與之關聯的UserInfoRepository類也需要拿到,並轉化成介面的形式保存 var aa = builder.Build().Resolve<UserInfoSevices>().QueryModel(r => r.Age > 0); //在這裡使用 return View(); }