最近有个需求就是一个抽象仓储层接口方法需要SqlServer以及Oracle两种实现方式,为了灵活我在依赖注入的时候把这两种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个方法的实现,这时候就在想能不能实现动态的选择使用哪种实现呢?如果可以的话那么我只需要在配置文件中进行相应的配置即可获取到正确的实现方法的调用,这样的话岂不快哉!今天我们就来一起探讨下实现这种需求的几种实现方式吧。

作者:依乐祝

原文地址:https://www.cnblogs.com/yilezhu/p/10236163.html

代码演示

在开始实现的方式之前,我们先模拟下代码。由于真实系统的结构比较复杂,所以这里我就单独建一个类似的项目结构代码。项目如下图所示:

接下来我来详细说下上面的结果作用及代码。

  1. MultiImpDemo.I 这个项目是接口项目,里面有一个简单的接口定义ISayHello,代码如下:

        public interface ISayHello
    {
    string Talk();
    }

    很简单,就一个模拟讲话的方法。

  2. MultiImpDemo.A 这个类库项目是接口的一种实现方式,里面有一个SayHello类用来实现ISayHello接口,代码如下:

    /**
    *┌──────────────────────────────────────────────────────────────┐
    *│ 描 述:
    *│ 作 者:yilezhu
    *│ 版 本:1.0
    *│ 创建时间:2019/1/7 17:41:33
    *└──────────────────────────────────────────────────────────────┘
    *┌──────────────────────────────────────────────────────────────┐
    *│ 命名空间: MultiImpDemo.A
    *│ 类 名: SayHello
    *└──────────────────────────────────────────────────────────────┘
    */
    using MultiImpDemo.I;
    using System;
    using System.Collections.Generic;
    using System.Text; namespace MultiImpDemo.A
    {
    public class SayHello : ISayHello
    {
    public string Talk()
    {
    return "Talk from A.SayHello";
    }
    }
    }
  3. MultiImpDemo.B 这个类库项目是接口的另一种实现方式,里面也有一个SayHello类用来实现ISayHello接口,代码如下:

    /**
    *┌──────────────────────────────────────────────────────────────┐
    *│ 描 述:
    *│ 作 者:yilezhu
    *│ 版 本:1.0
    *│ 创建时间:2019/1/7 17:41:45
    *└──────────────────────────────────────────────────────────────┘
    *┌──────────────────────────────────────────────────────────────┐
    *│ 命名空间: MultiImpDemo.B
    *│ 类 名: SayHello
    *└──────────────────────────────────────────────────────────────┘
    */
    using MultiImpDemo.I;
    using System;
    using System.Collections.Generic;
    using System.Text; namespace MultiImpDemo.B
    {
    public class SayHello:ISayHello
    {
    public string Talk()
    {
    return "Talk from B.SayHello";
    }
    }
    }
  4. MultiImpDemo.Show 这个就是用来显示我们模拟效果的API项目,首选我们在ConfigureServices中加入如下的代码来进行上述两种实现方式的注入:

     services.AddTransient<ISayHello, MultiImpDemo.A.SayHello>();
    services.AddTransient<ISayHello, MultiImpDemo.B.SayHello>();
  5. 在api实现里面获取服务并进行模拟调用:

      private readonly ISayHello sayHello;
    
            public ValuesController(ISayHello sayHello)
    {
    this.sayHello = sayHello;
    } // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
    return new string[] { sayHello.Talk() };
    }

    代码很简单对不对?你应该看的懂吧,这时候我们运行起来项目,然后访问API'api/values'这个接口,结果总是显示如下的结果:

两种需求对应两种实现

这里有两种业务需求!第一种业务中只需要对其中一种实现方式进行调用,如:业务需要SqlServer数据库的实现就行了。第二种是业务中对这两种实现方式都有用到,如:业务急需要用到Oracle的数据库实现同时也有用到SqlServer的数据库实现,需要同时往这两个数据库中插入相同的数据。下面分别对这两种需求进行解决。

业务中对这两种实现方式都有用到

