Simple Injector - 在运行时根据指定的泛型类型注入服务

Simple Injector - Inject service based on the specified generic type at runtime

我是 Autofac 的长期用户,最近切换到 Simple Injector 以满足我的 DI 容器需求。当我使用 Autofac 时,我能够做一些我仍然不能用 Simple Injector 做的事情,也许是因为我还没有完全理解 API。

假设我有 TEntityTDbContext 的服务 IEntityRepository。它的实现看起来像这样:

    public class EntityRepository<TEntity, TDbContext> : IEntityRepository<TEntity, TDbContext>
            where TDbContext : IEntityDbContext where TEntity : class
        {
            public EntityRepository(TDbContext dbContext)
            {
            }
        }

借助 Autofac,我能够将开放通用实现 EntityRepository 注册为开放通用接口 IEntityRepository,因此当我注入 [=25= 的 IEntityRepository 时] 和 IProductsDbContext,DI 容器会自动猜测我通过构造函数注入了一个 ProductsDbContext 的实例。

Simple Injector 可以吗?我尝试了这些,但仍然失败:

container.Register(typeof(IEntityRepository<,>), typeof(EntityRepository<,>).Assembly);
container.Register(typeof(IEntityRepository<,>), typeof(EntityRepository<,>));

在此先感谢您的帮助!

编辑: 所以这是史蒂文要求的 Autofac 的完整示例。创建一个新的 .NET Core 控制台应用程序。您需要安装 NuGet 包 Autofac。

Program.cs:

internal class Program
    {
        private static void Main(string[] args)
        {
            var builder = new ContainerBuilder();

            builder.RegisterType<ProductsDbContext>().AsImplementedInterfaces();
            builder.RegisterGeneric(typeof(EntityRepository<,>)).As(typeof(IEntityRepository<,>));

            var container = builder.Build();
            using (var scope = container.BeginLifetimeScope())
            {
                var productsRepository = scope.Resolve<IEntityRepository<Product, IProductsDbContext>>();
                Console.WriteLine($"Resolved IEntityRepository is of type: {productsRepository.GetType()}");
            }
        }
    }

ProductsDbContext.cs

public class ProductsDbContext : IProductsDbContext
    {
        public void Dispose()
        {
            // Demo, do nothing.
        }

        public int SaveChanges()
        {
            throw new System.NotImplementedException();
        }
    }

Product.cs

public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

EntityRepository.cs

public class EntityRepository<TEntity, TDbContext> : IEntityRepository<TEntity, TDbContext>
        where TDbContext : IEntityDbContext where TEntity : class
    {
        private readonly TDbContext _dbContext;

        public EntityRepository(TDbContext dbContext)
        {
            _dbContext = dbContext;
            Console.WriteLine($"Database context is of type {dbContext.GetType()}.");
        }

        public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> whereClause)
        {
            throw new NotImplementedException();
        }
    }

IEntityDbContext.cs

public interface IEntityDbContext : IDisposable
    {
        int SaveChanges();
    }

我ProductsDbContext.cs

public interface IProductsDbContext : IEntityDbContext
    {

    }

我EntityRepository.cs

public interface IEntityRepository<TEntity, TDbContext> where TDbContext : IEntityDbContext where TEntity : class
    {
        IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> whereClause);
    }

最终的控制台输出应该类似于:

Database context is of type GenericTypeDiTester.DbContexts.ProductsDbContext. Resolved IEntityRepository is of type: GenericTypeDiTester.Repositories.EntityRepository`2[GenericTypeDiTester.Models.Product,GenericTypeDiTester.Interfaces.DbContexts.IProductsDbContext]

您可以在此处下载完整示例:https://drive.google.com/file/d/1UkIYxLsY6YGwo5jOB5TyyncXc6yho8X5/view?usp=sharing

编辑 2: 问题不在于最后的 Simple Injector 库。混合使用 Microsoft.DependencyInjection 和 SimpleInjector 似乎并不是一件好事。正如 Steven 所建议的,您应该专门使用 SI 来注册您的大部分服务,在极少数情况下,MS.DI(例如使用 AddDbContext)。

就我而言,我在我的项目 MediatR 中有一个实现调解器模式的库。这个库为 MS.DI 的 IServiceCollection 提供了一个带有扩展方法 AddMediatR 的 NuGet 包,它应该正确地注册所有处理程序,但对我来说并非如此。所以我最终自己使用 SI 注册了模块。

最后一切都很完美。您确实需要在注册过程结束时调用这些行:EnableSimpleInjectorCrossWiringUseSimpleInjectorAspNetRequestScoping。之后无需使用 IServiceCollection 注册任何其他内容。这样,两个 DI 框架的交叉连接最终可以完美地工作。

在Simple Injector中注册的方法是:

container.Register(typeof(IEntityRepository<,>), typeof(EntityRepository<,>));
container.Register<IProductsDbContext, ProductsDbContext>();

在 Simple Injector 中没有 AsImplementedInterfaces 等效项,尽管有几种方法可以实现相同。在ProductsDbContext有多个接口需要注册的情况下,最明显的方法是显式注册每个接口:

container.Register<IProductsDbContext, ProductsDbContext>();
container.Register<IUsersDbContext, ProductsDbContext>();
container.Register<ICustomersDbContext, ProductsDbContext>();