从零开始写C# MVC框架之--- 用autofac ioc 容器实现依赖注入

来源:互联网 发布:网络重口君 编辑:程序博客网 时间:2024/05/20 13:19

本章查找了一篇对依赖注入解释得非常不错的文章为基础,再加上自己的理解,不然还真不好用语言的方式表达清楚,引用下面这位仁兄的文章

CodingLabs 张洋 --- 依赖注入那些事儿

依赖注入产生的背景:
        随着面向对象分析与设计的发展,一个良好的设计,核心原则之一就是将变化隔离,使得变化部分发生变化时,不变部分不受影响。为了做到这一点,要利用面向对象中的多态性,使用多态性后,客户类不再直接依赖服务类,而是依赖于一个抽象的接口,这样,客户类就不能在内部直接实例化具体的服务类。但是,客户类在运作中又客观需要具体的服务类提供服务,因为接口是不能实例化去提供服务的。就产生了“客户类不准实例化具体服务类”和“客户类需要具体服务类”这样一对矛盾。为了解决这个矛盾,开发人员提出了一种模式:客户类定义一个注入点,用于服务类的注入,而客户类的客户类负责根据情况,实例化服务类,注入到客户类中,从而解决了这个矛盾。

依赖注入的正式定义:
        依赖注入(Dependency Injection),是这样一个过程:由于某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点。在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常运行。

说了这么多依赖注入的解释,那什么IOC容器,用于实现依赖注入功能的组件或框架,就是IoC Container,比如本框架中用到的 autofac。

从零开始写C# MVC框架之--- 服务接口与实现 ,文章只描述了接口和实现,重点是面向接口编程的方式,但是控制器调用接口,接口怎样去自动调用实现类,就是本章讨论的重点,只要实现autofac的注入功能,就能解决 控制器中调用接口,接口自动调用服务实现类 。

 以上是理论讲解,下面结合系统代码:新增 Zy.Utility.ServicesProvider 项目,用于实现 autofac 依赖注入功能


实现原理,插件配置和反射获取程序集,再创建接口实例。

1、添加 Autofac 、Autofac.Configuration 的引用

2、获取程序集配置信息,用于反射获取接口与服务的程序集,再用 (T)Activator.CreateInstance 方法创建服务接口实现类实例

