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

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

        //...
    }
}
MUG4N
источник
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. В нем вы можете прочитать правила проверки каждого поля (из базы данных, файла конфигурации и т. Д.) И при необходимости добавить валидаторы. Он имеет дополнительные преимущества в том, что ваша проверка больше не тесно связана с моделью и может быть изменена без необходимости даже перезапуска сайта. Конечно, для вашего случая это было бы излишним, но для нашего он был идеальным!

JTMon
источник
Мы сделали это, когда впервые реализовали эту структуру. С тех пор мы перешли на 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;

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

Цвалес
источник