As interfaces fluentes são mais flexíveis que os atributos e por quê?

15

Em um tutorial do primeiro código EF 4.1, é fornecido o seguinte código:

public class Department
{
    public int DepartmentId { get; set; }
    [Required]
    public string Name { get; set; }
    public virtual ICollection<Collaborator> Collaborators { get; set; }
}

Em seguida, é explicado que a interface fluente é mais flexível:

As anotações de dados são definitivamente fáceis de usar, mas é preferível usar uma abordagem programática que ofereça muito mais flexibilidade.

O exemplo do uso da interface fluente é então dado:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Department>().Property(dp => dp.Name).IsRequired();
    modelBuilder.Entity<Manager>().HasKey(ma => ma.ManagerCode);
    modelBuilder.Entity<Manager>().Property(ma => ma.Name)
        .IsConcurrencyToken(true)
        .IsVariableLength()
        .HasMaxLength(20);
}

Não consigo entender por que a interface fluente é supostamente melhor. É realmente? Na minha perspectiva, parece que as anotações de dados são mais claras e têm uma sensação semântica mais clara.

Minha pergunta é por que uma interface fluente seria uma opção melhor do que usar atributos, especialmente neste caso?

(Nota: sou bastante novo em todo o conceito de interfaces fluentes; portanto, não espere conhecimento prévio sobre isso).

Referência: http://codefirst.codeplex.com/

Tjaart
fonte
Esta questão não é realmente sobre interfaces fluentes. A diferença está entre usar atributos e código normal. Se o código não fosse fluente, não mudaria muito em sua pergunta.
svick
As interfaces fluentes do @svick são basicamente código normal, mas o expressam de uma maneira diferente. Nós deixamos de especificar coisas no código simples para atributos, agora, com interfaces fluentes, parece que alguns estão voltando atrás e voltando a especificar as coisas no código novamente. Eu só quero entender por que você usaria código em vez de atributos. As interfaces fluentes justificam o afastamento dos atributos e a codificação de tudo novamente?
Tjaart

Respostas:

13

As anotações de dados são estáticas, por exemplo, esta declaração de método não pode ser alterada em tempo de execução:

  [MinLength(5)]
  [MaxLength(20,ErrorMessage="Le nom ne peut pas avoir plus de 20 caractères")]
  public new string Name { get; set; }

A interface fluente pode ser dinâmica:

   if (longNamesEnabled)
   {
      modelBuilder.Entity<Manager>().Property(ma => ma.Name)
        .HasMaxLength(100);
   }
   else
   {
      modelBuilder.Entity<Manager>().Property(ma => ma.Name)
        .HasMaxLength(20);
   }

para não mencionar o código pode ser reutilizado entre propriedades.

Garrett Hall
fonte
2
por que você acha que o comprimento (ou qualquer outra propriedade) da mesma propriedade mudaria em tempo de execução?
Yusubov
1
@ElYusubov: Eu começaria com os cenários em que não conhecia o comprimento do campo no momento da codificação.
Wyatt Barnett
@ WyattBarnett: pode fazer sentido ter o tamanho do campo como uma variável apenas quando os parâmetros do domínio são buscados dinamicamente a partir de algum serviço ou fonte externa não digitada. No entanto, lidar dinamicamente com propriedades de domínio exigiria uma abordagem de codificação defensiva.
Yusubov
1
@ElYusubov, você pode ter duas propriedades que precisam ser exatamente iguais, exceto pelo comprimento, então eu as passo para uma função que as define dinamicamente. É por isso que o autor os chamou de mais flexíveis.
Garrett Hall
1
@ElYusubov, você pode tornar o tamanho do campo uma configuração nas configurações do projeto, que alimenta o app.config ou web.config. Então, se o comprimento de um campo do banco de dados for alterado, você poderá alterar o comprimento no arquivo .config sem recompilar e reimplementar o aplicativo.
Kyralessa 8/08/12
8

Não acho que essa declaração deva ser aplicada de maneira ampla; é muito específico para o Code First. No Code First, as anotações de dados incluem apenas um subconjunto da funcionalidade disponível na API fluente. Em outras palavras, existem certas configurações de modelo que só podem ser feitas usando a API fluente.