/// <summary>    /// 实例创建代理    /// </summary>    /// <typeparam name="T"></typeparam>    public static class Instance<T>    {        /// <summary>        /// 配置文件节处理者        /// </summary>        static SectionHandler handler;        /// <summary>        /// 已手动配置的服务对应关系        /// </summary>        static Hashtable ServiceNameSpaceMapper = null;        /// <summary>        /// 已注册的类型        /// </summary>        static Dictionary<Type, Type> RegistedTypes = new Dictionary<Type, Type>();        /// <summary>        /// 初始化        /// </summary>        static Instance()        {            var reader = new ConfigurationSettingsReader("autofac");            handler = reader.SectionHandler;            GetDefaultNamespaceRegisterMapper();        }        /// <summary>        /// 检查是否已经进行了显示的配置        /// </summary>        /// <typeparam name="T">待检查的类型</typeparam>        /// <returns>如果已经在配置文件中配置返回true,否则返回false</returns>        static bool HasConfiguration<T>()        {            return handler.Components.Any(item => item.Service.Contains(typeof(T).FullName));        }        /// <summary>        /// 自动创建对应类型的实现        /// </summary>        public static T Create        {            get            {                try                {                    var hasconfiguration = HasConfiguration<T>();                    var interfaceType = typeof(T);                    if (!hasconfiguration)//配置文件中未配置,采用默认注册                    {                        if (!RegistedTypes.ContainsKey(interfaceType))                            RegisteType<T>();                    }                    var targetImplementType = RegistedTypes[interfaceType];                    return (T)Activator.CreateInstance(targetImplementType);                }                catch (Exception ex)                {                    throw new ServiceInstacnceCreateException(typeof(T).Name + "服务实例创建失败", ex);                }            }        }        /// <summary>        /// 注册T类型        /// </summary>        /// <typeparam name="T"></typeparam>        static void RegisteType<T>()        {            var interfaceType = typeof(T);            string fullName = interfaceType.FullName;            string iserviceName = interfaceType.Name;            lock (fullName)            {                if (!RegistedTypes.ContainsKey(interfaceType))                {                    string namespaceName = fullName.Substring(0, fullName.LastIndexOf('.'));//获取目标类型命名空间                    string implementClass = ServiceNameSpaceMapper[namespaceName] as string;//从配置中获取对应命名空间的实现命名空间的名称                    if (implementClass == null)                        throw new ApplicationException("未配置" + fullName + "的实现");                    string nameSpace = implementClass.Split(',')[0];//获取对应实现类的命名空间                    string assembly = implementClass.Split(',')[1];//获取对应实现类的程序集名称                    string implementName = iserviceName.Substring(1);//获取对应实现类的名称                    string className = string.Format("{0}.{1}, {2}", nameSpace, implementName, assembly);//获取对应实现类的全名                    Type implementType = Type.GetType(className);//根据实现类全名创建类型                    if (implementType == null)                        throw new ApplicationException("未找到" + className);                    RegistedTypes.Add(interfaceType, implementType);//将目标类型加入已注册类型列表中,表示已经注册过该类型的实现                }            }        }        /// <summary>        /// 获取手动配置的命名空间注册对应关系        /// </summary>        static void GetDefaultNamespaceRegisterMapper()        {            ServiceNameSpaceMapper = new Hashtable();            var config = ConfigurationManager.GetSection("serviceProvider") as ServiceProviderConfig;            foreach (var item in config.Items)            {                if (ServiceNameSpaceMapper.ContainsKey(item.Interface))                    throw new ApplicationException("配置文件中最多只能配置一个" + item.Interface + "的实现");                ServiceNameSpaceMapper.Add(item.Interface, item.NameSpace + "," + item.Assembly);            }        }    }

3、新建一个ServiceHelper类,用于调用 上面代码的 Create 方法,Web控制器使用此类。

4、在Web项目的web.config中配置,服务接口和实现的命名空间分别是:Zy.Xn.IServices 、 Zy.Xn.Services ,因此,配置代码如下:

<configSections>    <section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration" />    <section name="serviceProvider" type="Zy.Utility.ServicesProvider.ServiceProviderConfig,Zy.Utility.ServicesProvider" />    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />  </configSections>

<serviceProvider>    <item interface="Zy.Xn.IServices" assembly="Zy.Xn.Services" namespace="Zy.Xn.Services"></item>  </serviceProvider>
以上是依赖注入的原理和实现,下面是在项目中的使用,演示用户登录功能

1、用户服务接口:

public interface IUserService : IService    {        /// <summary>        /// 获取用户        /// </summary>        /// <param name="loginUserName"></param>        /// <returns></returns>        User GetUser(string loginUserName);    }

2、用户服务接口实现:

public class UserService : ServiceBase, IUserService    {        /// <summary>        /// 获取用户        /// </summary>        /// <param name="userName"></param>        /// <returns></returns>        public User GetUser(string loginUserName)        {            return context.Users.FirstOrDefault(item => item.LoginUserName == loginUserName);        }    }
3、登录功能LoginController:

/// <summary>/// 登录/// </summary>/// <param name="userName">用户名</param>/// <param name="password">密码</param>/// <returns></returns>[HttpPost]public JsonResult Login(string userName, string password){    bool flag = false;    string msg = string.Empty;    if (!string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password))    {var user = <strong>ServiceHelper.Create<IUserService>().GetUser(userName)</strong>;if (user != null){    if (user.LoginPassword == password)    {Session[CacheKeys.User_Info_Cache_Key] = user;Session.Timeout = CacheKeys.User_Info_Cache_Time;msg = "登录成功";flag = true;    }    elsemsg = "密码错误";}else    msg = "不存在该用户";    }    elsemsg = "请传入用户名和密码";    return Json(new { success = flag , msg = msg});}

OK,大功告成,以后项目中所有的接口调用都是如此,只调接口即可



0 0
原创粉丝点击