针对这种情况有如下两种实现方式:

  1. 第二种实现方式

    其实,在ASP.NET Core中,当你对一个接口注册了多个实现的时候,构造函数是可以注入一个该接口集合的,这个集合里是所有注册过的实现。

    下面我们先改造下ConfigureServices,分别注入下这两种实现

    services.AddTransient<ISayHello, A.SayHello>();
    services.AddTransient<ISayHello,B.SayHello>();

    接着继续改造下注入的方式,这里我们直接注入IEnumerable<ISayHello>如下代码所示:

    private readonly ISayHello sayHelloA;
    private readonly ISayHello sayHelloB;
    public ValuesController(IEnumerable<ISayHello> sayHellos)
    {
    sayHelloA = sayHellos.FirstOrDefault(h => h.GetType().Namespace == "MultiImpDemo.A");
    sayHelloB = sayHellos.FirstOrDefault(h => h.GetType().Namespace == "MultiImpDemo.B");
    } // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
    return new string[] { sayHelloA.Talk() , sayHelloB.Talk()};
    }

    然后运行起来看下效果吧

  2. 利用AddTransient的扩展方法public static IServiceCollection AddTransient<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) where TService : class; 然后根据我们的配置的实现来进行服务实现的获取。下面就让我们利用代码来实现一番吧:

      services.AddTransient<A.SayHello>();
    services.AddTransient<B.SayHello>(); services.AddTransient(implementationFactory =>
    {
    Func<string, ISayHello> accesor = key =>
    {
    if (key.Equals("MultiImpDemo.A"))
    {
    return implementationFactory.GetService<A.SayHello>();
    }
    else if (key.Equals("MultiImpDemo.B"))
    {
    return implementationFactory.GetService<B.SayHello>();
    }
    else
    {
    throw new ArgumentException($"Not Support key : {key}");
    }
    };
    return accesor;
    });

    当然了,既然用到了我们配置文件中的代码,因此我们需要设置下这个配置:

    然后我们具体调用的依赖注入的方式需要变化一下:

    private readonly ISayHello sayHelloA;
    private readonly ISayHello sayHelloB; private readonly Func<string, ISayHello> _serviceAccessor; public ValuesController(Func<string, ISayHello> serviceAccessor)
    {
    this._serviceAccessor = serviceAccessor; sayHelloA = _serviceAccessor("MultiImpDemoA");
    sayHelloB = _serviceAccessor("MultiImpDemoB");
    } // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
    return new string[] { sayHelloA.Talk() , sayHelloB.Talk()};
    }

    然后运行看下效果吧:

    可以看到A跟B的实现都获取到了!效果实现!

业务只需要对其中一种实现方式的调用

这时候我们可以根据我们预设的配置来动态获取我们所需要的实现。这段话说的我自己都感觉拗口。话不多少,开鲁吧!这里我将介绍三种实现方式。

  1. 根据我们的配置文件中设置的key来进行动态的注入。

    这种方式实现之前首先得进行相应的配置,如下所示:

      "CommonSettings": {
    "ImplementAssembly": "MultiImpDemo.A"
    }

    然后在注入的时候根据配置进行动态的进行注入:

     services.AddTransient<ISayHello, A.SayHello>();
    services.AddTransient<ISayHello, B.SayHello>();

    然后在服务调用的时候稍作修改:

      private readonly ISayHello sayHello;
    public ValuesController(IEnumerable<ISayHello> sayHellos,IConfiguration configuration)
    {
    sayHello = sayHellos.FirstOrDefault(h => h.GetType().Namespace == configuration.GetSection("CommonSettings:ImplementAssembly").Value);
    } // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
    return new string[] { sayHello.Talk() };
    }

    OK,到这里运行一下看下效果吧!然后改下配置文件再看下效果!

  2. 第二种实现方式,即接口参数的方式这样可以避免上个方法中反射所带来的性能损耗。

    这种方式是参考汪宇杰公众号里面的一篇文章,有兴趣的可以关注下他的公众号:“汪宇杰博客”

    这里也给出他的博客链接:https://edi.wang 经常分享干货!

    这里我们改造下接口,接口中加入一个程序集的属性,如下所示:

    public interface ISayHello
    {
    string ImplementAssemblyName { get; }
    string Talk();
    }

    对应的A跟B中的实现代码也要少做调整:

    A:

     public string ImplementAssemblyName => "MultiImpDemo.A";
    
            public string Talk()
    {
    return "Talk from A.SayHello";
    }

    B:

     public string ImplementAssemblyName => "MultiImpDemo.B";
    
            public string Talk()
    {
    return "Talk from B.SayHello";
    }

    然后,在实现方法调用的时候稍微修改下:

     private readonly ISayHello sayHello;
    public ValuesController(IEnumerable<ISayHello> sayHellos,IConfiguration configuration)
    {
    sayHello = sayHellos.FirstOrDefault(h => h.ImplementAssemblyName == configuration.GetSection("CommonSettings:ImplementAssembly").Value);
    } // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
    return new string[] { sayHello.Talk() };
    }

    效果自己运行下看下吧!

  3. 第三种实现是根据配置进行动态的注册

    首先修改下ConfigureServices方法:

     var implementAssembly = Configuration.GetSection("CommonSettings:ImplementAssembly").Value;
    if (string.IsNullOrWhiteSpace(implementAssembly)) throw new ArgumentNullException("CommonSettings:ImplementAssembly未配置");
    if (implementAssembly.Equals("MultiImpDemo.A"))
    {
    services.AddTransient<ISayHello, A.SayHello>(); }
    else
    {
    services.AddTransient<ISayHello, B.SayHello>(); }

    这样的话就会根据我们的配置文件来进行动态的注册,然后我们像往常一样进行服务的调取即可:

      private readonly ISayHello _sayHello;
    public ValuesController(ISayHello sayHello)
    {
    _sayHello = sayHello;
    } // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
    return new string[] { _sayHello.Talk() };
    }

    运行即可得到我们想要的效果!

