65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.54/5 (20投票s)

2014年6月18日

CPOL

3分钟阅读

viewsIcon

103634

downloadIcon

2045

如何在真实世界应用程序中将 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 项目。

安装包

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

参考

以下是主要参考资料

摘要

我将时间和精力投入到我的所有文章中。请不要忘记标记您的投票、建议和反馈,以提高本文和即将发布的文章的质量。感谢您的阅读。

© . All rights reserved.