Pergunta

Descrição

A minha solução tem esses projetos:

  • DAL = Modificado Entity Framework
  • DTO Transferência de objetos = Dados que são capazes de validar-se
  • BL = Negócios Camada de Serviços
  • WEB = apresentação de aplicação Asp.net MVC

DAL, BL e WEB toda referência DTO que é ótimo.
O processo geralmente executa desta maneira:

  1. A solicitação da web é feita para o WEB
  2. WEB recebe DTOs postadas
    • DTOs se automagicamente validado via ActionFilter personalizado
    • erros de validação são automaticamente coletadas
  3. (A validação é OK) chamadas web em BL fornecendo DTOs
  4. BL põe em DAL usando DTOs (pode passá-los através ou apenas usá-los)

problema DTO Validação então ...

Os meus DTOs são capazes de validar-se com base em seu próprio estado (valores Propriedades). Mas agora eu sou apresentado com um problema quando este não é o caso. Eu preciso deles para validar usando BL (e consequentemente DAL).

Meu exemplo da vida real : registros de usuários e WEB recebe um DTO usuário que fica validado. A parte problemática é a validação username. Sua singularidade deve ser verificado contra o armazenamento de dados.
Como é que eu vou fazer isso?

Não há informações adicionais que todos os DTOs implementar uma interface (ie. Implementos User DTO IUser) para fins COI e TDD. Ambos fazem parte do projeto DTO .

tentativas impossíveis

  1. Eu não pode fazer referência BL no DTO porque eu vou chegar referência circular.
    Compilation error
  2. Não consigo criar um projeto adicional DTO.Val que faria referência as classes DTO parciais e implementar a sua validação lá (eles referenciar BL + DTO).
    Partial classes can't span assemblies.

tentativas possíveis

  1. Criar um ActionFilter especial que iria validar objeto contra condições externas. Este seria criado dentro Project Web , assim, vendo DTO e BL que seria usado aqui.
  2. DTOs Coloque em BL e manter interfaces de DTO como DTOs reais referenciados por outros projetos e refazer todo o código para usar interfaces em vez de classes concretas.
  3. Não manuseie validação dependente externo e deixar dependências externas lançar uma exceção - provavelmente o pior solução para esta questão

O que você sugere?

Foi útil?

Solução 4

solução resultante

I acabou usando filtro acção controlador que era capaz de objecto validar contra factores externos que não podem ser obtidos a partir do próprio objecto.

Eu criei o filtro que leva o nome do parâmetro de ação para verificar e tipo de validador que irá validar esse parâmetro particular. É claro que esta validador tem de implementar determinada interface para fazer tudo reutilizável.

[ValidateExternalFactors("user", typeof(UserExternalValidator))]
public ActionResult Create(User user)

necessidades validador para implementar essa interface simples

public interface IExternalValidator<T>
{
    bool IsValid(T instance);
}

É uma solução simples e eficaz para um problema aparentemente complexa.

Outras dicas

Gostaria de sugerir uma experiência que eu só foram experimentação para a última semana ou assim.

Com base esta inspiração Estou criando DTOs que validam um pouco diferente para que da abordagem DataAnnotations. DTO Amostra:

public class Contact : DomainBase, IModelObject
{
    public int ID { get; set; }
    public string Name { get; set; }
    public LazyList<ContactDetail> Details { get; set; }
    public DateTime Updated { get; set; }


    protected override void ConfigureRules()
    {
        base.AddRule(new ValidationRule()
        {
            Properties = new string[] { "name" },
            Description = "A Name is required but must not exceed 300 characters in length and some special characters are not allowed",
            validator = () => this.Name.IsRequired300LenNoSpecial()
        });

        base.AddRule(new ValidationRule()
        {
            Properties = new string[] { "updated" },
            Description = "required",
            validator = () => this.Updated.IsRequired()
        });
    }
}

Isto pode parecer mais trabalho do que DataAnnotations e bem, isso é coz que é, mas não é enorme. Eu acho que é mais apresentável na classe (eu tenho algumas classes DTO realmente feias agora com atributos DataAnnotations - você não pode sequer ver as propriedades mais). E o poder de delegados anônimos nesta aplicação é quase reservar digno (por isso estou descobrindo).

classe Base:

public partial class DomainBase : IDataErrorInfo
{
    private IList<ValidationRule> _rules = new List<ValidationRule>();

    public DomainBase()
    {
        // populate the _rules collection
        this.ConfigureRules();
    }

    protected virtual void ConfigureRules()
    {
        // no rules if not overridden
    }

    protected void AddRule(ValidationRule rule)
    {
        this._rules.Add(rule);
    }





    #region IDataErrorInfo Members

    public string Error
    {
        get { return String.Empty; }    // Validation should call the indexer so return "" here
    }                                   // ..we dont need to support this property.

