Приведение с использованием переменной типа для быстрой проверки и пост-острого аспекта - PullRequest
1 голос
/ 21 января 2020

Я узнал от здесь , что приведение не может быть выполнено с помощью переменной типа.

Я использую плавную проверку для проверки сущности в моем проекте, и для этого я написал аспект, используя postsharp. Но я застрял где-то. В методе конструктора моего класса аспектов я получаю тип моего класса проверки. В методе Entry я динамически создаю пример этого класса и приводлю его к IValidator. Но так как я приведу к IValidator, я не могу передать параметр набора правил в метод Validate. Мне нужно разыграть IValidator. Это тоже невозможно. Как я должен go об этой проблеме? Я надеюсь, что мог сказать. Я добавляю коды ниже для примера. Спасибо.

[PSerializable]
public class FluentValidationAspect : OnMethodBoundaryAspect
{
    private readonly Type _validatorType;
    private readonly string _ruleSet;

    public FluentValidationAspect(Type validatorType, string ruleSet = null)
    {
        _validatorType = validatorType;
        _ruleSet = ruleSet;
    }

    public override void OnEntry(MethodExecutionArgs args)
    {
        var entityType = _validatorType.BaseType?.GetGenericArguments()[0];
        var entities = args.Arguments.Where(x => x.GetType() == entityType);


        if (string.IsNullOrEmpty(_ruleSet))
        {
            var validator = (IValidator)Activator.CreateInstance(_validatorType);
            foreach (var entity in entities)
                ValidatorTool.FluentValidate(validator, entity);
        }
        else
        {
            var validator = (IValidator<entityType>)Activator.CreateInstance(_validatorType);
            foreach (var entity in entities)
                ValidatorTool.FluentValidate<entityType>(validator, entity);
        }

    }
}

public static class ValidatorTool
{

    public static void FluentValidate(IValidator validator, object entity)
    {
        var result = validator.Validate(entity);
        if (result.Errors.Any())
            throw new ValidationException(result.Errors);
    }

    public static void FluentValidate<T>(IValidator<T> validator, T entity, string ruleSet) where T : class, new()
    {
        var result = validator.Validate(entity, ruleSet: ruleSet);
        if (result.Errors.Any())
            throw new ValidationException(result.Errors);
    }
}


1 Ответ

1 голос
/ 03 февраля 2020

У вас есть два варианта. Вы можете использовать рефлексию для вызова правильного экземпляра FluentValidate generi c метода, или вам нужен сам аспект типа generi c.

Последний вариант будет быстрее во время выполнения. В следующем коде используется IAspectProvider, который программно добавляет общий аспект c к целевому методу. Тип провайдера не должен быть аспектом (но может). Предоставленный аспект generi c не может быть производным от обычных типов, поскольку типы атрибутов не могут быть generi c, поэтому он реализует интерфейсы аспектов.

[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
[MulticastAttributeUsage(MulticastTargets.Method)]
public class FluentValidationAspect : MulticastAttribute, IAspectProvider
{
    private readonly Type _validatorType;
    private readonly string _ruleSet;

    public FluentValidationAspect(Type validatorType, string ruleSet = null)
    {
        _validatorType = validatorType;
        _ruleSet = ruleSet;
    } 

    public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
    {
        var entityType = _validatorType.BaseType?.GetGenericArguments()[0];
        var genericAspectType = typeof(FluentValidationAspect<>).MakeGenericType(entityType);

        yield return new AspectInstance(targetElement, new ObjectConstruction(genericAspectType, new object[] { _validatorType, _ruleSet }));
    }
}

[PSerializable]
public class FluentValidationAspect<T> : IMethodLevelAspect
{
    private Type _validatorType;
    private string _ruleSet;

    public FluentValidationAspect(Type validatorType, string ruleSet = null)
    {
        _validatorType = validatorType;
        _ruleSet = ruleSet;
    }

    public void RuntimeInitialize(MethodBase method)
    {
    }

    [SelfPointcut]
    [OnMethodEntryAdvice]
    public void OnEntry(MethodExecutionArgs args)
    {
        var entities = args.Arguments.Where(x => x is T).Select(x => (T)x);

        var validator = (IValidator<T>)Activator.CreateInstance(_validatorType);
        foreach (var entity in entities)
            ValidatorTool.FluentValidate(validator, entity);
    }
}

Обратите внимание, что во время выполнения не происходит отражения. Однако я хотел бы полностью удалить LINQ из OnEntry совета, чтобы повысить производительность.

...