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(); }