Por exemplo, aqui estão algumas das coisas que não podem ser especificadas usando as anotações:

  • A precisão de uma propriedade DateTime
  • A precisão e a escala das propriedades numéricas
  • Uma propriedade String ou Binary como comprimento fixo
  • Uma propriedade String como não unicode
  • O comportamento ao excluir dos relacionamentos
  • Estratégias avançadas de mapeamento

Pessoalmente, costumo usar as anotações de dados relacionadas à validação sempre que possível, pois outras tecnologias como o MVC também podem tirar proveito delas. Para todo o resto, prefiro a API fluente.

bricelam
fonte
Você pode dar um exemplo do que só pode ser feito usando a API fluente? Também seria interessante saber por que eles escolheram fazer dessa maneira. Estou tentando entender APIs fracas versus métodos mais convencionais e estrutura de entidades é apenas um exemplo. Quero saber por que eles preferem isso a atributos. Para mim, os atributos parecem mais corretos e legíveis.
Tjaart
1
@ Tjaart Adicionei alguns exemplos. Ao projetar isso, havia dois princípios motivadores principais. Primeiro, permita que os desenvolvedores escolham. Algumas pessoas vêem os atributos como uma violação do POCO, outras, como sua natureza declarativa. Segundo, aproveite os atributos existentes e introduza apenas novos para cenários comuns. Você provavelmente concordará que os exemplos que dei acima são relativamente incomuns.
Bricelam 08/08/2012
Percebi que o comportamento do OnDelete parece estar disponível apenas na API fluente. Você pode pensar por que eles escolheram fazer dessa maneira? Isso é realmente o que estou tentando entender com essa pergunta. A violação do POCO pode ser um bom motivo se você estiver compartilhando as classes entre os projetos. Você pode acabar atraindo uma dependência de estrutura de entidade que não deseja se usar atributos!
Tjaart
@ Tjaart, não me lembro das razões exatas. Entrei para a equipe no final do recurso Code First e não estava aqui pelo design. Vou ver se consigo convencer alguém da equipe a pesar.
Bricelam
1

A resposta à sua pergunta é fornecida no link.

Em seguida, defina suas restrições aplicáveis ​​ao seu domínio dentro deste método programaticamente.

Basicamente, é mais ou menos preferência usar Atributos versus abordagem programática, em que a abordagem programática tem mais controle sobre a entidade. No entanto, existe uma maneira personalizada de adicionar Atributos para decorar seu modelo que você também deve ter.

Ao usar essa abordagem, você pode até descrever as relações entre tabelas e colunas. Resumindo, se você deseja ter um controle mais refinado sobre seu domínio, pode usar esta nova abordagem que vem com o EF4.1.

No entanto, para cenários comuns de validação, a aplicação de Atributos deve funcionar bem, pois é robusta para cobrir a maioria dos casos; Além disso, você pode economizar tempo.

Yusubov
fonte
Você pode ilustrar como a maneira programática lhe dá mais controle? Eu realmente não estou conseguindo neste momento.
Tjaart
Por exemplo, considere ".IsConcurrencyToken (true)" - como você faria isso em uma definição de atributo?
Yusubov
[ConcurrencyCheck] <- o que na verdade parece mais simples
Tjaart 1/08/2012
boa captura, como você descreveria "relações entre tabelas e colunas"?
Yusubov
[ForeignKey ("PersonId")] <- assim, o que provavelmente não é tão simples quanto .HasForeignKey (t => t.ProjectId), embora tudo o que seja necessário seja permitir que ForeignKey () use uma lambda exatamente como a interface fluente. Ainda não explica por que um é melhor que o outro.
Tjaart
0

Meu pensamento é que eles recomendem a API fluente para as primeiras implementações de código, porque você descreve explicitamente como os relacionamentos são criados no banco de dados. Se você usar anotações de dados, o banco de dados criado pelo Entity Framework pode não ser o que você espera. Seu exemplo inicial é muito simples, então, como você, eu usaria apenas o método de anotação de dados.

str8killinit
fonte
Você pode dar um exemplo de que o banco de dados não é o que você espera e como uma interface fluente impede que isso aconteça?
Tjaart