Simple Injector - 在运行时根据指定的泛型类型注入服务
Simple Injector - Inject service based on the specified generic type at runtime
我是 Autofac 的长期用户,最近切换到 Simple Injector 以满足我的 DI 容器需求。当我使用 Autofac 时,我能够做一些我仍然不能用 Simple Injector 做的事情,也许是因为我还没有完全理解 API。
假设我有 TEntity
和 TDbContext
的服务 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 注册了模块。
最后一切都很完美。您确实需要在注册过程结束时调用这些行:EnableSimpleInjectorCrossWiring
和 UseSimpleInjectorAspNetRequestScoping
。之后无需使用 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>();
我是 Autofac 的长期用户,最近切换到 Simple Injector 以满足我的 DI 容器需求。当我使用 Autofac 时,我能够做一些我仍然不能用 Simple Injector 做的事情,也许是因为我还没有完全理解 API。
假设我有 TEntity
和 TDbContext
的服务 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 注册了模块。
最后一切都很完美。您确实需要在注册过程结束时调用这些行:EnableSimpleInjectorCrossWiring
和 UseSimpleInjectorAspNetRequestScoping
。之后无需使用 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>();