web-dev-qa-db-fra.com

Ajouter des annotations de données à une classe générée par la structure d'entité

J'ai la classe suivante générée par framework d'entité:

public partial class ItemRequest
{
    public int RequestId { get; set; }
    //...

Je voudrais en faire un champ obligatoire

[Required]
public int RequestId { get;set; }

Cependant, étant donné que ce code est généré, il sera effacé. Je ne peux pas imaginer un moyen de créer une classe partielle car la propriété est définie par la classe partielle générée. Comment définir la contrainte de manière sécurisée?

91
P.Brian.Mackey

La classe générée ItemRequest sera toujours une classe partial. Cela vous permet d'écrire une deuxième classe partielle qui est marquée avec les annotations de données nécessaires. Dans votre cas, la classe partielle ItemRequest ressemblerait à ceci:

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

//make sure the namespace is equal to the other partial class ItemRequest
namespace MvcApplication1.Models 
{
    [MetadataType(typeof(ItemRequestMetaData))]
    public partial class ItemRequest
    {
    }

    public class ItemRequestMetaData
    {
        [Required]
        public int RequestId {get;set;}

        //...
    }
}
138
MUG4N

En tant que MUG4N répondu, vous pouvez utiliser classes partielles mais il sera préférable d'utiliser interfaces à la place. Dans ce cas, vous aurez des erreurs de compilation si le modèle EF ne correspond pas au modèle de validation. Vous pouvez donc modifier vos modèles EF sans craindre que les règles de validation ne soient obsolètes.

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace YourApplication.Models
{
    public interface IEntityMetadata
    {
        [Required]
        Int32 Id { get; set; }
    }

    [MetadataType(typeof(IEntityMetadata))]
    public partial class Entity : IEntityMetadata
    {
        /* Id property has already existed in the mapped class */
    }
}

P.S. Si vous utilisez un type de projet différent de ASP.NET MVC (lorsque vous effectuez une validation manuelle des données), n'oubliez pas d'enregistrer vos validateurs.

/* Global.asax or similar */

TypeDescriptor.AddProviderTransparent(
    new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Entity), typeof(IEntityMetadata)), typeof(Entity));
39
dimonser

J'ai trouvé une solution comme celle de MUG4N, mais en imbriquant la classe MetaData dans la classe d'entité, réduisant ainsi le nombre de classes dans votre liste d'espaces de noms publics et éliminant ainsi le besoin d'avoir un nom unique pour chaque classe de métadonnées.

using System.ComponentModel.DataAnnotations;

namespace MvcApplication1.Models 
{
    [MetadataType(typeof(MetaData))]
    public partial class ItemRequest
    {
        public class MetaData
        {
            [Required]
            public int RequestId;

            //...
        }
    }
}
13
Carter Medlin

Ceci est en quelque sorte une extension de @dimonser answer si vous régénérez votre modèle de base de données, vous devrez rajouter manuellement des interfaces sur ces classes.

Si vous en avez l'estomac, vous pouvez aussi modifier votre .tt modèles:

Voici un exemple d’interfaces générées automatiquement sur certaines classes, il s’agit d’un fragment de .tt _ remplacez simplement la méthode EntityClassOpening dans le vôtre par (et évidemment var stringsToMatch avec vos noms d’entités et interfaces).

public string EntityClassOpening(EntityType entity)
{
    var stringsToMatch = new Dictionary<string,string> { { "Answer", "IJourneyAnswer" }, { "Fee", "ILegalFee" } };
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1}partial class {2}{3}{4}",
        Accessibility.ForType(entity),
        _code.SpaceAfter(_code.AbstractOption(entity)),
        _code.Escape(entity),
        _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)),
        stringsToMatch.Any(o => _code.Escape(entity).Contains(o.Key)) ? " : " + stringsToMatch.Single(o => _code.Escape(entity).Contains(o.Key)).Value : string.Empty);
}

Aucune personne normale ne devrait se faire cela cependant, il a été prouvé dans la Bible que l’on se rend en enfer pour cela.

5
Matas Vaitkevicius

Je ne sais pas comment faire ce que vous demandez, mais il existe un moyen de le contourner. Validation dynamique des données en remplaçant les GetValidators de votre DataAnnotationsModelValidatorProvider personnalisé. Vous pouvez y lire les règles de validation de chaque champ (depuis une base de données, un fichier de configuration, etc.) et ajouter des validateurs selon les besoins. Il a pour valeur supplémentaire que votre validation n'est plus étroitement liée au modèle et peut être modifiée sans avoir à redémarrer le site. Bien sûr, cela pourrait être exagéré pour votre cas, mais c'était idéal pour le nôtre!

2
JTMon

Modifiez le modèle T4 en ajoutant les annotations requises. Ce fichier est généralement nommé MODELNAME.tt.

trouvez où le T4 crée la classe et les méthodes pour savoir où les placer.

     <#=codeStringGenerator.IgnoreJson(navigationProperty)#>


//create this method in file
public string IgnoreJson(NavigationProperty navigationProperty){
            string result = navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? "" : @"[JsonIgnore]
    [IgnoreDataMember]";

            return result;
        }

Vous devrez également ajouter les espaces de noms.

<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using System.Runtime.Serialization;

Reconstruisez vos classes en enregistrant votre modèle. Toutes vos méthodes doivent être annotées.

1
tswales