Как я могу добавить свои атрибуты в свойства классов Linq2Sql, генерируемых кодом? - PullRequest
22 голосов
/ 26 декабря 2008

Я хотел бы добавить атрибуты в свойства классов Linq 2 Sql. Например, этот столбец доступен для просмотра в пользовательском интерфейсе или ReadOnly в пользовательском интерфейсе и так далее.

Я думал об использовании шаблонов, кто-нибудь знает, как его использовать? или что-то другое?

Вообще говоря, вы бы сделали, чтобы решить эту проблему с классами, генерируемыми кодом?

Ответы [ 6 ]

39 голосов
/ 20 марта 2009

Вы можете воспользоваться новыми функциями метаданных в System.ComponentModel.DataAnnotations, которые позволят нам отделить метаданные от существующей модели домена.

Например:

[MetadataType (typeof (BookingMetadata))]
public partial class Booking
{
 // This is your custom partial class     
}

public class BookingMetadata
{
 [Required] [StringLength(15)]
 public object ClientName { get; set; }

 [Range(1, 20)]
 public object NumberOfGuests { get; set; }

 [Required] [DataType(DataType.Date)]
 public object ArrivalDate { get; set; }
}
5 голосов
/ 27 декабря 2008

По запросу, вот подход, использующий CustomTypeDescriptor для редактирования атрибутов во время выполнения; пример здесь - win-формы, но это должно быть довольно просто перенести его в WPF, чтобы посмотреть, работает ли он ...

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
// example POCO
class Foo {
    static Foo()
    {   // initializes the custom provider (the attribute-based approach doesn't allow
        // access to the original provider)
        TypeDescriptionProvider basic = TypeDescriptor.GetProvider(typeof(Foo));
        FooTypeDescriptionProvider custom = new FooTypeDescriptionProvider(basic);
        TypeDescriptor.AddProvider(custom, typeof(Foo));
    }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
}
// example form
static class Program {
    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        Application.Run( new Form {
                Controls = {
                    new DataGridView {
                        Dock = DockStyle.Fill,
                        DataSource = new BindingList<Foo> {
                            new Foo { Name = "Fred", DateOfBirth = DateTime.Today.AddYears(-20) }
                        }
                    }
                }
            });
    }
}

class FooTypeDescriptionProvider : TypeDescriptionProvider
{
    ICustomTypeDescriptor descriptor;
    public FooTypeDescriptionProvider(TypeDescriptionProvider parent) : base(parent) { }
    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    {   // swap regular descriptor for bespoke (Foo) descriptor
        if (descriptor == null)
        {
            ICustomTypeDescriptor desc = base.GetTypeDescriptor(typeof(Foo), null);
            descriptor = new FooTypeDescriptor(desc);
        }
        return descriptor;
    }
}
class FooTypeDescriptor : CustomTypeDescriptor
{
    internal FooTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) { }
    public override PropertyDescriptorCollection GetProperties()
    {   // wrap the properties
        return Wrap(base.GetProperties());
    }
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {   // wrap the properties
        return Wrap(base.GetProperties(attributes));
    }

    static PropertyDescriptorCollection Wrap(PropertyDescriptorCollection properties)
    {
        // here's where we have an opportunity to swap/add/remove properties
        // at runtime; we'll swap them for pass-thru properties with
        // edited atttibutes
        List<PropertyDescriptor> list = new List<PropertyDescriptor>(properties.Count);
        foreach (PropertyDescriptor prop in properties)
        {
            // add custom attributes here...
            string displayName = prop.DisplayName;
            if (string.IsNullOrEmpty(displayName)) displayName = prop.Name;

            list.Add(new ChainedPropertyDescriptor(prop, new DisplayNameAttribute("Foo:" + displayName)));
        }
        return new PropertyDescriptorCollection(list.ToArray(), true);
    }
}


class ChainedPropertyDescriptor : PropertyDescriptor
{
    // this passes all requests through to the underlying (parent)
    // descriptor, but has custom attributes etc;
    // we could also override properties here...
    private readonly PropertyDescriptor parent;
    public ChainedPropertyDescriptor(PropertyDescriptor parent, params Attribute[] attributes)
        : base(parent, attributes)
    {
        this.parent = parent;
    }
    public override bool ShouldSerializeValue(object component) { return parent.ShouldSerializeValue(component); }
    public override void SetValue(object component, object value) { parent.SetValue(component, value); }
    public override object GetValue(object component) { return parent.GetValue(component); }
    public override void ResetValue(object component) { parent.ResetValue(component); }
    public override Type PropertyType {get { return parent.PropertyType; } }
    public override bool IsReadOnly { get { return parent.IsReadOnly; } }
    public override bool CanResetValue(object component) {return parent.CanResetValue(component);}
    public override Type ComponentType { get { return parent.ComponentType; } }
    public override void AddValueChanged(object component, EventHandler handler) {parent.AddValueChanged(component, handler);  }
    public override void RemoveValueChanged(object component, EventHandler handler) { parent.RemoveValueChanged(component, handler); }
    public override bool SupportsChangeEvents { get { return parent.SupportsChangeEvents; } }
}
1 голос
/ 26 декабря 2008

Вы также можете написать / использовать другой генератор кода вместо стандартного MSLinqToSQLGenerator.

Один из вариантов для начала - этот .

Я построил свой, в соответствии с моими потребностями. Я не знаю, есть ли способ указать, какие атрибуты должны быть размещены в каждом свойстве, с помощью DBML Designer или XML-файла, но, возможно, вы можете найти способ использовать эту альтернативу, чтобы помочь вам в вашей проблеме.

1 голос
/ 26 декабря 2008

Вы можете использовать частичный класс, чтобы ваша сущность реализовала интерфейс, который объявляет те же свойства вашей сущности, а затем помещает атрибуты в интерфейс.

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

Я не знаю, сможете ли вы использовать атрибуты таким образом, но вы можете попробовать что-то подобное.

Пример:


public interface IConcept {
    long Code { get; set; }
    [Unique]
    string Name { get; set; }
    bool IsDefault { get; set; }
}

public partial class Concept : IConcept { }

[Table(Name="dbo.Concepts")]
public partial class Concept
{
//...
}
1 голос
/ 26 декабря 2008

Это распространенная проблема с генерацией кода; хотя вы можете добавлять члены и атрибуты уровня class через дополнительный частичный класс, вы не можете добавлять атрибуты к сгенерированным членам. Чтобы компенсировать это, некоторые основанные на атрибутах механизмы позволяют вам указывать атрибуты в классе (называя члена), но не любые из тех, которые вы цитируете.

Один из вариантов хардкора - написать TypeDescriptionProvider, который предоставляет пользовательские PropertyDescriptor определения для свойств. Это позволит вам полностью контролировать метаданные, используемые инструментами привязки пользовательского интерфейса, такими как PropertyGrid, DataGridView и т. Д.

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

Примечание: если вы используете PropertyGrid, то не может установить свойства вручную, но вы можете написать TypeConverter, что немного меньше работы, чем полное TypeDescriptionProvider; просто наследуйте от ExpandableObjectConverter и переопределяйте GetProperties(). Вам все еще понадобится прокладка PropertyDescriptor, так что все еще нетривиально ...

1 голос
/ 26 декабря 2008

Вы можете рассмотреть возможность использования шаблонов T4 Дэмиена Гварда для Linq To Sql . Изменение его шаблонов, скорее всего, даст вам результаты, которые вы ищете.

Надеюсь, это поможет!

...