.NET Core 注册具有不同数量参数的原始泛型

.NET Core Register Raw Generic with Different Number of Parameters

我正在处理 .NET Core 项目。我需要为一些原始泛型自动注册一个默认值。当参数计数相同时,一切正常。

public void RegisterServiceDefaults(IServiceCollection services)
{
     services.AddSingleton(typeof(IAdaptable<,>), typeof(DynamicAdapter<,>));
}

我的 IAdaptable<TEntity, TDTO> 允许实体之间的动态适应,但在我的具体服务中,默认情况下我们需要一个接口,以便我们可以控制每个 属性 如何适应,我的 IAdaptable<TEntity, TDTO>实际上只是为了方便这个接口的包装

IAdaptable<TEntity, TIDTO, TDTO>  
    where TDTO: TIDTO
{
}

IAdaptable<TEntity, TDTO> : IAdaptable<TEntity, TDTO, TDTO>
{
}

我如何才能通用地注册我的适应性对象,以便如果有人请求 IAdaptable<TEntity, TDTO, TDTO> 它会默认 return IAdaptable<TEntity, TDTO>

EDIT 提供用于理解问题 TL;DR

这用于提供两种注册方式的休息框架,最终用户可以创建一个 IAdaptable<TEntity, TIDTO, TDTO> 示例适配器可能如下所示:

public UserAdapter : IAdaptable<User, IUserDTO, UserDTO>, IUserDTO
{
     User _user;

     public UserAdapter(User user)
     {
         this._user = user;
     }


     public int Id
     {
         get => this._user.Id;
         set { /*Do nothing you cannot override the server Id*/}
     }

     //... The rest of the User Properties
}

当最终用户创建服务时,他们可以使用 IAdaptable<TEntity, TIDTO, TDTO>,或者如果他们不想创建具体的服务,他们可以将一些选项传递给只知道 TEntityTDTO 属性,没有理由创建仅用于适应的 TIDTO

现在当最终用户创建他们可以依赖任一接口的服务时,GenericBase 服务默认请求 IAdaptable,如下所示:

public class RestService<TEntity, TIDTO, TDTO>
    where TDTO: TIDTO
{
    public RestService(DbContext context, IAdaptable<TEntity, TIDTO, TDTO> adapter, IValidator<TEntity> validator)
    {
    }
}

对于简化版本:

  public class RestService<TEntity, TDTO> : RestService<TEntity, TDTO, TDTO>
  {
     //Implementation...
  }

现在一般来说,如果最终用户想要一个动态适配器,他们应该使用简化的 RestService,但根据他们的体系结构,他们可能会在 DTO 上实现接口,或者他们可能只是使用更明确的基础并决定使用 RestService<TEntity, TIDTO, TDTO>,在这种情况下会给他们带来严重的运行时错误。因此,为了避免让我的支持团队处理最终用户错误,我想避免这种情况,让 IAdaptable<TEntity,TIDTO, TDTO> 与我的动态适配器一起工作,这是一个 IAdaptable<TEntity,TDTO>.

您可以创建自己的扫描实现,使用反射在一行中注册多个类型。

ServiceCollectionExtensions

这里我们实现了一个简单的 Scan 扩展方法,允许您选择一个接口(无论是通用接口还是 non-generic),并且该接口的所有实现都将为所有提供的程序集注册.

public static class ServiceCollectionExtensions
{
    public static IServiceCollection Scan(
        this IServiceCollection serviceCollection,
        Assembly assembly,
        Type serviceType,
        ServiceLifetime lifetime)
    {
        return Scan(serviceCollection, new Assembly[] { assembly }, serviceType, lifetime);
    }

    public static IServiceCollection Scan(
        this IServiceCollection serviceCollection, 
        IEnumerable<Assembly> assemblies, 
        Type interfaceType,
        ServiceLifetime lifetime)
    {
        foreach (var type in assemblies.SelectMany(x => 
            x.GetTypes().Where(t => t.IsClass && !t.IsAbstract)))
        {
            foreach (var i in type.GetInterfaces())
            {
                // Check for generic
                if (i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType)
                {
                    var genericInterfaceType = interfaceType.MakeGenericType(i.GetGenericArguments());
                    serviceCollection.Add(new ServiceDescriptor(genericInterfaceType, type, lifetime));
                }
                // Check for non-generic
                else if (!i.IsGenericType && i == interfaceType)
                {
                    serviceCollection.Add(new ServiceDescriptor(interfaceType, type, lifetime));
                }
            }
        }

        return serviceCollection;
    }

    // TODO: Add overloads ScanTransient, ScanSingleton, etc that call the 
    // above with the corresponding lifetime argument
}

用法

要注册关闭通用 IAdaptable<,,> 类型的所有类型并将它们注册为单例,您只需使用:

services.Scan(
    assembly: typeof(User).Assembly,
    interfaceType: typeof(IAdaptable<,,>), 
    lifetime: ServiceLifetime.Singleton);