Dodaj adnotacje danych do klasy wygenerowanej przez platformę jednostki

93

Mam następującą klasę wygenerowaną przez strukturę jednostki:

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

Chciałbym, aby to pole było wymagane

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

Jednak ponieważ jest to wygenerowany kod, zostanie on usunięty. Nie wyobrażam sobie sposobu na utworzenie klasy częściowej, ponieważ właściwość jest zdefiniowana przez wygenerowaną klasę częściową. Jak w bezpieczny sposób zdefiniować ograniczenie?

P. Brian Mackey
źródło
Jeśli twoja właściwość to int, jest ona domyślnie wymagana przez modelbinder, więc atrybut [Wymagany] nie doda tutaj niczego.
Kirill Bestemyanov
@KirillBestemyanov - @ Html.ValidationMessageFor (model => model.Item.Item.ResourceTypeID) powinien zawieść po stronie klienta. To nie.
P.Brian.Mackey

Odpowiedzi:

143

Wygenerowana klasa ItemRequestzawsze będzie partialklasą. Pozwala to na napisanie drugiej klasy częściowej, która jest oznaczona niezbędnymi adnotacjami danych. W twoim przypadku klasa częściowa ItemRequestwyglądałaby tak:

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;}

        //...
    }
}
MUG4N
źródło
11
Klasa częściowa nie zostanie zregenerowana. To jest powód, dla którego jest określany jako częściowy.
MUG4N
przegapiłeś częściowy modyfikator? Czy używasz tej samej przestrzeni nazw?
MUG4N
5
Użytkownicy .NET Core: użyj ModelMetadataType zamiast MetadataType.
Bob Kaufman
1
Możesz umieścić klasę częściową w dowolnym miejscu, o ile przestrzeń nazw jest identyczna
MUG4N
40

Jak odpowiedział MUG4N , możesz używać klas częściowych, ale zamiast tego lepiej będzie używać interfejsów . W takim przypadku wystąpią błędy kompilacji, jeśli model EF nie odpowiada modelowi walidacji. Możesz więc modyfikować modele EF bez obawy, że reguły walidacji są nieaktualne.

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 */
    }
}

PS Jeśli używasz typu projektu, który różni się od ASP.NET MVC (podczas ręcznej weryfikacji danych) nie zapomnij zarejestrować swoich walidatorów

/* Global.asax or similar */

TypeDescriptor.AddProviderTransparent(
    new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Entity), typeof(IEntityMetadata)), typeof(Entity));
dimonser
źródło
@dimonser fajne rozwiązanie, próbowałem dodawać komentarze xml, takie jak ten (dla tych pól DB, które wymagają małego wyjaśnienia w kodzie - tj. mają być wyświetlane w intellitype), ale wydaje się, że nie działa. Masz jakiś pomysł, jak to zrobić?
Percy
Cześć @Rick, możesz dodać komentarz do właściwości interfejsu, ale zobaczysz go tylko podczas pracy ze zmienną interfejsu. Możesz też umieścić komentarz w części zajęć. W takim przypadku zobaczysz to podczas pracy z instancją swojej klasy. Brak innych dostępnych przypadków. Możesz więc użyć obu z nich do pokrycia wszystkich sytuacji, w pierwszym przypadku możesz opisać zasady walidacji pola, aw drugim przypadku spróbować opisać cele
dimonser
Naprawdę dobrze przemyślana odpowiedź, ale wolałbym widzieć błędy kompilacji, jeśli walidacja nie jest już zsynchronizowana z automatycznie generowaną klasą struktury jednostki. Staram się wymyślić sytuację, w której możesz chcieć zweryfikować właściwość, która nie jest już obecna w twojej klasie frameworkowej jednostki.
Mike
1
To nie działa dla mnie, mówi, że muszę zaimplementować interfejs IEntityMetadata ...
Worthy7
14

Znalazłem rozwiązanie takie jak odpowiedź MUG4N , ale zamiast tego zagnieżdżając MetaDataklasę w klasie encji, zmniejszając w ten sposób liczbę klas na publicznej liście przestrzeni nazw i eliminując potrzebę posiadania unikalnej nazwy dla każdej klasy metadanych.

using System.ComponentModel.DataAnnotations;

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

            //...
        }
    }
}
Carter Medlin
źródło
Używałem tego w całym moim projekcie. Dużo łatwiejsze do zorganizowania. Dodam również właściwości niestandardowe, używając [NotMapped]wewnątrz klasy częściowej, gdy ich potrzebuję.
Carter Medlin
5

Jest to swego rodzaju rozszerzenie odpowiedzi @dimonser, jeśli zregenerujesz model bazy danych, będziesz musiał ręcznie ponownie dodać interfejsy do tych klas.

Jeśli masz do tego żołądek, możesz również zmodyfikować swoje .ttszablony:

Oto przykład automatycznego generowania interfejsów w niektórych klasach, jest to fragment z metody .ttzastąpienia EntityClassOpeningmetody w twojej następującą (i oczywiście var stringsToMatchz nazwami jednostek i interfejsami).

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

Jednak żaden normalny człowiek nie powinien sobie tego robić, zostało udowodnione w Biblii, że idzie się za to do piekła.

Matas Vaitkevicius
źródło
2

Nie jestem pewien, jak zrobić to, o co prosisz, ale można to obejść. Dynamiczne sprawdzanie poprawności danych przez zastąpienie GetValidators Twojego niestandardowego DataAnnotationsModelValidatorProvider. Możesz w nim przeczytać zasady walidacji każdego pola (z bazy danych, pliku konfiguracyjnego itp.) I dodać walidatory w razie potrzeby. Ma dodatkowe wartości, że walidacja nie jest już ściśle powiązana z modelem i można ją zmienić bez konieczności ponownego uruchamiania witryny. Oczywiście może to być przesada w twoim przypadku, ale w naszym przypadku było idealne!

JTMon
źródło
Zrobiliśmy, kiedy po raz pierwszy wdrożyliśmy tę strukturę. Od tego czasu przeszliśmy na NHibernate, ale nie ma to wpływu na rozwiązanie. Nasz kod walidacyjny działał bez zmian (zmieniono tylko warstwę dostępu do danych).
JTM
1

Zmodyfikuj szablon T4, dodając wymagane adnotacje, plik ten zwykle nosi nazwę NAZWA MODELU.tt

dowiedzieć się, gdzie T4 tworzy klasę i metody, aby wiedzieć, gdzie je umieścić.

     <#=codeStringGenerator.IgnoreJson(navigationProperty)#>


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

            return result;
        }

Będziesz także musiał dodać przestrzenie nazw;

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

Przebuduj swoje klasy, zapisując model, wszystkie metody powinny być opatrzone adnotacjami.

tswales
źródło