Добавить аннотации данных в класс, созданный платформой сущностей


94

У меня есть следующий класс, созданный структурой сущностей:

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

Я хочу сделать это поле обязательным

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

Однако, поскольку это сгенерированный код, он будет уничтожен. Я не могу представить себе способ создать частичный класс, потому что свойство определяется сгенерированным частичным классом. Как я могу определить ограничение безопасным способом?


Если ваше свойство - int, оно по умолчанию требуется для modelbinder, поэтому ваш атрибут [Required] ничего здесь не добавит.
Кирилл Бестемьянов

@KirillBestemyanov - @ Html.ValidationMessageFor (model => model.Item.Item.ResourceTypeID) должен выйти из строя на стороне клиента. Это не.
P.Brian.Mackey

Ответы:


144

Сгенерированный класс ItemRequestвсегда будет partialклассом. Это позволяет вам написать второй частичный класс, помеченный необходимыми аннотациями данных. В вашем случае частичный класс ItemRequestбудет выглядеть так:

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

        //...
    }
}

12
Частичный класс не будет восстановлен. Вот почему он определяется как частичный.
MUG4N

вы пропустили частичный модификатор? Вы используете то же пространство имен?
MUG4N

5
Пользователи .NET Core: используйте ModelMetadataType вместо MetadataType.
Боб Кауфман

1
Вы можете разместить частичный класс где угодно, если пространство имен идентично
MUG4N 06

40

Как ответил MUG4N, вы можете использовать частичные классы, но вместо этого лучше использовать интерфейсы . В этом случае у вас будут ошибки компиляции, если модель EF не соответствует модели проверки. Таким образом, вы можете изменять свои модели EF, не опасаясь, что правила проверки устарели.

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 Если вы используете тип проекта, который отличается от ASP.NET MVC (когда вы выполняете ручную проверку данных), не забудьте зарегистрировать свои валидаторы

/* Global.asax or similar */

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

Хорошее решение @dimonser, я также попытался добавить такие комментарии xml (для тех полей БД, которые нуждаются в небольшом пояснении в коде - т.е. для отображения в intellitype), но, похоже, это не работает. Есть идеи, как это сделать?
Перси

Привет, @Rick, вы можете оставить комментарий к свойству интерфейса, но вы увидите его только тогда, когда будете работать с переменной интерфейса. Или вы можете поместить комментарий в частичный класс. В этом случае вы увидите это, когда будете работать с экземпляром вашего класса. Других случаев нет. Таким образом, вы можете использовать оба из них, чтобы охватить все ситуации. В первом случае вы можете описать правила проверки полей, а во втором - попытаться описать цели,
dimonser

Действительно хорошо продуманный ответ, но я бы предпочел видеть ошибки компиляции, если проверка больше не синхронизируется с автоматически сгенерированным классом структуры сущностей. Я изо всех сил пытаюсь придумать ситуацию, когда вы можете захотеть проверить свойство, которое больше не присутствует в вашем классе фреймворка сущности.
Майк

1
У меня это не работает, в нем говорится, что мне нужно реализовать интерфейс IEntityMetadata ...
Worthy7,

14

Я нашел такое решение, как ответ MUG4N , но вместо этого вложилMetaData класс в класс сущности, тем самым уменьшив количество классов в вашем списке общедоступных пространств имен и устранив необходимость иметь уникальное имя для каждого класса метаданных.

using System.ComponentModel.DataAnnotations;

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

            //...
        }
    }
}

Я использую это во всем своем проекте. Намного проще организовать. Я также добавляю настраиваемые свойства, используя [NotMapped]внутри частичного класса, когда они мне нужны.
Картер Медлин

5

Это своего рода расширение ответа @dimonser, если вы регенерируете свою модель db, вам придется вручную повторно добавлять интерфейсы в эти классы.

Если у вас есть желание, вы также можете изменить свои .ttшаблоны:

Вот пример автогенерирования интерфейсов для некоторых классов, это фрагмент из вашего метода .ttзамены EntityClassOpeningна следующий (и, очевидно, var stringsToMatchс вашими именами сущностей и интерфейсами).

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

Ни один нормальный человек не должен поступать так с собой, ведь в Библии доказано, что за это человек попадает в ад.


2

Я не уверен, как сделать то, о чем вы просите, но есть способ обойти это. Динамическая проверка данных путем переопределения GetValidators вашего настраиваемого DataAnnotationsModelValidatorProvider. В нем вы можете прочитать правила проверки каждого поля (из базы данных, файла конфигурации и т. Д.) И при необходимости добавить валидаторы. Он имеет дополнительные преимущества в том, что ваша проверка больше не тесно связана с моделью и может быть изменена без необходимости даже перезапуска сайта. Конечно, для вашего случая это было бы излишним, но для нашего он был идеальным!


Мы сделали это, когда впервые реализовали эту структуру. С тех пор мы перешли на NHibernate, но это не имеет отношения к решению. Наш код проверки работал без изменений (был изменен только уровень доступа к данным).
JTM

1

Измените шаблон T4, добавив необходимые аннотации, этот файл обычно называется MODELNAME.tt

найдите, где T4 создает класс и методы, чтобы знать, где их разместить.

     <#=codeStringGenerator.IgnoreJson(navigationProperty)#>


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

            return result;
        }

Вам также нужно будет добавить пространства имен;

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

Восстановите свои классы, сохранив вашу модель, все ваши методы должны быть аннотированы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.