Создать экземпляр универсального класса с правильным конкретным типом - PullRequest
0 голосов
/ 13 мая 2019

Я хочу реализовать общий механизм проверки сущности поверх платформы проверки 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);
    }
}

внутри метода ValidateEntity в ClientContext

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 - onследующая строка:

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

Ошибка говорит: Использование универсального типа 'ValidatorLocator' требует аргументов 1 типа

Как правильно вернуть конкретный экземплярмоего родового типа?

1 Ответ

0 голосов
/ 13 мая 2019

Вы можете использовать что-то вроде контейнера внедрения зависимостей:

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