關於Entity framework和Ninject是什麼,此處省略一萬個字。
這裏記錄下傳統代碼架構和使用Ioc工具後的松耦合架構:
以一個簡單的示例說明:
新建一個空解決方案,添加MVC項目(這裏用的是MVC4),爲了方便演示,直接選擇Internet應用程序:
再添加業務邏輯層(BLL)和數據訪問層(DAL)兩個類庫項目(也可以放在MVC的model下,因爲MVC的model層就類似三層架構中的BLL+DAL+Model) 如下:
然後在數據庫裏添加一個persons表,隨便手動輸入幾條測試數據:
在DAL項目下新建一個文件夾,加入一個實體數據庫模型edmx文件,把上面的表加進來(這裏爲了方便演示,就不用code first了):
修改HomeController中Index方法對應的視圖文件爲:
@model IEnumerable<DAL.EF_Model.PersonInfo>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Age)
</th>
<th>
@Html.DisplayNameFor(model => model.Sex)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Age)
</td>
<td>
@Html.DisplayFor(modelItem => item.Sex)
</td>
</tr>
}
</table>
然後,要做一個簡單的功能,就是查出滿足條件的人員名單,一般有如下幾種做法:
一.傳統做法:
在BLL中加入代碼:
public class PersonBL<T> where T:class,new()
{
public IEnumerable<T> GetPersons(Expression<Func<T,bool>> perdicate)
{
return new PersonDA<T>().GetPersons(perdicate);
}
}
在DAL中加入代碼: public class PersonDA<T> where T:class,new()
{
private MyDbEntities dbContext = new MyDbEntities();
public IEnumerable<T> GetPersons(System.Linq.Expressions.Expression<Func<T, bool>> perdicate)
{
return dbContext.Set<T>().Where(perdicate).Select(p => p).ToList();
}
}
最後在HomeController修改Index方法爲:
public ActionResult Index()
{
IEnumerable<PersonInfo> table = new PersonBL<PersonInfo>().GetPersons(person => person.Sex == "男");
return View(table);
}
最後運行結果:
這種做法實現了controller層和BL,DA層的解耦,但是並沒有面向接口,也就是說所有的數據庫操作都在DA層進行。關於面向接口編程的好處,直接百度,此處省略一萬字。
二.第二種面向接口做法:
在解決方案下新增一個接口類庫項目,並新增一個公共接口文件IRepository(名字隨便取,最好以i開頭表示interface):
接口代碼爲(這裏只實現了部分增刪改查,針對不同情況可自行補充):
public interface IRepository<T> where T : class,new()
{
//增C
void Create(T entity);
void Create(IEnumerable<T> entities);
//查R
T Get(Expression<Func<T, bool>> predicate);
IEnumerable<T> Fetch(Expression<Func<T, bool>> predicate);
//改U
void Update(T entity);
void Update(IEnumerable<T> entities);
//刪D
void Delete(T entity);
void Delete(IEnumerable<T> entities);
void Delete(Expression<Func<T, bool>> predicate);
}
然後新增一個繼承該接口的person接口:
public interface IPerson<T>:IRepository<T> where T:class,new()
{
//TODO
//此處添加其它接口方法
}
最後在BLL下新增一個繼承IPerson接口的實現類PersonManager1:
public class PersonManager1<T> : IPerson<T> where T : class,new()
{
private MyDbEntities dbContext = new MyDbEntities();
public PersonManager1()
{
//dbContext.Database.Connection.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["mycon"].ConnectionString;
}
private DbSet<T> DbTable
{
get
{
return dbContext.Set<T>();
}
}
#region IRepository<T> 成員
#region 常規CRUD
public void Create(T entity)
{
DbTable.Add(entity);
dbContext.SaveChanges();
}
public void Create(IEnumerable<T> entities)
{
if (entities.ToList().Count > 0)
{
DbTable.AddRange(entities);
dbContext.SaveChanges();
}
}
public T Get(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
return Fetch(predicate).FirstOrDefault();
}
public IEnumerable<T> Fetch(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
return DbTable.Where(predicate);
}
public void Update(T entity)
{
DbTable.Attach(entity);
dbContext.Entry(entity).State = EntityState.Modified;
dbContext.SaveChanges();
}
public void Update(IEnumerable<T> entities)
{
foreach (var item in entities)
{
DbTable.Attach(item);
dbContext.Entry(item).State = EntityState.Modified;
}
dbContext.SaveChanges();
}
public void Delete(T entity)
{
DbTable.Attach(entity);
dbContext.Entry(entity).State = EntityState.Deleted;
dbContext.SaveChanges();
}
public void Delete(IEnumerable<T> entities)
{
foreach (var item in entities)
{
DbTable.Attach(item);
dbContext.Entry(item).State = EntityState.Deleted;
}
dbContext.SaveChanges();
}
public void Delete(Expression<Func<T, bool>> predicate)
{
var delEntities = Fetch(predicate).ToList();//讓其立即查詢
foreach (var item in delEntities)
{
Delete(item);
}
}
#endregion
#endregion
}
最後將HomerController的Index方法修爲:
public ActionResult Index()
{
//IEnumerable<Persons> table = new PersonBL<Persons>().GetPersons(person => person.Sex == "男");
IPerson<Persons> persons = new PersonManager1<Persons>();
IEnumerable<Persons> table=persons.Fetch(person => person.Sex == "女");
return View(table);
}
查詢結果:
這種做法實現了面向接口方式,但是帶來一個問題就是和Controller層耦合了,也就是說如果以後我修改了PersonManager1類,那麼相應的Controller層也要對應修改。爲了實現和Controller層解耦,就要用到DI,也就是Ioc工具,這類工具有很多,如AutoFac,Spring.Net,這裏使用一個輕量級的名叫:Ninject
這個工具直接在NuGet下可以下載:
安裝到當前項目下,新建一個Factory項目,並增加一個NInjectFactory類:
Ninject可以從構造函數注入或者直接綁定:
1)構造函數注入:
//構造函數注入(不支持Get取得綁定的類型,不推薦)
public class NinjectControlFactory : DefaultControllerFactory
{
private IKernel ninectKernel;
public NinjectControlFactory()
{
ninectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)ninectKernel.Get(controllerType);
}
private void AddBindings()
{
//ninectKernel.Bind<IProduct>().To<ProductNameA>(); //不帶泛型約束注入
ninectKernel.Bind(typeof(IPerson<>)).To(typeof(PersonManager1<>));////帶泛型約束注入
}
}
還需要註冊綁定: protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//構造函數注入(不推薦)
ControllerBuilder.Current.SetControllerFactory(new NinjectControlFactory());
}
修改Controller中的方法:
private IPerson<Persons> _iPerson;
public HomeController(IPerson<Persons> iPerson)
{
_iPerson = iPerson;
}
public ActionResult Index()
{
//IEnumerable<Persons> table = new PersonBL<Persons>().GetPersons(person => person.Sex == "男");
//IPerson<Persons> persons = new PersonManager1<Persons>();
//IEnumerable<Persons> table=persons.Fetch(person => person.Sex == "女");
IEnumerable<Persons> table = _iPerson.Fetch(person=>person.Age>=40);
return View(table);
}
最後直接運行即可:
但是這種方式我不太喜歡,破壞構造函數,而且不能動態Get到具體類型,不是很好,下面使用另一種方式。
2)重寫NinjectModule的Load方式:
//屬性、字段、參數式注入(支持Get取得綁定的類型)
public class MyBindModule : NinjectModule
{
public override void Load()
{
Bind(typeof(IPerson<>)).To(typeof(PersonManager1<>));//帶泛型約束注入
}
}
然後在controller中:
private IPerson<Persons> _iPerson;
public HomeController()
{
IKernel kernal = new StandardKernel(new MyBindModule());
_iPerson = kernal.Get<IPerson<Persons>>();
}
public ActionResult Index()
{
//IEnumerable<Persons> table = new PersonBL<Persons>().GetPersons(person => person.Sex == "男");
//IPerson<Persons> persons = new PersonManager1<Persons>();
//IEnumerable<Persons> table=persons.Fetch(person => person.Sex == "女");
IEnumerable<Persons> table = _iPerson.Fetch(person=>person.Age<=30);
return View(table);
}
結果:
Bind(typeof(IPerson<>)).To(typeof(PersonManager2<>));
即可,不需要求修改Controller層的任何代碼。