源码地址

https://download.csdn.net/download/qin_yu_2010/10917684

注意,源码有改动,你可以跟着文章把相应的代码拷贝进来即可运行

总结

本文从具体的业务需求入手,根据需求来或动态的进行对应服务的获取,或同时使用两个不同的实现!希望对您有所帮助!如果您有更多的实现方法可以在下方留言,或者加入.NET Core实战千人群跟637326624大伙进行交流,最后感谢您的阅读!

.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了的更多相关文章

  1. ExpandoObject与DynamicObject的使用 RabbitMQ与.net core(一)安装 RabbitMQ与.net core(二)Producer与Exchange ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler) .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

    ExpandoObject与DynamicObject的使用   using ImpromptuInterface; using System; using System.Dynamic; names ...

  2. 在.NET Core中处理一个接口多个不同实现的依赖注入问题

    前言 近段时间在准备公司的技术分享,所以这段时间将大部分时间放在准备分享内容上去了.博客也就停了一下下. 在.NET Core中处理依赖注入问题时,往往是定义好了一个操作规范的接口,会有N多个基于不同 ...

  3. ASP.NET Core中使用自定义MVC过滤器属性的依赖注入

    除了将自己的中间件添加到ASP.NET MVC Core应用程序管道之外,您还可以使用自定义MVC过滤器属性来控制响应,并有选择地将它们应用于整个控制器或控制器操作. ASP.NET Core中常用的 ...

  4. 在ASP.NET Core中实现一个Token base的身份认证

    注:本文提到的代码示例下载地址> How to achieve a bearer token authentication and authorization in ASP.NET Core 在 ...

  5. [转]NET Core中实现一个Token base的身份认证

    本文转自:http://www.cnblogs.com/Leo_wl/p/6077203.html 注:本文提到的代码示例下载地址> How to achieve a bearer token ...

  6. NET Core中实现一个Token base的身份认证

    NET Core中实现一个Token base的身份认证 注:本文提到的代码示例下载地址> How to achieve a bearer token authentication and au ...

  7. 如何在ASP.NET Core中实现一个基础的身份认证

    注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...

  8. [转]如何在ASP.NET Core中实现一个基础的身份认证

    本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...

  9. [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

    [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 本文首发自:博客园 文章地址: https://www.cnblogs.com/yilezhu/p/ ...

随机推荐

  1. bzoj 1064 假面舞会 图论??+dfs

    有两种情况需要考虑 1.链:可以发现对最终的k没有影响 2.环:如果是真环(即1->2->3->4->1),可以看出所有可行解一定是该环的因数 假环呢??(1->2-&g ...

  2. BZOJ_2962_序列操作_线段树

    Description 有一个长度为n的序列,有三个操作1.I a b c表示将[a,b]这一段区间的元素集体增加c,2.R a b表示将[a,b]区间内所有元素变成相反数,3.Q a b c表示询问 ...

  3. layer使用总结

    1.询问框的使用 主要体现在删除等重要操作 让用户进行二次确认的场景 //询问框 layer.confirm('您是如何看待前端开发?', { btn: ['重要','奇葩'] //按钮 }, fun ...

  4. oracle改造常见问题

    一. to_char: 将数值型或者日期型转化为字符型 (string) 日期到字符操作 select to_char(sysdate,'yyyy-MM-dd HH24:mi:ss') from du ...

  5. Android--APP性能测试工具GT的使用总结

    GT(随身调)是APP的随身调测平台,它是直接运行在手机上的"集成调测环境"(IDTE, Integrated Debug Environment).利用GT,仅凭一部手机,无需连 ...

  6. selenium+python,解决selenium弹出新页面,无法定位元素的问题(报错:Unable to locate element:元素)

    1.问题发生描述: 从一个页面进行点击等操作,页面跳转到第二个页面,对第二个页面中的元素,采取任何措施定位都报错,问题报错点如下: 2.出现问题的原因: 窗口句柄还停留在上一个页面,对于当前新弹出的页 ...

  7. C# Vista Command Link Control with Windows Forms

    using System; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; u ...

  8. 《前端之路》 之 前端 安全 XSS 原理以及防御手段

    什么是 XSS 一.XSS 什么是 XSS XSS,即 Cross Site Script , 翻译过来就是 跨站脚本攻击:为了和 css 有所区分,因而在安全领域被称为 XSS. 什么是 XSS 攻 ...

  9. BoltDB简单使用教程

    1.BoltDB简介 Bolt是一个纯粹Key/Value模型的程序.该项目的目标是为不需要完整数据库服务器(如Postgres或MySQL)的项目提供一个简单,快速,可靠的数据库. BoltDB只需 ...

  10. 使用 ASP.NET Core MVC 创建 Web API(五)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...