.NET中的依赖注入示例?

iibxawm4  于 2023-03-24  发布在  .NET
关注(0)|答案(9)|浏览(115)

有人能用一个基本的.NET示例来解释依赖注入***,并提供一些指向.NET资源的链接来扩展这个主题吗?
这不是What is dependency injection?的重复,因为我询问的是特定的.NET示例和资源。

snz8szmq

snz8szmq1#

这里有一个常见的例子,你需要登录你的应用程序,但是在设计时,你不确定客户端是想登录数据库、文件还是事件日志。
因此,您希望使用DI将该选择推迟到可由客户机配置的选择。
这是一些伪代码(大致基于Unity):
创建日志记录接口:

public interface ILog
{
  void Log(string text);
}

然后在类中使用此接口

public class SomeClass
{
  [Dependency]
  public ILog Log {get;set;}
}

在运行时注入这些依赖项

public class SomeClassFactory
{
  public SomeClass Create()
  {
    var result = new SomeClass();
    DependencyInjector.Inject(result);
    return result;
  }
}

示例在app.config中配置:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name ="unity"
             type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
              Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity>
    <typeAliases>
      <typeAlias alias="singleton"
                 type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,Microsoft.Practices.Unity" />
    </typeAliases>
    <containers>
      <container>
        <types>
          <type type="MyAssembly.ILog,MyAssembly"
                mapTo="MyImplementations.SqlLog, MyImplementations">
            <lifetime type="singleton"/>
          </type>
        </types>
      </container>
    </containers>
  </unity>
</configuration>

现在,如果您想更改日志记录器的类型,只需进入配置并指定另一种类型。

w7t8yxp5

w7t8yxp52#

Ninject必须有一个最酷的样本:(从样品中截取)

interface IWeapon {
  void Hit(string target);
}
class Sword : IWeapon {
  public void Hit(string target) {
    Console.WriteLine("Chopped {0} clean in half", target);
  }
}
class Shuriken : IWeapon {
  public void Hit(string target) {
    Console.WriteLine("Shuriken tossed on {0}", target);
  }
}
class Samurai {
  private IWeapon _weapon;

  [Inject]
  public Samurai(IWeapon weapon) {
    _weapon = weapon;
  }

  public void Attack(string target) {
    _weapon.Hit(target);
  }
}

class WeaponsModule: NinjectModule {
  private readonly bool _useMeleeWeapons;

  public WeaponsModule(bool useMeleeWeapons) {
    _useMeleeWeapons = useMeleeWeapons;
  }

  public void Load() {
    if (useMeleeWeapons)
      Bind<IWeapon>().To<Sword>();
    else
      Bind<IWeapon>().To<Shuriken>();
  }
}

class Program {
  public static void Main() {
    bool useMeleeWeapons = false;
    IKernel kernel = new StandardKernel(new WeaponsModule(useMeleeWeapons));
    Samurai warrior = kernel.Get<Samurai>();
    warrior.Attack("the evildoers");
  }
}

这,对我来说,读起来非常流利,在你开始你的 dojo 之前,你可以决定如何武装你的武士。

hpcdzsge

hpcdzsge3#

我认为你首先学习没有IoC容器的DI是很重要的。因此,我写了一个例子,慢慢地构建一个IoC容器。这是我工作中的一个真实的的例子,但仍然足够基础,让新手抓住DI的本质。你可以在这里找到它:https://dannyvanderkraan.wordpress.com/2015/06/15/real-world-example-of-dependeny-injection/
它使用C#.NET,后来使用Unity。

评论后更新:

  • 文章的相关部分 *

“观察原始设计的以下变化:

我们使用“构造函数注入”模式来实现DI,重构步骤如下:
1.通过创建接口ICardPresenceChecker抽象出CardPresenceChecker接口;
1.通过将其名称更改为XCardPresenceChecker,明确此CardPresenceChecker仅适用于公司X的库;
1.使用XCardPresenceChecker实现接口ICardPresenceChecker ;
1.将LogInService的属性抽象为ICardPresenceChecker类型,而不是“知道”船上的具体实现;
1.最后但并非最不重要的是,LogInService的用户(其他开发人员)要求他们提供至少实现ICardPresenceChecker的任何类,以便LogInService可以做它的事情。
LogInService的构造函数如下所示:

this.myCardPresenceChecker = cardPresenceChecker;
this.myCardPresenceChecker.CardIn += MyCardPresenceChecker_CardIn;
this.myCardPresenceChecker.CardOut += MyCardPresenceChecker_CardOut;
this.myCardPresenceChecker.Init();