    public string this[string columnName]
    {
        get
        {
            // get all the rules that apply to the property being validated
            var rulesThatApply = this._rules
                .Where(r => r.Properties.Contains(columnName));

            // get a list of error messages from the rules
            StringBuilder errorMessages = new StringBuilder();
            foreach (ValidationRule rule in rulesThatApply)
                if (!rule.validator.Invoke())   // if validator returns false then the rule is broken
                    if (errorMessages.ToString() == String.Empty)
                        errorMessages.Append(rule.Description);
                    else
                        errorMessages.AppendFormat("\r\n{0}", rule.Description);

            return errorMessages.ToString();
        }
    }

    #endregion
}

ValidationRule e meu validação funções:

public class ValidationRule
{
    public string[] Properties { get; set; }
    public string Description { get; set; }
    public Func<bool> validator { get; set; }
}


/// <summary>
/// These extention methods return true if the validation condition is met.
/// </summary>
public static class ValidationFunctions
{
    #region IsRequired

    public static bool IsRequired(this String str)
    {
        return !str.IsNullOrTrimEmpty();
    }

    public static bool IsRequired(this int num)
    {
        return num != 0;
    }

    public static bool IsRequired(this long num)
    {
        return num != 0;
    }

    public static bool IsRequired(this double num)
    {
        return num != 0;
    }

    public static bool IsRequired(this Decimal num)
    {
        return num != 0;
    }

    public static bool IsRequired(this DateTime date)
    {
        return date != DateTime.MinValue;
    }

    #endregion


    #region String Lengths

    public static bool IsLengthLessThanOrEqual(this String str, int length)
    {
        return str.Length <= length;
    }

    public static bool IsRequiredWithLengthLessThanOrEqual(this String str, int length)
    {
        return !str.IsNullOrTrimEmpty() && (str.Length <= length);
    }

    public static bool IsRequired300LenNoSpecial(this String str)
    {
        return !str.IsNullOrTrimEmpty() &&
            str.RegexMatch(@"^[- \r\n\\\.!:*,@$%&""?\(\)\w']{1,300}$",
                RegexOptions.Multiline) == str;
    }

    #endregion

}

Se meus olhares código confuso Bem, isso é porque eu só tenho trabalhado sobre esta abordagem de validação para os últimos dias. Eu preciso esta idéia para atender alguns requisitos:

  • Eu preciso suportar a interface IDataErrorInfo por isso a minha camada de MVC valida automaticamente
  • Eu preciso ser capaz de suportar cenários de validação complexas (todo o ponto da sua pergunta eu acho): Eu quero ser capaz de validar contra várias propriedades no mesmo objeto (. Ie StartDate e FinishDate); propriedades de diferentes múltiplos / / objectos associados como I teria em um gráfico de objeto; e até mesmo outras coisas que eu não tenha pensado ainda.
  • Eu preciso apoiar a ideia de um erro ao aplicar para mais de uma propriedade
  • Como parte do meu TDD e viagem DDD eu quero meu objetos de domínio para descrever mais o meu 'domínio' do que os meus métodos de camada de serviço, de modo colocando essas condições complexas nos objetos de modelo (não DTOs) parece atingir este

Esta abordagem eu acho que vai ficar me o que eu quero, e talvez você também.

Eu imagino se você ir a bordo comigo sobre isso que seria muito 'por nós mesmos', mas pode valer a pena. Eu estava lendo sobre o novos recursos de validação em MVC 2 mas ainda não atende a lista de desejos acima sem modificação personalizada.

Espero que isso ajude.

O S # arp Architecture tem um [DomainSignature] identificador método que usado com o validador nível de classe [HasUniqueDomainSignature] vai fazer o trabalho. Consulte o código de exemplo abaixo:

[HasUniqueDomainSignature]
public class User : Entity
{
    public User()
    {
    }

    public User(string login, string email) : this()
    {
        Login = login;
        Email = email;
    }

    [DomainSignature]
    [NotNullNotEmpty]
    public virtual string Login { get; set; }

    [DomainSignature]
    public virtual string Email { get; set; }

}

Dê uma olhada em http://www.sharparchitecture.net/

Eu tive esse mesmo problema exato e depois tentar encontrar um trabalho em torno de dias e dias e dias, acabei fundindo minha DTO, DAL, e BL em uma biblioteca. Eu mantive a minha camada de apresentação separada. Não tenho certeza se isso é uma opção para você ou não. Para mim, eu percebi que minhas chances de sempre em mudança do armazenamento de dados eram muito ligeira, e assim a camada separada não era realmente necessário.

Eu também implementou o Microsoft Application Block Validation para todas as validações minha DTO. Eles têm um método "Self Validação" que permite executar validações complexas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top