Связать атрибут с кодом, созданным свойством в .net - PullRequest
17 голосов
/ 19 января 2009

Я хочу установить атрибут для открытого свойства в .NET, однако у меня нет доступа к самому явному свойству, так как это был код, созданный в другом файле.

У меня есть это поле:

public virtual string Name { get; set; }

Я хочу установить это:

[ValidateNonEmpty("Name is required", ExecutionOrder = 1)]
public virtual string Name { get; set; }

Мой класс помечен как частичный, но вы не можете иметь частичные свойства. Мне показалось, что я кое-что понял с классом MetadataType, который является новой функцией Dynamic Data и DataAnnotations, но, увы, я чувствую, что его можно использовать только с Dynamic Data, верно?

Цитирование: http://blogs.oosterkamp.nl/blogs/jowen/archive/2008/10/16/metadatatype-attribute.aspx http://blogs.msdn.com/davidebb/archive/2008/06/16/dynamic-data-and-the-associated-metadata-class.aspx

Можно ли как-нибудь установить эти атрибуты (даже через web.config!), Не касаясь сгенерированного кода класса?

Заранее спасибо, Graham

Ответы [ 4 ]

23 голосов
/ 19 января 2009

Это известная неприятность; вы просто не можете добавить метаданные к сгенерированным членам.

Здесь есть 6 вариантов (в порядке возрастания усилия):

  • если вы владеете атрибутом, сделайте возможным объявить его в отношении класса, например: [ValidateNonEmpty("Name", "Name is required", ExecutionOrder = 1)] - затем добавьте несколько атрибутов в определение частичного класса
  • используйте виртуальный метод / interface / etc для запроса этого, а не через атрибуты
  • подкласс сгенерированного типа; переопределить или повторно объявить участника, добавив метаданные (очень грязно)
  • использовать пользовательский TypeDescriptionProvider для предоставления динамических метаданных (много и много работы) - при условии, что потребитель уважает TypeDescriptor; большинство связанных с привязкой потребителей это делают, но, например, Expression (используется многими поставщиками LINQ) не
  • изменить генератор кода / написать свой
  • попробуйте расширить что-то вроде PostSharp, чтобы выполнить эту работу (я не нашел способа сделать это, но мне нравится слышать, если вы найдете способ!)

У меня обычно есть успех с первым вариантом, если это не системный атрибут ([DisplayName] и т. Д.). Если [ValidateNonEmpty] определяется динамическими данными, вы не сможете этого сделать.

6 голосов
/ 16 октября 2013

Поскольку сгенерированный класс является частичным классом, должно работать следующее:

  1. Создайте интерфейс, в котором объявлено это свойство, и украсьте его атрибутом ValidateNonEmpty.
  2. Создайте собственный частичный класс с тем же именем, что и у класса AutoGenerated, и заставьте его реализовать интерфейс, который вы только что создали.
  3. Свойство теперь должно быть украшено этим атрибутом

Например:

// Decorate the properties with attributes as required
public interface IMyInterface
{
    [ValidateNonEmpty("Name is required")]
    string Name { get; set; }
}

// This is the partial class I created, that implements the interface
public partial class MyGeneratedClass : IMyInterface
{    
}

// This is the auto-generated class
public partial class MyGeneratedClass
{
    public virtual string Name { get; set; }
}

Я получил эту идею от geekswithblogs .

2 голосов
/ 07 мая 2014

Это отличное решение, но оно не сработало для моей проблемы. Я использую EF 6 с классами, сгенерированными вначале из существующей базы данных. Одним из столбцов в таблице является IDENTITY с автоматически генерируемыми значениями. Однако сгенерированный частичный класс не предоставил атрибут [DatabaseGenerated (DatabaseGeneratedOption.Identity)] , необходимый для того, чтобы база данных генерировала ключ. В результате возникает ошибка « Невозможно вставить явное значение для столбца идентификаторов в таблице« mytable », если для параметра IDENTITY_INSERT установлено значение OFF. ». Я попробовал ваше решение, но оно не сработало. Но если я добавлю атрибут в исходный сгенерированный класс, он будет работать. Поэтому я все еще пытаюсь найти решение, которое не требует изменения автоматически сгенерированного файла.

Вот код, который я пробовал, используя ваше решение:

public interface IMyTable
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    int ID { get; set; }
}

public partial class MyTable : IMyTable
{
}

оригинальный сгенерированный код:

[Table("MyTable")]
public partial class MyTable
{
    [Key]
    [Column(Order = 1)]
    public int ID { get; set; }
}
1 голос
/ 15 ноября 2012

Другой вариант - заключить свойства в не сгенерированные свойства в том же классе. Не идеально, потому что у вас могут быть двойные свойства, но если вы можете заставить свой генератор создавать защищенные свойства, это был бы довольно хороший подход.

Просто пришлось разобраться с этой проблемой: Entity Framework генерирует классы, я хочу сериализовать их в JSON с более простыми именами.

// GENERATED BY EF
public partial class ti_Users
{
    public ti_Users()
    {
        this.ti_CardData = new HashSet<ti_CardData>();
        this.ti_Orders = new HashSet<ti_Orders>();
    }

    protected int userId { get; set; }
    protected string userName { get; set; }
    protected string userEmail { get; set; }
    protected string userPassHash { get; set; }
    protected Nullable<System.DateTime> userLastLogin { get; set; }
    protected string userLastIP { get; set; } 

    public virtual ICollection<ti_CardData> ti_CardData { get; set; }
    public virtual ICollection<ti_Orders> ti_Orders { get; set; }
}

и дополнительный класс:

[JsonObject(memberSerialization: MemberSerialization.OptIn)]
public partial class ti_Users
{
    [JsonProperty]
    public int UserId
    {
        get { return this.userId; }
        set { this.userId = value; }
    }

    [JsonProperty]
    public string Name
    {
        get { return this.userName; }
        set { this.userName = value; }
    }

    [JsonProperty]
    public string Email
    {
        get { return this.userEmail; }
        set { this.userEmail = value; }
    }

    [JsonProperty]
    public string PassHash
    {
        get { return this.userPassHash; }
        set { this.userPassHash = value; }
    }
} 
...