那么你在哪里为LogInService提供一个ICardPresenceChecker的实现呢(在该示例中,我们将ICardPresenceChecker“Map”到XCardPresenceChecker)在应用程序启动时的一个中心位置,在概念上称为“组合根”。对于可以是Program类中的空Main的常规控制台应用程序。因此对于该示例,这段代码将在前面提到的地方使用:
LogInService logInService = new LogInService(new XCardPresenceChecker());”

kgsdhlau

kgsdhlau4#

我有一个非常简单的依赖注入的例子。
看看下面的类,你会得到整个想法。正如你所看到的,除非你提供文件,它将使用默认的一个设置文件,但你可以设置一个设置文件,然后类将使用它。

Public Class ProcessFile

Private _SettingsFile As String = "settings.bin"

Public Sub New()
End Sub

Public Sub New(settings As String)
_SettingsFile= settings
End Sub

Public Function ReadFile() As String 
'Do stuff based on the settings stored in the _SettingsFile 
End Function

End Class

显然这是最基本的情况。在真实的世界中,你可以对类类型做同样的事情,比如你有数据库层,你可以通过依赖注入来切换底层数据库dll,只要你能提供有效的类(一个实现你正在使用的接口的类),你的代码就可以与任何数据库一起工作。
在掌握了基础知识之后,你可以在更大的范围内完成这项工作,并通过使用像unity这样的DI框架完全独立于应用程序。

3mpgtkmj

3mpgtkmj5#

你基本上在构造函数中传递了所有必需的对象。或者,你可以在运行时使用接口解析器来解析它们(尽管这不太类型安全)。你可以在Ninject网站上找到第一种方法的优秀例子,Unity网站上有第二种方法的很好的例子。这就避免了对单例的需要,并允许您轻松地放入符合所需接口的替换对象

gpnt7bae

gpnt7bae6#

在主mvc4项目名称SampleDependency. Unity.mvc4、unity.webapi和MicrosoftAsp.Net Web API 2.2 Web主机中安装以下Nuget包
在Web项目中

public static class Bootstrapper
{
    public static IUnityContainer Initialise()
    {
        var container = BuildUnityContainer();

        DependencyResolver.SetResolver(new UnityDependencyResolver(container));
        GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);

        return 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

        // e.g. container.RegisterType<ITestService, TestService>();
        container.RegisterType<IUserDetailLogic, UserDetailLogic>();
        container.RegisterType<IUserData, UserData>();

        RegisterTypes(container);

        return container;
    }
    public static void RegisterTypes(IUnityContainer container)
    {

    }
}
ffscu2ro

ffscu2ro7#

添加类型类库的业务逻辑项目,并在其中添加以下代码。public class UserDetailLogic:IUserDetailLogic {私有IUserData用户数据=空;

public UserDetailLogic(IUserData userData)
    {
        _userData = userData;
    }
    public string getUserDetails()
    {
        return _userData.getUserDetails();
    }
}

public interface IUserDetailLogic
{
    string getUserDetails();
}

在你的主项目中添加下面的代码在家庭控制器。
公共类家庭控制器:控制器{ private readonly IUserDetailLogic _userDetailLogic;

public HomeController(IUserDetailLogic userDetailLogic)
    {
        _userDetailLogic = userDetailLogic;
    }

    public ActionResult Index()
    {
        ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
        string str = _userDetailLogic.getUserDetails();
        return View();
    }

    public ActionResult About()
    {
        ViewBag.Message = "Your app description page.";

        return View();
    }

    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";

        return View();
    }
}
58wvjzkj

58wvjzkj8#

创建数据库层项目(类库),并添加以下代码。

public class UserData : IUserData
{
    public string getUserDetails()
    {
        return "Asif";
    }
}

public interface IUserData
{
    string getUserDetails();
}
ou6hu8tu

ou6hu8tu9#

这里的dependencies是什么意思?

一个类在其构造函数中拥有的所有参数都是该类的依赖项(好吧,有时属性可以被标记为依赖项,但现在让我们忽略它)。在下面的类中,依赖项是ILoggerIDbConnectionFactory

public class MyHandler
{
    private IConnectionFactory _connectionFactory;
    private ILogger _logger;

    public MyHandler(
        IConnectionFactory connectionFactory,
        ILogger logger)
    {
        _connectionFactory = connectionFactory;
        _logger = logger;
    }
    
    public void Handle(MyRequest request)
    {
        // do stuff with the dependencies.
    }
}

