【问题标题】:.net-core Dependency Injection.net-core 依赖注入
【发布时间】:2016-05-22 09:56:55
【问题描述】:

我有一个我想为 DI 注册的通用存储库,它实现了一个接口 IRepository。

通常我会像这样创建它的一个实例:

IRepository repo = new Repository<Order>();

但是,我试图在 .net 5 发布之前加快速度,并希望在 DI 中使用它,我采取了以下措施:

services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>();

但这感觉不对,我不希望模型中的每个类都有 50 多行...

我在网上找不到任何关于此的信息,我知道它可以与其他 ioc 容器一起使用。但由于这是一个学习项目,我不想使用其他容器,我的目标是使用 .net5s 原生容器来完成所有这些。

【问题讨论】:

    标签: c# dependency-injection inversion-of-control asp.net-core


    【解决方案1】:

    您可以做的是创建一个扩展方法来封装所有需要注册的单个项目。

    这与微软正在使用的技术相同,例如你只将它放在启动中:

    services.AddMvc();
    

    但这是一种扩展方法,在幕后你可以打赌它正在注册一堆它需要的东西。

    因此您可以像这样创建自己的扩展方法:

    using Microsoft.Extensions.DependencyInjection;
    public static IServiceCollection AddMyFoo(this IServiceCollection services)
    {
        services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>();
        //....
    
        return services;
    }
    

    通过使方法返回 IServiceCollection,您可以使其流畅,这样您就可以做到

    services.AddMyFoo().AddSomeOtherFoo();
    

    根据评论更新

    另一种减少注册的技术是,当您的依赖项本身没有依赖项时,您可以使构造函数的默认值为 null,这样您仍然可以解耦,并且可以稍后传递一个不同的,但 DI 不会抛出错误,如果没有传入,你可以实例化你需要的东西。

    public class MyFoo(IFooItemDependency myItem = null)
    {
        private IFooItemDependency internalItem;
    
        public MyFoo(IFooItemDependency myItem = null)
        {
            internalItem = myItem ?? new FooItemItem();
        }
    }
    

    【讨论】:

    • 这仍然需要我注册 50 多个存储库,它只是将代码移动到其他地方......如果我要这样做,那么我还不如摆脱通用存储库。为了澄清我想要(如果可能的话)有一行来注册 repo,不管它的具体实现是什么类型。
    • 刚刚用一些可能有帮助的东西更新了我的答案,但最终什么都不能传递,除非它在某处注册,反射没有自动发现。请注意,您不必使用内置 DI,因此如果其他兼容的 DI 支持自动注册,您可以使用它,但这不是内置 DI 的功能
    • 这就是我的想法。谢谢您的确认。尽管我不想使用其他任何东西,但我将使用另一个 IOC 容器。
    • @D3vy - 你必须明白 IoC 不会为你做你的工作。是的,它将注册移动到其他地方,但您仍然需要进行注册。将它移到其他地方的目的是它促进可测试性、可发现性和强制依赖隔离,所有这些都会导致更好的代码,而不是减少你的工作量。
    【解决方案2】:

    我不是 100% 确定你的问题是什么,我认为你不想拥有

    services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>();
    services.AddTransient<DAL.IRepository<Models.Person>, DAL.Repository<Models.Person>>();
    services.AddTransient<DAL.IRepository<Models.Invoice>, DAL.Repository<Models.Invoice>>();
    

    我以前做过这个(使用 ninject)

    Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
    

    我想你可以为 Unity 做类似的事情

    services.AddTransient<DAL.IRepository<>, typeof(Repository<>)();
    

    然后在服务中使用它

    public OrderService(IRepository<Models.Order> orderRepository)
    {
        this.orderRepository = orderRepository;
    }
    

    编辑

    正如 OP 所指出的,正确的语法是:

    services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
    

    【讨论】:

    • 遗憾的是 services.AddTransient&lt;DAL.IRepository&lt;&gt;, typeof(Repository&lt;&gt;)(); 不是有效代码,问题与 Unity 无关。
    • 我不喜欢提到 Unity,但无论如何它不是统一代码。有没有错误?运行?构建时间?
    • 我没有将它输入到 VS 但可以从这里看到 3 个问题都将无法编译:1. &lt; 的数量比 &gt; 的数量多。 2.你不能把typeof(Repository&lt;&gt;)放在&lt;&gt;之间,3.你不能把像DAL.IRepository&lt;&gt;这样的开放泛型类型放在&lt;&gt;之间(你需要像@987654335这样的东西@)
    • 你知道现在复制粘贴是一种选择吗?在这里(正如我所说,它可能会起作用。我的意思是至少尝试对吗?) services.AddTransient, typeof(Repository)>();
    • @qujck 感谢您编辑我的答案,通过 SO android 应用程序发布并发现界面有点缺乏。无论如何,您上面的一个 cmets 让我走上了正确的道路,问题是我使用了不正确的语法(尝试在 之间放置一个开放的泛型类型),您指出的正确语法是:services.AddTransient(typeof(IRepository ), typeof(存储库));如果您想将其放入答案中,请将其标记为正确。感谢您的帮助!
    【解决方案3】:

    在 cmets 中对其他答案进行了一些来回后,我有了一个可行的解决方案,这可能不是最好的方法,但它确实有效。如果我找到更好的方法来实现它,我会再次更新。

    我遇到的两个问题是:需要注册一个泛型接口,这里的问题是我的注意力不集中。我注册泛型类型的语法错误,当然是:

    services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
    

    第二个问题是我有一个程序集,其中包含我想要注册的 50 多个不同模型,我解决这个问题的方法是编写一个方法,我可以将程序集列表与我想要的命名空间一起传递给进行注册,它会遍历任何符合条件的类型并将它们注册到 DI 容器中。

    public void RegisterModels(IServiceCollection services, string[] Assemblies, string @NameSpace)
        {
            foreach (var a in Assemblies)
            {
                Assembly loadedAss = Assembly.Load(a);
    
                var q = from t in loadedAss.GetTypes()
                        where t.IsClass && !t.Name.Contains("<") && t.Namespace.EndsWith(@NameSpace)
                        select t;
    
                foreach (var t in q.ToList())
                {
                    Type.GetType(t.Name);
                    services.AddTransient(Type.GetType(t.FullName), Type.GetType(t.FullName));
                }
            }
        }
    

    然后从 startup.cs 方法 ConfigureServices 调用它:

    public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddEntityFramework()
                .AddSqlServer()
                .AddDbContext<TestContext>(options =>
                    options.UseSqlServer(@"Server=LOCALHOST\SQLEXPRESS;Database=Test;Trusted_Connection=True;"));
    
            services.AddMvc();
    
            RegisterModels(services, new string[] { "UI" }, "UI.Models");
    
            services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
        }
    

    可能有更好的方法来做到这一点,肯定是使用不同的 DI 容器,如果有人有改进,请告诉我。

    【讨论】:

    • 除非我遗漏了什么。为什么需要使用 DI 注册视图模型?
    • 你说得对,最初的问题是我无法在不指定类型的情况下注册通用存储库,这导致我走上了一条建议这不是可能在.net 5 DI容器中 - 所以我开始寻找方法来最小化注册我需要的所有不同类型的通用回购的工作......但是你是对的,不需要RegisterModels方法 - 我离开了因为它可能对其他希望在命名空间中注册多个类的人有用,我将对其进行编辑以使其更清晰。
    【解决方案4】:

    你应该能够注册开放的泛型

    services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
    

    【讨论】:

      【解决方案5】:

      您可以使用基于约定的注册库,例如 Scrutor

      Scutor 是一个小型开源库,它提供流畅的 API 以根据约定(类似于 Autofac 的 RegisterAssemblyTypes 方法、StructureMap 的 Scan 方法和 Ninject 的 Conventions 包)在您的 Microsoft.Extensions.DependencyInjection 容器中注册服务。

      这将允许您执行以下操作:

      services.Scan(scan => scan
                  .FromAssemblies(<<TYPE>>.GetTypeInfo().Assembly)
                      .AddClasses(classes => classes.Where(x => {
                          var allInterfaces = x.GetInterfaces();
                          return 
                              allInterfaces.Any(y => y.GetTypeInfo().IsGenericType && y.GetTypeInfo().GetGenericTypeDefinition() == typeof(IRepository<>)));
                      }))
                      .AsSelf()
                      .WithTransientLifetime()
              );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-23
        • 1970-01-01
        • 2021-02-02
        • 1970-01-01
        • 2020-12-20
        • 2018-03-03
        • 2021-07-15
        • 1970-01-01
        相关资源
        最近更新 更多