zoukankan      html  css  js  c++  java
  • 从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之二autofac解耦

    上一篇 中将项目的基本骨架搭起来能正常跑通,这一篇将讲到,如何通过autofac将DbContext和model进行解耦,只用添加model,而不用在DbContext中添加DbSet。

    在这里就不详细讲autofac是干什么用的了,简单说下autofac。

    1.autofac可替换net core自带的DI IOC,用来扩展。

    2.autofac可提供Aop,具体实现在博客园有很多示例。

    3.autofac的几个生命周期用法:InstancePerDependency 每次都创建一个对象 ,SingleInstance 每次都是同一个对象,InstancePerLifetimeScope 同一个生命周期生成的对象是同一个。

      接下来,我们需要在启动项目上通过nuget安装两个Package:Autofac、Autofac.Extensions.DependencyInjection

    因为autofac是通过接口来进行注入的,因此我们需要创建对应的基层接口用来注入。在basic项目通过nuget安装Autofac.Extensions.DependencyInjection、

      然后中添加 Dependency 文件夹来存放基层接口,添加IOC容器接口:IIocManager,代码如下:

    using System;
    using Autofac;
    using Autofac.Core;
    
    namespace DemoFrame_Basic.Dependency
    {
        /// <summary>
        /// IOC容器接口
        /// </summary>
        public interface IIocManager
        {
            IContainer Container { get; }
    
            bool IsRegistered(Type serviceType, ILifetimeScope scope = null);
            object Resolve(Type type, ILifetimeScope scope = null);
            T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class;
            T Resolve<T>(params Parameter[] parameters) where T : class;
            T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null);
            object ResolveOptional(Type serviceType, ILifetimeScope scope = null);
            object ResolveUnregistered(Type type, ILifetimeScope scope = null);
            T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class;
            ILifetimeScope Scope();
            bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance);
        }
    }
    View Code-IOC容器接口

      再添加一个数据库基础接口:IEntityBase

        /// <summary>
        /// 数据库基础接口
        /// </summary>
        public interface IEntityBase
        {
        }
    View Code-数据库基础接口

      IIocManager的实现类:IocManager

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Loader;
    using Autofac;
    using Autofac.Core;
    using Autofac.Core.Lifetime;
    using Autofac.Extensions.DependencyInjection;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyModel;
    
    namespace DemoFrame_Basic.Dependency
    {
        /// <summary>
        /// Container manager
        /// </summary>
        public class IocManager : IIocManager
        {
            private IContainer _container;
    
            public static IocManager Instance { get { return SingletonInstance; } }
            private static readonly IocManager SingletonInstance = new IocManager();
    
            /// <summary>
            /// Ioc容器初始化
            /// </summary>
            /// <param name="config"></param>
            /// <returns></returns>
            public IServiceProvider Initialize(IServiceCollection services)
            {
    
                //.InstancePerDependency()    //每次都创建一个对象
                //.SingleInstance()   //每次都是同一个对象
                //.InstancePerLifetimeScope()     //同一个生命周期生成的对象是同一个
    
                var builder = new ContainerBuilder();
                builder.RegisterInstance(Instance).As<IIocManager>().SingleInstance();
                //所有程序集 和程序集下类型
                var deps = DependencyContext.Default;
                var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包
                var listAllType = new List<Type>();
                foreach (var lib in libs)
                {
                    try
                    {
                        var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
                        listAllType.AddRange(assembly.GetTypes().Where(type => type != null));
                    }
                    catch { }
                }
    
                //注册IEntityBase实现类
                var entityBaseType = typeof(IEntityBase);
                var arrEntityBaseType = listAllType.Where(t => entityBaseType.IsAssignableFrom(t) && t != entityBaseType).ToArray();
                builder.RegisterTypes(arrEntityBaseType)
                    .AsImplementedInterfaces()
                    .SingleInstance()
                    .PropertiesAutowired();
    
                foreach (var type in arrEntityBaseType)
                {
                    if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object))
                    {
                        builder.RegisterType(type).As(type.BaseType)
                            .SingleInstance()
                            .PropertiesAutowired();
                    }
                }
    
    
                //注册controller实现类 让Controller能被找到
                var controller = typeof(ControllerBase);
                var arrcontrollerType = listAllType.Where(t => controller.IsAssignableFrom(t) && t != controller).ToArray();
                builder.RegisterTypes(arrcontrollerType)
                    .AsImplementedInterfaces()
                    .SingleInstance()
                    .PropertiesAutowired();
    
                foreach (var type in arrcontrollerType)
                {
                    if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object))
                    {
                        builder.RegisterType(type).AsSelf();
                    }
                }
    
                builder.Populate(services);
                _container = builder.Build();
                return new AutofacServiceProvider(_container);
            }
    
            /// <summary>
            /// Gets a container
            /// </summary>
            public virtual IContainer Container
            {
                get
                {
                    return _container;
                }
            }
    
            /// <summary>
            /// Resolve
            /// </summary>
            /// <typeparam name="T">Type</typeparam>
            /// <param name="key">key</param>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <returns>Resolved service</returns>
            public virtual T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class
            {
                if (scope == null)
                {
                    //no scope specified
                    scope = Scope();
                }
                if (string.IsNullOrEmpty(key))
                {
                    return scope.Resolve<T>();
                }
                return scope.ResolveKeyed<T>(key);
            }
    
            /// <summary>
            /// Resolve
            /// </summary>
            /// <typeparam name="T">Type</typeparam>
            /// <param name="key">key</param>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <returns>Resolved service</returns>
            public virtual T Resolve<T>(params Parameter[] parameters) where T : class
            {
                var scope = Scope();
                return scope.Resolve<T>(parameters);
            }
    
            /// <summary>
            /// Resolve
            /// </summary>
            /// <param name="type">Type</param>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <returns>Resolved service</returns>
            public virtual object Resolve(Type type, ILifetimeScope scope = null)
            {
                if (scope == null)
                {
                    //no scope specified
                    scope = Scope();
                }
                return scope.Resolve(type);
            }
    
            /// <summary>
            /// Resolve all
            /// </summary>
            /// <typeparam name="T">Type</typeparam>
            /// <param name="key">key</param>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <returns>Resolved services</returns>
            public virtual T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null)
            {
                if (scope == null)
                {
                    //no scope specified
                    scope = Scope();
                }
                if (string.IsNullOrEmpty(key))
                {
                    return scope.Resolve<IEnumerable<T>>().ToArray();
                }
                return scope.ResolveKeyed<IEnumerable<T>>(key).ToArray();
            }
    
            /// <summary>
            /// Resolve unregistered service
            /// </summary>
            /// <typeparam name="T">Type</typeparam>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <returns>Resolved service</returns>
            public virtual T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class
            {
                return ResolveUnregistered(typeof(T), scope) as T;
            }
    
            /// <summary>
            /// Resolve unregistered service
            /// </summary>
            /// <param name="type">Type</param>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <returns>Resolved service</returns>
            public virtual object ResolveUnregistered(Type type, ILifetimeScope scope = null)
            {
                if (scope == null)
                {
                    //no scope specified
                    scope = Scope();
                }
                var constructors = type.GetConstructors();
                foreach (var constructor in constructors)
                {
                    try
                    {
                        var parameters = constructor.GetParameters();
                        var parameterInstances = new List<object>();
                        foreach (var parameter in parameters)
                        {
                            var service = Resolve(parameter.ParameterType, scope);
                            if (service == null) throw new Exception("Unknown dependency");
                            parameterInstances.Add(service);
                        }
                        return Activator.CreateInstance(type, parameterInstances.ToArray());
                    }
                    catch (Exception)
                    {
    
                    }
                }
                throw new Exception("No constructor  was found that had all the dependencies satisfied.");
            }
    
            /// <summary>
            /// Try to resolve srevice
            /// </summary>
            /// <param name="serviceType">Type</param>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <param name="instance">Resolved service</param>
            /// <returns>Value indicating whether service has been successfully resolved</returns>
            public virtual bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance)
            {
                if (scope == null)
                {
                    //no scope specified
                    scope = Scope();
                }
                return scope.TryResolve(serviceType, out instance);
            }
    
            /// <summary>
            /// Check whether some service is registered (can be resolved)
            /// </summary>
            /// <param name="serviceType">Type</param>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <returns>Result</returns>
            public virtual bool IsRegistered(Type serviceType, ILifetimeScope scope = null)
            {
                if (scope == null)
                {
                    //no scope specified
                    scope = Scope();
                }
                return scope.IsRegistered(serviceType);
            }
    
            /// <summary>
            /// Resolve optional
            /// </summary>
            /// <param name="serviceType">Type</param>
            /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
            /// <returns>Resolved service</returns>
            public virtual object ResolveOptional(Type serviceType, ILifetimeScope scope = null)
            {
                if (scope == null)
                {
                    //no scope specified
                    scope = Scope();
                }
                return scope.ResolveOptional(serviceType);
            }
    
            /// <summary>
            /// Get current scope
            /// </summary>
            /// <returns>Scope</returns>
            public virtual ILifetimeScope Scope()
            {
                try
                {
                    //when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)
                    return Container.BeginLifetimeScope();
                }
                catch (Exception)
                {
                    //we can get an exception here if RequestLifetimeScope is already disposed
                    //for example, requested in or after "Application_EndRequest" handler
                    //but note that usually it should never happen
    
                    //when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)
                    return Container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
                }
            }
        }
    }
    View Code-Container manager

      在这里添加完以后,我们需要将自带的DI容器给替换成现在使用的autofac,

      在启动项目的Startup文件中更改,最终代码如下:

            public IServiceProvider ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
                services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
                services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
    
                services.AddDbContext<DemoDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SqlServerConnection")));
    
                return IocManager.Instance.Initialize(services);
            }
    View Code-ConfigureServices

      为了方便使用,在CoreMvc项目中添加DemoWeb的类来存放一些系统数据:

    using DemoFrame_Basic.Dependency;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Caching.Memory;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Hosting;
    using System.Linq;
    
    namespace DemoFrame_CoreMvc
    {
        public class DemoWeb
        {
            private static IHttpContextAccessor _httpContextAccessor;
    
            /// <summary>
            /// Configure
            /// </summary>
            /// <param name="httpContextAccessor"></param>
            public static void Configure(IHttpContextAccessor httpContextAccessor)
            {
                _httpContextAccessor = httpContextAccessor;
            }
    
            /// <summary>
            /// 当前请求HttpContext
            /// </summary>
            public static HttpContext HttpContext
            {
                get => _httpContextAccessor.HttpContext;
                set => _httpContextAccessor.HttpContext = value;
            }
    
    
            /// <summary>
            /// IocManager
            /// </summary>
            public static IIocManager IocManager { get; set; }
    
            /// <summary>
            /// Environment
            /// </summary>
            public static IHostingEnvironment Environment { get; set; }
    
            /// <summary>
            /// Configuration
            /// </summary>
            public static IConfiguration Configuration { get; set; }
    
            /// <summary>
            /// MemoryCache
            /// </summary>
            public static IMemoryCache MemoryCache { get; set; }
    
            /// <summary>
            /// 获取当前请求客户端IP
            /// </summary>
            /// <returns></returns>
            public static string GetClientIp()
            {
                var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault()?.Split(',')[0].Trim();
                if (string.IsNullOrEmpty(ip))
                {
                    ip = HttpContext.Connection.RemoteIpAddress.ToString();
                }
                return ip;
            }
        }
    }
    View Code

      Startup的完整代码如下:

    public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                DemoWeb.Configuration = configuration;
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public IServiceProvider ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
                services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
                services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
    
                services.AddDbContext<DemoDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SqlServerConnection")));
    
                return IocManager.Instance.Initialize(services);
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                DemoWeb.IocManager = app.ApplicationServices.GetService<IIocManager>();
                DemoWeb.Environment = env;
                try//注意这里在本地开发允许时会重置数据库,并清空所有数据,如不需要请注释
                {
                    if (env.IsDevelopment())
                    {
                        using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
                            .CreateScope())
                        {
                            var dbContent = serviceScope.ServiceProvider.GetService<DemoDbContext>();
                            //CheckMigrations(dbContent);
                            var database = serviceScope.ServiceProvider.GetService<DemoDbContext>().Database;
                            database.EnsureDeleted();
                            database.EnsureCreated();
                        }
                    }
                }
                catch (Exception ex)
                {
                    //LogHelper.Logger.Error(ex, "Failed to migrate or seed database");
                }
                DemoWeb.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    app.UseHsts();
                }
                app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());//允许跨域
                app.UseHttpsRedirection();
                app.UseMvc();
            }
    
        }
    View Code-Startup

      在这么多的配置都完成了的情况下,我们该去实现mode与DbContext的解耦操作了。那么该如何做呢?

      废话不多说了,直接上代码,在数据库上下文DemoDbContext中将之前的DbSet删掉,更改如下:

      !!!先将之前的model都继承 IEntityBase 接口。这样在模型生成时才能生成到数据库!!!

        /// <summary>
        /// 数据库上下文
        /// </summary>
        public class DemoDbContext : DbContext
        {
            public DemoDbContext(DbContextOptions<DemoDbContext> options)
         : base(options)
            { }
    
            #region IOC得到所有实体
            private readonly IEntityBase[] _entitys = DemoWeb.IocManager.ResolveAll<IEntityBase>();
            #endregion
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                if (_entitys == null)
                {
                    return;
                }
                foreach (var entity in _entitys)
                {
                    modelBuilder.Model.AddEntityType(entity.GetType());
                }
            }
        }
    View Code-数据库上下文

      在使用数据库上下文是,使用Set<T>方法设置需要使用的的类

      在下一篇中将介绍如何使用基类controller来统一前后端交互数据,统一使用一个模型进行返回。

      有需要源码的可通过此 GitHub 链接拉取 觉得还可以的给个 start 哦,谢谢!

  • 相关阅读:
    洛谷P1306 斐波那契公约数
    Codevs 1688 求逆序对(权值线段树)
    poj1006 Biorhythms
    2017-9-2 NOIP模拟赛
    洛谷P1633 二进制
    洛谷P2513 [HAOI2009]逆序对数列
    洛谷P2687 [USACO4.3]逢低吸纳Buy Low, Buy Lower
    洛谷P2285 [HNOI2004]打鼹鼠
    2017-8-31 NOIP模拟赛
    洛谷P2134 百日旅行
  • 原文地址:https://www.cnblogs.com/levywang/p/coreframe_2.html
Copyright © 2011-2022 走看看