【问题标题】:How can I add my attributes to Code-Generated Linq2Sql classes properties?如何将我的属性添加到代码生成的 Linq2Sql 类属性?
【发布时间】:2010-09-28 11:10:12
【问题描述】:

我想向 Linq 2 Sql 类属性添加属性。比如这个 Column 在 UI 中是可浏览的,或者在 UI 中是只读的等等。

我考虑过使用模板,有人知道怎么用吗?还是别的什么?

一般来说,您会通过代码生成的类来解决这个问题吗?

【问题讨论】:

    标签: c# linq-to-sql attributes code-generation


    【解决方案1】:

    您可以利用 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; }
    }
    

    【讨论】:

    • 有趣,我想看看。谢谢
    • 我认为这是解决方案。
    • 我同意。这是最好的解决方案。
    • 是的,这就是炸弹 - 很好的答案!
    • 注意:MetadataClass 中的属性实际上并没有直接添加到目标类中。例如,如果您在 Booking.ClientName 上运行 MemberInfo.IsDefined() 以查找所需属性,您将找不到它。似乎使用 MetadataType 应用属性的库必须在目标类上查找类级别的 MetadataType 属性,如果找到,则在元类中搜索任何相关属性。我很失望我无法定义自定义属性,使用 MetadataType 应用它们,并使用 Member.IsDefined() 检测它们。为此,我认为您需要 Marc 的解决方案。
    【解决方案2】:

    根据要求,这是一种使用CustomTypeDescriptor 在运行时编辑属性的方法;这里的例子是win-forms,但是应该很简单的把它换成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; } }
    }
    

    【讨论】:

    • 马克谢谢你的帮助!我仍然不确定,对不起我这么厚。因此,在此示例中,如果我想将“名称”属性设为只读且仅此列,您需要做什么?在 Wrap 方法中执行 "if prop.DisplayName == "Name" ...?
    • 好吧,您应该能够添加 ReadOnlyAttribute(true) - 但是,在这种情况下,覆盖 IsReadOnly 可能更容易。您可能希望为此添加特定的 ChainedPropertyDescriptor 子类。
    • 如果我使用您的代码并获得堆栈溢出异常,我做错了什么?我刚刚用我的班级替换了 Foo,没有别的。溢出是由以下调用链引起的: Wrap(base.GetProperties()); -> GetTypeDescriptor(Type objectType, object instance)里面的行返回描述符;再次调用具有 Wrap(base.GetProperties()); 的 GetProperties()
    • 变量描述符始终为空。为什么?
    • @lorddarkangel 我需要看一个例子;我已经仔细检查过,发布的代码工作正常。 descriptor 字段在第 48 行获得非空值
    【解决方案3】:

    您可能需要考虑使用Damien Guard's T4 templates for Linq To Sql。修改他的模板很可能会给你想要的结果。

    希望这会有所帮助!

    【讨论】:

    • 当您想应用相同的属性和相同的参数时,这很好。如果我有 UI 的 Visibility 属性:对于 ID 列,我会将其分配为不可见作为参数,但对于 Name 列,我会将其分配为可见。
    【解决方案4】:

    这是代码生成的常见问题;虽然您可以通过附加的部分类添加成员和 class 级别的属性,但您不能向生成的成员添加属性。作为补偿,一些基于属性的机制允许您在类中指定属性(命名成员),但不能指定您引用的任何属性。

    一个核心选​​项是编写一个TypeDescriptionProvider,为属性提供自定义PropertyDescriptor 定义。这将允许您完全控制 UI 绑定工具(如 PropertyGridDataGridView 等)使用的元数据。

    但是,如果您也可以手动设置它们,那么简单地设置一些 UI 属性可能是太多的工作!但是,如果您有兴趣追求该选项,请告诉我 - 这是我熟悉的领域,但如果您不想要它,代码太多,无法编写示例。

    注意:如果你使用PropertyGrid,那么你不能手动设置属性,但是你可以写一个TypeConverter,这比一个完整的@要少一些工作987654328@;只需从ExpandableObjectConverter 继承并覆盖GetProperties()。你仍然需要一个垫片PropertyDescriptor,所以仍然不是微不足道的......

    【讨论】:

    • Marc,我正在使用 WPF 数据网格,codeplex.com/wpf。我认为他们不支持 TypeDescriptionProvider。 ://
    • Marc,到目前为止,我一直在调查您的方法,如果您能抽出时间提供帮助,我将不胜感激 :)
    • 如果它支持常规绑定钩子,它应该可以工作。我看看能不能举个例子……
    【解决方案5】:

    您可以使用分部类使您的实体实现一个接口,该接口声明您的实体的相同属性,然后将属性放在接口上。

    这样你就可以使用接口类型从属性中获取属性了。

    我不知道您是否可以通过这种方式使用属性,但您可以尝试类似的方法。

    例子:

    
    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
    {
    //...
    }
    

    【讨论】:

      【解决方案6】:

      您还可以编写/使用另一个代码生成器来代替默认的 MSLinqToSQLGenerator。

      一个开始的选项是this one

      我根据自己的需要构建了自己的。我不知道是否有一种方法可以使用 DBML Designer 或 xml 文件指示必须在每个属性中放置哪些属性,但也许您可以找到一种方法来使用这种替代方法来帮助您解决问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-22
        • 1970-01-01
        相关资源
        最近更新 更多