Вы можете использовать службы TypeDescriptor
для расширения класса модели с вычисленными свойствами.
Для этого вам понадобятся некоторые вспомогательные классы.
Во-первых, общий класс для пользовательских вычисленной собственности:
public class CalculatedProperty<TComponent, TValue> : PropertyDescriptor
{
private Func<TComponent, TValue> func;
public CalculatedProperty(string name, Func<TComponent, TValue> func)
: base(name, null)
{
this.func = func;
}
public override Type ComponentType { get { return typeof(TComponent); } }
public override bool IsReadOnly { get { return true; } }
public override Type PropertyType { get { return typeof(TValue); } }
public override bool CanResetValue(object component) { return false; }
public override object GetValue(object component) { return func((TComponent)component); }
public override void SetValue(object component, object value) { throw new InvalidOperationException(); }
public override bool ShouldSerializeValue(object component) { return false; }
public override void ResetValue(object component) { throw new InvalidOperationException(); }
}
и завод (чтобы сделать его проще в использовании):
public static class CalculatedProperty
{
public static PropertyDescriptor Create<TComponent, TValue>(string name, Func<TComponent, TValue> func)
{
return new CalculatedProperty<TComponent, TValue>(name, func);
}
}
Далее, для того, чтобы «добавить» свойства к существующему классу вам нужен класс, который реализует интерфейс ICustomTypeDescriptor
и выставляет его через пользовательский TypeDescriptionProvider
. Процесс немного сложнее, так что я воплощен в следующий класс (ов):
public class CustomPropertyTypeDescriptor : CustomTypeDescriptor
{
public static void Register(Type type, params PropertyDescriptor[] customProperties)
{
var baseProvider = TypeDescriptor.GetProvider(type);
var typeDescriptor = new CustomPropertyTypeDescriptor(baseProvider.GetTypeDescriptor(type), customProperties);
TypeDescriptor.AddProvider(new Provider(baseProvider, typeDescriptor), type);
}
PropertyDescriptor[] customProperties;
private CustomPropertyTypeDescriptor(ICustomTypeDescriptor baseDescriptor, PropertyDescriptor[] customProperties)
: base(baseDescriptor)
{
this.customProperties = customProperties;
}
public override PropertyDescriptorCollection GetProperties() { return GetProperties(null); }
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return new PropertyDescriptorCollection(base.GetProperties(attributes).Cast<PropertyDescriptor>().Concat(customProperties).ToArray());
}
private class Provider : TypeDescriptionProvider
{
private CustomPropertyTypeDescriptor typeDescriptor;
public Provider(TypeDescriptionProvider baseProvider, CustomPropertyTypeDescriptor typeDescriptor)
: base(baseProvider)
{
this.typeDescriptor = typeDescriptor;
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return typeDescriptor;
}
}
}
Это все для общей части. В конце всего вам нужно позвонить CustomPropertyTypeDescriptor.Register
один раз при запуске приложения для каждого класса, которому нужны рассчитанные свойства, предоставляя им метод CalculatedProperty.Create
.
Вот пример:
Модель: (Примечание нет Total
собственности)
public class OrderLine
{
public string ItemNo { get; set; }
(many other properties here...)
public int Quantity { get; set; }
public decimal Price { get; set; }
}
Применение:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
CustomPropertyTypeDescriptor.Register(typeof(OrderLine),
CalculatedProperty.Create("Total", (OrderLine source) => source.Quantity * source.Price)
);
var form = new Form();
var dg = new DataGridView { Dock = DockStyle.Fill, Parent = form };
dg.DataSource = Enumerable.Range(1, 10).Select(n => new OrderLine
{
ItemNo = "Item#" + n,
Quantity = n,
Price = 10 * n
}).ToList();
Application.Run(form);
}
}
Результат: (Обратите внимание, Total
co lumn)
![enter image description here](https://i.stack.imgur.com/6zBNK.png)
По крайней мере, вы можете использовать частичные классы моделей. –
Но что же вы подразумеваете под автосозданием служебных прокси-классов? Как вы используете службы ASP.NET Web Api? Или вы используете службы WCF? –
@Reza Aghei Вы можете добавить ссылку на службу в Visual Studio для службы веб-API, которая реализует ODATA v3 так же, как и с WCF. Например: http: // localhost: 63957/odata – Lars335