2016-10-03 3 views
1

У меня есть приложение WinForms, которое использует сервер веб-API (ODATA) для своих источников данных. Я привязываю решетки и формы к прокси-классам ссылки на службу.Рассчитанные свойства с ссылкой на службу

В некоторых полях пользовательского интерфейса мне нужно отобразить вычисленные значения. Если бы я писал «стандартное» приложение WinForms (без использования служебной ссылки и ее прокси-классов), я бы привязывался к бизнес-объектам, которые заполняли себя из SQL и позволяли этим бизнес-объектам выставлять вычисленные свойства, чтобы я мог привязываться к ним из пользовательский интерфейс. Например:

public class OrderLine 
{ 
    public string ItemNo { get; set; } 
    (many other properties here...) 
    public int Quantity { get; set; } 
    public decimal Price { get; set; } 
    public decimal Total { get { return Quantity * Price; } } 
} 

Теперь я могу привязывать данные к общему количеству необходимых вам данных.

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

Я мог бы, конечно, создать локальные бизнес-объекты для привязки данных, а затем использовать их для заполнения служебных ссылочных объектов, когда пришло время для сохранения данных, или я мог бы выполнить расчет в пользовательском интерфейсе (например, в событии OnChange [или аналогичном ] для количества или цены), но я бы предпочел, если есть лучший способ. Оба они приводят к дублированию кода.

Что такое хороший способ обработки рассчитанных свойств в этом сценарии?

+0

По крайней мере, вы можете использовать частичные классы моделей. –

+0

Но что же вы подразумеваете под автосозданием служебных прокси-классов? Как вы используете службы ASP.NET Web Api? Или вы используете службы WCF? –

+0

@Reza Aghei Вы можете добавить ссылку на службу в Visual Studio для службы веб-API, которая реализует ODATA v3 так же, как и с WCF. Например: http: // localhost: 63957/odata – Lars335

ответ

1

Прокси-классы моделей будут сгенерированы как классы partial. Таким образом, вы можете создать частичный класс модели и добавить вычисленное свойство. Например:

namespace ProductServiceClient.ServiceReference1 
{ 
    public partial class Product 
    { 
     public decimal SomeProperty 
     { 
      get 
      { 
       return this.Price * 10; 
      } 
     } 
    } 
} 

Пространство имен - это пространство имен приложений по умолчанию + пространство имен службы, которое вы установили в диалоговом окне Добавить ссылку.

Here - хороший пример для создания сервисного и сервисного клиента для тех, кто хочет воспроизвести и решить проблему с минимальными усилиями.

+0

@ reze-aghaei: Да, это должно сработать. Примечание: в вашем ответе отсутствует ключевое слово «partial». – Lars335

+0

@ Lars335 Спасибо за исправление :) –

+0

@ reze-aghaei: Несмотря на то, что это решение работает на стороне клиента, оно генерирует исключение на стороне сервера: «Свойство SomeProperty не существует по типу. Обязательно используйте свойство имена, определенные типом ". Я искал атрибут или аналогичный, который может использоваться, чтобы не отправлять добавленные свойства из частичного класса на сервер, но, похоже, не существует такого атрибута. – Lars335

1

Вы можете использовать службы 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

0

Вот чистый OData v4 Web API с использованием StructuralTypes.

Учитывая оригинальной ПОКА модель на сайте автора

public class OrderLine 
{ 
    public string ItemNo { get; set; } 
    (many other properties here...) 
    public int Quantity { get; set; } 
    public decimal Price { get; set; } 
    public decimal Total { get { return Quantity * Price; } } 
} 

Вы можете использовать StructuralTypes изменить модель и добавить обратно в только для чтения свойства. Логическое/вычисленное свойство не будет существовать в физической базе данных, но все равно будет отображаться в службе OData.

ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); 

builder.EntitySet<OrderLine>("OrderLines"); 

builder.StructuralTypes 
    .First(t => t.ClrType == typeof(OrderLine)) 
    .AddProperty(typeof(OrderLine).GetProperty("Total")); 

config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel()); 

Вы можете принять это дальше, если вы источник (существующий) БД, которая не соответствует требованиям бизнеса для проектируемой OData схемы. Сначала добавьте следующее использование в свой класс POCO.

using System.ComponentModel.DataAnnotations; 
using System.ComponentModel.DataAnnotations.Schema; 

Затем добавьте атрибуты, чтобы сопоставить свой класс POCO с схемой базы данных, именем таблицы и именами столбцов.

[Table("Order_Line", Schema = "dbo")] 
public class OrderLine 
{ 
    [Key] 
    [Column("ItemNo")] 
    public string Id { get; set; } 
    (many other properties here...) 

    [Column("Qty")] 
    public int Quantity { get; set; } 

    [Column("Price")] 
    public decimal Price { get; set; } 

    public decimal Total { get { return Quantity * Price; } } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^