23

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

我考虑过使用模板,有人知道如何使用它吗?或不同的东西?

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

4

6 回答 6

39

您可以利用 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; }
}
于 2009-03-20T17:21:34.467 回答
5

根据要求,这是一种使用 aCustomTypeDescriptor在运行时编辑属性的方法;这里的例子是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; } }
}
于 2008-12-27T16:38:37.540 回答
1

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

希望这可以帮助!

于 2008-12-26T11:15:51.000 回答
1

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

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

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

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

于 2008-12-26T11:25:17.267 回答
1

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

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

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

例子:


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
{
//...
}
于 2008-12-26T14:25:41.457 回答
1

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

一个开始的选择是这个

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

于 2008-12-26T14:39:48.997 回答