.net-core 依赖注入

.net-core Dependency Injection

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

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

IRepository repo = new Repository<Order>();

然而,我正试图在发布之前加快 .net 5 的速度,并希望它与 DI 一起工作,我已经采取了以下措施:

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

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

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

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

这与 Microsoft 正在使用的技术相同,例如您只将其放在启动时:

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;
}

并通过使方法 return 成为 IServiceCollection 使其变得流畅,因此您可以

services.AddMyFoo().AddSomeOtherFoo();

根据评论更新

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

public class MyFoo(IFooItemDependency myItem = null)
{
    private IFooItemDependency internalItem;

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

我不是 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(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 容器,如果有人提供改进请告诉我。

您应该能够使用

注册开放通用
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));

您可以使用像 Scrutor 这样基于约定的注册库。

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

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

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()
        );