Add data annotations to a class generated by entit

2019-01-02 14:37发布

I have the following class generated by entity framework:

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

I would like to make this a required field

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

However, because this is generated code this will get wiped out. I can't imagine a way to create a partial class because the property is defined by the generated partial class. How can I define the constraint in a safe way?

6条回答
余生无你
2楼-- · 2019-01-02 15:18

As MUG4N answered you can use partial classes but will be better use interfaces instead. In this case you will have compilation errors if EF model doesn't correspond to validation model. So you can modify your EF models without fear that validation rules are outdated.

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. If you are using project type which is differ from ASP.NET MVC (when you perform manual data validation) don't forget to register your validators

/* Global.asax or similar */

TypeDescriptor.AddProviderTransparent(
    new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Entity), typeof(IEntityMetadata)), typeof(Entity));
查看更多
梦寄多情
3楼-- · 2019-01-02 15:21

I am not sure how to do what you are asking for but there is a way around it. Dynamic data validation by overriding the GetValidators of your custom DataAnnotationsModelValidatorProvider. In it you can read the rules for validating each field (from a database, config file, etc.) and add validators as need be. It has the added values that your validation is no longer tightly coupled to the model and can be changed without need to even restart the site. Of course it might be overkill for your case, but it was ideal for ours!

查看更多
余欢
4楼-- · 2019-01-02 15:24

I found a solution like MUG4N's answer, but instead, nesting the MetaData class within the entity class, thereby reducing the number of classes in your public namespace list, and eliminating the need to have a unique name for each metadata class.

using System.ComponentModel.DataAnnotations;

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

            //...
        }
    }
}
查看更多
情到深处是孤独
5楼-- · 2019-01-02 15:24

This is sort of extension to @dimonser answer if you regenerate your db model you will have to manually re-add interfaces on those classes.

If you have stomach for it you can also modify your .tt templates:

Here's example of auto-generating interfaces on some classes, this is fragment from .tt just replace EntityClassOpening method in yours with following (and obviously var stringsToMatch with your entity names and 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);
}

No normal person should do this to himself though, it has been proven in Bible that one goes to Hell for this.

查看更多
浪荡孟婆
6楼-- · 2019-01-02 15:24

Modify the T4 template adding required annotations, this file is usually named MODELNAME.tt

find where the T4 is creating the class and methods to know where to put these.

     <#=codeStringGenerator.IgnoreJson(navigationProperty)#>


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

            return result;
        }

You will also need to add the namespaces;

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

Rebuild your classes by saving your model, all your methods should be annotated.

查看更多
与君花间醉酒
7楼-- · 2019-01-02 15:28

The generated class ItemRequest will always be a partial class. This allows you to write a second partial class which is marked with the necessary data annotations. In your case the partial class ItemRequest would look like this:

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

        //...
    }
}
查看更多
登录 后发表回答