请注意,Handle方法的参数MyRequest不是类的依赖项,因为您不需要它来创建类的示例。

这里的injection是什么意思?

而不是手动注入依赖项:

var myHandler = new MyHandler(new MyConnectionFactory(), new MyLogger());

您可以将MyConnectionFactoryMyLogger注册到“服务集合”,并自动注入这些依赖项。

将类注册到服务集合

在.NET中,一个服务集合实现了IServiceCollection interface。假设你想让一个logger从应用程序启动到关闭都是同一个示例。然后你在启动时将它添加为“singleton”:

serviceCollection.AddSingleton<ILogger>(new MyLogger());

现在你可能会想:“但那是什么serviceCollection?它是一个列表?一个字典?".我现在的答案:服务集合是一个类,它可能在内部使用List或Dictionary或其他东西,但您不应该担心这些。
在哪里可以找到这个服务集合?这取决于你正在构建的应用类型。在关于App startup in ASP.NET Core的页面上,你可以看到这行代码:

builder.Services.AddTransient<IStartupFilter, RequestSetOptionsStartupFilter>();

builder具有IServiceCollection类型的属性Services,并且已将IStartupFilter添加到服务集合中。因此,在ASP.NET Core应用中,您可以在builder.Services处找到服务集合。其他应用类型的命名可能不同,但您可能会在文档或示例项目中轻松找到它。

如何使用这些服务?

在app启动的某个时候,从你的service集合中创建了一个实现IServiceProvider interface的新类,它有一个方法GetService。理论上一个类可以实现两个接口:

public class MyServices : IServiceCollection, IServiceProvider
{
    // implement all interface members.
}

但实际上(经常)不是这种情况。
那么,我们如何找到IServiceProvider类型的属性呢?当构建ASP.NET Core应用时,页面有一个实际上是IServiceProviderHttpContext.RequestServices property。但是,你可能不需要使用它,因为如果你向页面的构造函数添加依赖项,它会自动注入:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.Use("Hello!");
    }
}

另一种方法是在页面的GetPost方法中使用[FromServices] attribute

public class IndexModel : PageModel
{
    public void OnGet([FromServices] IMyDependency myDependency)
    {
        myDependency.Use("Hello again!");
    }
}

注册什么,不注册什么?

  • 在服务集合中包含一些非常基本且经常使用的类,例如logger、数据库连接工厂、电子邮件发送者等。服务可能依赖于其他服务,服务提供者会为您处理所有注入。此外,无论此类的构造函数参数何时更改,都没有关系,因为接口保持不变。
  • 仅在一个或两个页面上使用的高级类可能不应该添加到您的服务集合中。例如,考虑ProductOverviewQueryHandler。但是,如果该查询处理程序具有依赖关系怎么办?我如何注入它们?请继续阅读...

注入高层类依赖

这可以通过多种方式实现。一种方式是在页面的任何地方使用ActivatorUtilities class

ActivatorUtilities.CreateInstance<ProductOverviewQueryHandler>(HttpContext.RequestServices)

但很多人会觉得这是一个丑陋的阅读。
我发现非常干净的方法是使用NuGet包IGet。您的页面类看起来像这样:

public class IndexModel : PageModel
{
    public void OnGet([FromServices] IGet i)
    {
        var queryHandler = i.Get<ProductOverviewQueryHandler>();
        // do something
    }
}

或者像这样

public class IndexModel : PageModel
{
    private readonly IGet i;

    public IndexModel(IGet iget)
    {
        i = iget;            
    }

    public void OnGet()
    {
        var queryHandler = i.Get<ProductOverviewQueryHandler>();
        // do something
    }

    public Task<IActionResult> OnPost(FilledOutForm request)
    {
        var result = i.Get<OtherHandler>().Handle(request);
        // return something
    }
}

或者为你的页面创建一个基类:

public class PageBase : PageModel
{
    private IGet _iget;
    private IGet i => _iget ??= HttpContext.RequestServices.GetRequiredService<IGet>();
}

像这样使用它:

public class IndexModel : PageBase
{
    public void OnGet()
    {
        var queryHandler = i.Get<ProductOverviewQueryHandler>();
        // do something
    }
}

安装IGet

通过NuGet Package Manager in Visual Studio安装IGet,并添加到应用程序的启动:

serviceCollection.AddIGet();

之后,您可以使用IGet,如上面的示例所示。

进一步阅读

  • 使用扩展方法注册服务组。
  • 服务寿命。

相关问题