【问题标题】:Create instance of generic class with proper concrete type使用适当的具体类型创建泛型类的实例
【发布时间】:2019-09-30 06:59:24
【问题描述】:

我想在 EF 验证框架之上实现一个通用的实体验证机制。我所做的是使用单个Validate() 方法创建了一个通用接口,该方法将由不同的实体验证器实现。然后我使用服务定位器模式在 EF ValidateEntity 方法覆盖内实例化相关的具体验证器实现。这是我的实现:

验证器界面

public interface IEntityValidator<T>
{
    DbEntityValidationResult ValidateEntity(T entity);
}

验证器实现

public class BranchValidator<Branch>: IEntityValidator<Branch>
{
    ClientContext clientContext;

    public BranchValidator(ClientContext clientContext)
    {
        this.clientContext = clientContext;
    }

    public DbEntityValidationResult ValidateEntity(Branch entity)
    {
        throw new NotImplementedException();
    }
}

服务定位器(验证定位器)

public static class ValidatorLocator<T>
{
    public static IEntityValidator<T> validatorObject;

    public static IEntityValidator<T> GetValidator(string entityType, ClientContext clientContext)
    {
        switch (entityType)
        {
            case "User":
                validatorObject = new UserValidator<T>(clientContext);
                break;
            case "Branch":
                validatorObject = new BranchValidator<T>(clientContext);
                break;
            default:
                validatorObject = null;
                break;
        }

        return validatorObject;
    }

    public static void Validate(T obj)
    {
        validatorObject.ValidateEntity(obj);
    }
}

在 ClientContext 中的 ValidateEntity 方法内

public partial class ClientContext : DbContext
{
    public ClientContext(string connectionString)
        : base(connectionString)
    {
    }

    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
    {
        var entityName = entityEntry.Entity.GetType().FullName; 

        IEntityValidator<T> validatorObj = (T)ValidatorLocator.GetValidator(entityName, this);


        return base.ValidateEntity(entityEntry, items);
    }
}

我在 ValidateEntity 方法中遇到编译器错误 - 在以下行:

IEntityValidator<T> validatorObj = (T)ValidatorLocator.GetValidator(entityName, this);

错误提示:使用泛型类型 'ValidatorLocator' 需要 1 个类型参数

返回我的泛型类型的具体实例的正确方法是什么?

【问题讨论】:

  • 要修复该错误,您需要执行ValidatorLocator&lt;T&gt;.GetValidator(...)。但是,您的代码无论如何都不会像这样工作。
  • 什么意思?你能指出这里有什么问题吗?
  • 那么在ValidateEntity 方法中,T 是什么?
  • 这将是一个不同的实体类型,例如,在上面的 BranchValidator 中,T 将是一个 Branch 对象,在 UserValidator 中,它将是一个 User 对象 - 两者都是 EF 实体
  • 是的,但是您的代码使用的是 T 而不是其中一种类型,您不能这样做。

标签: c# generics service-locator


【解决方案1】:

你可以使用依赖注入容器之类的东西:

public class ClientContext { }

public interface IEntityValidator<T> { }

public class Branch { }
public class BranchValidator : IEntityValidator<Branch>
{
    public BranchValidator(ClientContext clientContext)
    {
        Console.WriteLine("Creating a BranchValidator with context {0}", clientContext);
    }
}

public class Other { }
public class OtherValidator : IEntityValidator<Other> { }

public static class ValidatorLocator
{
    private static readonly Dictionary<Type, Type> ValidatorContainer
        = new Dictionary<Type, Type>();

    /// <summary>
    /// Use this method to register the validator class for an entity type.
    /// It should have a constructor with ClientContext parameter
    /// </summary>
    public static void RegisterValidator<TEntity,TEntityValidator>()
        where TEntityValidator : IEntityValidator<TEntity>
    {
        Console.WriteLine("=== RegisterValidator ===");
        Console.WriteLine("Registering {0} to validate {1}:", 
            typeof(TEntityValidator), typeof(TEntity));
        ValidatorContainer.Add(typeof(TEntity), typeof(TEntityValidator));
        Console.WriteLine("  Checking constructor with ClientContext parameter"); 
        var ctorParamTypes = new[] {typeof(ClientContext)};
        var validatorCtor = typeof(TEntityValidator).GetConstructor(ctorParamTypes);
        if (validatorCtor == null)
            Console.WriteLine("  Couldn't find the constructor!!");
        else
            Console.WriteLine("  Successfully registered!");
    }

    public static IEntityValidator<TEntity> GetValidator<TEntity>(ClientContext clientContext)
    {
        Console.WriteLine("=== GetValidator ===");
        Console.WriteLine("Getting validator for {0}", typeof(TEntity));
        var validatorType = ValidatorContainer[typeof(TEntity)];
        Console.WriteLine("  Looking for the constructor");
        var ctorParamTypes = new[] {typeof(ClientContext)};
        var validatorCtor = validatorType.GetConstructor(ctorParamTypes);
        Console.WriteLine("  Constructor found");
        var ctorParams = new[] {clientContext};
        Console.WriteLine("  Creating validator");
        var validator = validatorCtor.Invoke(ctorParams);
        Console.WriteLine("  Succesfully created");
        return (IEntityValidator<TEntity>)validator;
    }
}

要使用它,您必须为每个实体类型注册验证器类,然后您可以找到服务构造函数并调用它。这种技术有多种变化:例如,您可以为每个验证器注册一个单例实例并重用它,而不是在每次使用时查找构造函数并调用它。

注意:Console.WriteLine 显然可以查看发生了什么,但在实际实现中不是必需的

使用示例:

class Program
{
    static void Main(string[] args)
    {
        ValidatorLocator.RegisterValidator<Branch,BranchValidator>();
        ValidatorLocator.RegisterValidator<Other,OtherValidator>();

        var clientContex = new ClientContext();
        ValidatorLocator.GetValidator<Branch>(clientContext);

        Console.WriteLine("Press a key to exit!!");
        Console.ReadKey();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多