我想为Linq 2 Sql类属性添加属性。例如,此列可以在用户界面中浏览,也可以在UI中自读,目前为止。
我考虑过使用模板,有谁知道如何使用它?还是别的什么?
一般来说,您是否会使用代码生成的类来解决这个问题?
答案 0 :(得分:39)
您可以利用System.ComponentModel.DataAnnotations中的新元数据功能,这将允许我们将MetaData与现有域模型分开。
例如:
[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; }
}
答案 1 :(得分:5)
根据要求,这是一种使用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; } }
}
答案 2 :(得分:1)
您可能需要考虑使用Damien Guard's T4 templates for Linq To Sql。修改他的模板很可能会给你你想要的结果。
希望这有帮助!
答案 3 :(得分:1)
这是代码生成的常见问题;虽然您可以通过其他分部类添加成员和类级别属性,但您无法向生成的成员添加属性。为了补偿,一些基于属性的机制允许您指定类中的属性(命名成员),但不能指定您引用的任何属性。
一个核心选项是编写一个TypeDescriptionProvider
,为属性提供自定义PropertyDescriptor
定义。这将允许您完全控制UI绑定工具(如PropertyGrid
,DataGridView
等)使用的元数据。
但是,如果您也可以手动设置它们,那么设置一些UI属性可能太多了!但是如果你对追求这个选项感兴趣,请告诉我 - 这对我来说是一个熟悉的领域,但是如果你不想要的话,可以编写一个例子。
注意:如果您使用的是PropertyGrid
,那么无法手动设置属性,但您可以编写TypeConverter
,这样可以减少工作量而不是一个完整的TypeDescriptionProvider
;只需继承ExpandableObjectConverter
并覆盖GetProperties()
。你仍然需要一个垫片PropertyDescriptor
,所以仍然不是微不足道的......
答案 4 :(得分: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
{
//...
}
答案 5 :(得分:1)
您也可以编写/使用其他代码生成器来代替默认的MSLinqToSQLGenerator。
一个选项是this one。
根据我的需要,我建立了自己的。我不知道是否有办法使用DBML Designer或xml文件指示必须在每个属性中放置哪些属性,但也许您可以找到一种方法来使用此替代方法来帮助您解决问题。