ASP.NET MVC 与 Unity (依赖注入)






4.54/5 (20投票s)
如何在真实世界应用程序中将 Unity 容器用于支持依赖注入。
引言
如何使用 Unity 容器为实际应用添加对依赖注入的支持,以及如何使用 Unity 容器注册类型,在运行时解析类型,以及管理已解析对象的生命周期。除了了解 Unity 容器如何能够在启动时构建应用程序的对象图,您还可以了解这种方法如何促进设计和运行单元测试。
背景
我从以下文章中引用了有关 Unity 的信息:http://unity.codeplex.com
Unity 应用程序块 (Unity) 是一个轻量级可扩展的依赖注入容器,支持构造函数、属性和方法调用注入。
Unity 解决了从事基于组件的软件工程的开发人员所面临的问题。现代业务应用程序由自定义业务对象和组件组成,这些组件在应用程序中执行特定或通用的任务,此外还有单独解决诸如日志记录、身份验证、授权、缓存和异常处理等横切关注点的组件。
成功构建此类应用程序的关键是实现解耦或非常松散耦合的设计。松散耦合的应用程序更灵活,更容易维护。它们也更容易在开发过程中进行测试。您可以模拟具有强大具体依赖项的对象的 shim(轻量级模拟实现);例如数据库连接、网络连接、ERP 连接和丰富的用户界面组件。
依赖注入是构建松散耦合应用程序的主要技术。它提供了处理对象之间依赖关系的方法。例如,处理客户信息的对象可能依赖于访问数据存储、验证信息以及检查用户是否有权执行更新的其他对象。依赖注入技术可以确保客户类正确地实例化并填充所有这些对象,尤其是在依赖关系可能抽象的情况下。
使用代码 (EF with CODE FIRST)
0. 准备
在 Visual Studio 2012 或 2013 中使用现有模板创建一个 ASP.NET MVC 项目。
安装包
- EntityFramework (https://nuget.net.cn/packages/EntityFramework/6.1.0)
- Unity (https://nuget.net.cn/packages/Unity/3.5.1404)
- Unity.Mvc5 (https://nuget.net.cn/packages/Unity.Mvc5/)
1. 在数据库中创建表
首先,我们在数据库中创建一个名为 Articles
的表,如下所示
CREATE TABLE [dbo].[Articles]
(
[Id] [uniqueidentifier] NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[Summary] [nvarchar](500) NULL,
[ArticleContent] [nvarchar](max) NULL,
[ViewCount] [int] NULL,
[CreatedDate] [datetime] NULL,
[CreatedByUsername] [varchar](50) NULL,
[Tags] [nvarchar](500) NULL,
CONSTRAINT [PK_Articles] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
)
2. 在 ASP.NET MVC 项目的 Models 文件夹中创建一个 Article 类
Article
的类(在 ASP.NET MVC 项目的 Models 文件夹中),对应于数据库中的 Articles
表,如下所示using System;
namespace UnityTutorials.Models
{
public class Article
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string ArticleContent { get; set; }
public int ViewCount { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedByUsername { get; set; }
public string Tags { get; set; }
}
}
3. 在 ASP.NET MVC 项目的 Models 文件夹中创建一个 ArticleMapping 类
第三,我们创建一个名为 ArticleMapping
的类(在 ASP.NET MVC 项目的 Models 文件夹中),如下所示
using System.Data.Entity.ModelConfiguration;
namespace UnityTutorials.Models
{
public class ArticleMapping : EntityTypeConfiguration<Article>
{
public ArticleMapping()
{
this.HasKey(x => x.Id);
this.ToTable("Articles"); // map to Articles table in database
}
}
}
4. 在 ASP.NET MVC 项目的 Models 文件夹中创建一个接口 IWebDbContext 和一个实现 IWebDbContext 接口的类 WebDbContext
using System;
using System.Data.Entity;
namespace UnityTutorials.Models
{
public interface IWebDbContext : IDisposable
{
}
public class WebDbContext : DbContext, IWebDbContext
{
public DbSet<Article> Articles { get; set; }
public WebDbContext()
{
Configuration.LazyLoadingEnabled = true;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Mappings
modelBuilder.Configurations.Add(new ArticleMapping());
base.OnModelCreating(modelBuilder);
}
public new void Dispose()
{
}
}
}
5. 在 ASP.NET MVC 项目的 Models 文件夹中创建一个 ArticleRepository 类
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace UnityTutorials.Models
{
public interface IArticleRepository
{
IEnumerable<Article> GetAll();
Article Add(Article item);
void Update(Article item);
void Delete(Article item);
Article Get(Guid id);
}
public class ArticleRepository : IArticleRepository
{
private readonly WebDbContext _context;
public ArticleRepository(IWebDbContext context)
{
this._context = context as WebDbContext;
}
public Article Get(Guid id)
{
return _context.Articles.FirstOrDefault(x => x.Id == id);
}
public IEnumerable<Article> GetAll()
{
return _context.Articles;
}
public Article Add(Article item)
{
this._context.Articles.Add(item);
return item;
}
public void Update(Article item)
{
// Check there's not an object with same identifier already in context
if (_context.Articles.Local.Select(x => x.Id == item.Id).Any())
{
throw new ApplicationException("Object already exists in context");
}
_context.Entry(item).State = EntityState.Modified;
}
public void Delete(Article item)
{
this._context.Articles.Remove(item);
}
}
}
6. 创建一个 UnitOfWork 类来管理事务
使用 Entity Framework,大多数时候 SaveChanges()
就足够了。这将创建一个事务,或加入任何环境事务,并在该事务中完成所有必要的工作。
using System;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
namespace UnityTutorials.Models
{
public partial interface IUnitOfWork : IDisposable
{
void Commit();
}
public class UnitOfWork : IUnitOfWork
{
private readonly WebDbContext _context;
private readonly IDbTransaction _transaction;
private readonly ObjectContext _objectContext;
public UnitOfWork(IWebDbContext context)
{
this._context = context as WebDbContext ?? new WebDbContext();
this._objectContext = ((IObjectContextAdapter)this._context).ObjectContext;
if (this._objectContext.Connection.State != ConnectionState.Open)
{
this._objectContext.Connection.Open();
this._transaction = _objectContext.Connection.BeginTransaction();
}
}
public void Commit()
{
try
{
this._context.SaveChanges();
this._transaction.Commit();
}
catch (Exception)
{
Rollback();
throw;
}
}
private void Rollback()
{
this._transaction.Rollback();
foreach (var entry in this._context.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Deleted:
entry.State = EntityState.Unchanged;
break;
}
}
}
public void Dispose()
{
if (this._objectContext.Connection.State == ConnectionState.Open)
{
this._objectContext.Connection.Close();
}
}
}
}
7. 创建一个 UnitOfWorkManage 类来管理 UnitOfWork
using System;
using System.Data.Entity;
namespace UnityTutorials.Models
{
public interface IUnitOfWorkManager : IDisposable
{
IUnitOfWork NewUnitOfWork();
}
public class UnitOfWorkManager : IUnitOfWorkManager
{
private bool _isDisposed;
private readonly WebDbContext _context;
public UnitOfWorkManager(IWebDbContext context)
{
Database.SetInitializer<WebDbContext>(null);
this._context = context as WebDbContext;
}
public IUnitOfWork NewUnitOfWork()
{
return new UnitOfWork(this._context);
}
public void Dispose()
{
if (this._isDisposed == false)
{
this._context.Dispose();
this._isDisposed = true;
}
}
}
}
8. 现在,测试我们的 Unity
步骤 1:创建一个名为 BaseController 的类
using System.Web.Mvc;
using UnityTutorials.Models;
namespace UnityTutorials.Controllers
{
public class BaseController : Controller
{
protected readonly IUnitOfWorkManager UnitOfWorkManager;
// GET: Base
public BaseController(IUnitOfWorkManager unitOfWorkManager)
{
UnitOfWorkManager = unitOfWorkManager;
}
}
}
步骤 2:创建一个名为 ArticleController 的类
using System;
using System.Web.Mvc;
using UnityTutorials.Models;
namespace UnityTutorials.Controllers
{
public class ArticleController : BaseController
{
private readonly IArticleRepository _articleRepository;
public ArticleController(IUnitOfWorkManager unitOfWorkManager, IArticleRepository articleRepository)
: base(unitOfWorkManager)
{
this._articleRepository = articleRepository;
}
public ActionResult Test()
{
// Create a article
using (var unitOfWork = this.UnitOfWorkManager.NewUnitOfWork())
{
this._articleRepository.Add(new Article()
{
Id = Guid.NewGuid(),
Title = "Title Text",
Summary = "Summary Text",
ArticleContent = "Html content",
ViewCount = 0,
CreatedDate = DateTime.Now,
CreatedByUsername = "admin",
Tags = "mvc, unity, asp.net"
});
unitOfWork.Commit();
}
return View();
}
}
}
9. 创建一个名为 UnityMvc5 的类以将 Unity 注册到 ASP.NET MVC
注意:请记住安装 Unity.Mvc5,使用 Visual Studio 中的 Nuget 进行安装:Install-Package Unity.Mvc5
您可以参考:https://nuget.net.cn/packages/Unity.Mvc5/
using System.Web.Mvc;
using Microsoft.Practices.Unity;
using Unity.Mvc5;
namespace UnityTutorials.Models
{
public static class IocExtensions
{
public static void BindInRequestScope<T1, T2>(this IUnityContainer container) where T2 : T1
{
container.RegisterType<T1, T2>(new HierarchicalLifetimeManager());
}
public static void BindInSingletonScope<T1, T2>(this IUnityContainer container) where T2 : T1
{
container.RegisterType<T1, T2>(new ContainerControlledLifetimeManager());
}
}
public class UnityMvc5
{
public static void Start()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// Database context, one per request, ensure it is disposed
container.BindInRequestScope<IWebDbContext, WebDbContext>();
//Bind the various domain model services and repositories that e.g. our controllers require
container.BindInRequestScope<IUnitOfWorkManager, UnitOfWorkManager>();
container.BindInRequestScope<IArticleRepository, ArticleRepository>();
//container.BindInRequestScope<ISessionHelper, SessionHelper>();
return container;
}
}
}
10. 在 Global.asax 的 Application_Start 事件中启动 Unity
添加行:UnityMvc5.Start();
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace UnityTutorials
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
UnityMvc5.Start(); // <----- Add this line
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
参考
以下是主要参考资料
摘要
我将时间和精力投入到我的所有文章中。请不要忘记标记您的投票、建议和反馈,以提高本文和即将发布的文章的质量。感谢您的阅读。