2015-09-18 8 views
11

Mam aplikację dla wielu dzierżawców, która zawiera warstwę usługi ODA Web API. Mam nowy wymóg obsługi pól niestandardowych, które będą unikalne dla każdego lokatora, a dodanie ogólnych tabel "customfield01", "customfield02" do moich tabel nie będzie wystarczająco elastyczne.Web API OData V4 Otwarte typy - konfigurowanie kontekstu kontrolera i danych

Przeanalizowałem wiele sposobów opisywania i utrzymywania niestandardowych danych na zapleczu, ale wydaje mi się, że trudniejszą częścią jest rozszerzenie usług odata o niestandardowe pola, inaczej dla każdego dzierżawcy.

Poniższy link opisuje „Otwórz typów” w OData v4 z Web API:

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/use-open-types-in-odata-v4

Przykładowy kod działa dobrze i zapewnia dynamiczne zachowanie własności muszę na moich podmiotów. Jednak kod idzie tak daleko, jak przy użyciu zakodowanej listy wartości dla zaplecza. Nie jest jasne, w jaki sposób zapełnić encje z kontekstu danych Entity Framework.

Początkowo wydawało się, że może to być tak proste jak posiadanie konkretnego lokatora w bazie danych, dla każdego lokatora, ale problem polega na tym, że rozszerzone właściwości muszą być naprawdę "niepodzielone" z kolumn, do klucza - pary wartości. Z tego powodu zastanawiam się, czy potrzebuję osobnego elementu dla właściwości "rozszerzenia". Więc mogę mieć coś takiego na moim Poços:

public class Item 
{ 
    [Key] 
    public Guid ItemId { get; set; } 

    public Guid TenantId { get; set; } 

    // navigation property for the extension entity 
    public virtual ItemExtension ItemExtension { get; set; } 
} 

public class ItemExtension 
{ 
    [Key] 
    public Guid ItemId { get; set; }  

    // dynamic properties for the open type 
    public IDictionary<string, object> DynamicProperties { get; set; }} 
} 

Ale znów pojawia się pytanie, jak wypełnić te obiekty z danymi z mojego kontekście danych. Po raz kolejny pomyślałem, że mogę mieć widok, aby rozpakować kolumny, ale to nie działa, ponieważ mogłem mieć różne typy danych (dla mnie ważne) dla każdej właściwości dynamicznej.

Tak naprawdę mam kilka pytań:

  1. Czy model POCO powyżej sensu, co staram się osiągnąć?
  2. Co powinno mój kod ItemController wyglądać włączenie ItemExtension dla wszystkich zleceń HTTP (GET, POST, PUT, łata, DELETE)
  3. Co powinno mój kontekst dane mają za ItemExtension aby umożliwić mu dostęp do rozszerzonych kolumn na back-end, w jaki sposób powinny być przedłużone kolumny na zapleczu, aby to wspierać.

Jeśli chodzi o to, co próbowałem - wiele rzeczy, które nie działają, ale ja osiadł na następujące (przy założeniu, że nie ma lepszego sposobu):

  1. Podstawowy POCO dla każdej "rozszerzalnej" encji z osobnym "rozszerzeniem" jednostki dla każdego (jak powyższy model)

  2. Z tyłu, ponieważ potrzebuję nieograniczonej elastyczności i silnych typów danych, planuję mieć osobna tablica rozszerzeń dla każdej kombinacji Tenant/Entity (zostanie ona nazwana jako [TenantId]. [ItemExtension] wi każda kolumna jest nazwana i wpisana w razie potrzeby).

To czego mi brakuje to wszystko pomiędzy moimi danymi a moim modelem. Każda pomoc będzie wielce ceniona.

+0

wielkie pytanie, jeden, że jestem naprawdę utknąć na teraz, czy ktoś z powodzeniem użył otwartych typów OData v4 z backendem EF6? Uwaga: nie NHibernate ... szukam rozwiązania EF6 Najpierw wezmę kod lub model. –

+0

Co obecnie robię, to używam połączenia EF Context do wykonania procedury składowanej. Następnie muszę powtórzyć dane, aby utworzyć typ otwarty. To działa, chciałbym tylko, żeby nie musiałem "przetwarzać" danych w kontrolerze przed zwróceniem go do osoby dzwoniącej. –

+0

Okazuje się, że utknąłem na nieco inny problem, miałem to wszystko działa z tym wyjątkiem, że każde wywołanie GET, które nie określiło instrukcji $ select, zawodziło, gdy środowisko wykonawcze próbowało zastosować zapytania, które zostały przekazane w ... Mój problem polegał na tym, że gdy nie określono żadnego $ select, jest to to samo co SQL select *, tylko środowisko wykonawcze wstrzykuje wszystkie kolumny do wybranych parametrów, w tym właściwości DynamicProperties. Musiałam zastąpić EnableQuery ... Nie jestem pewien, czy uda ci się uzyskać "przetwarzanie" danych, w przeciwnym razie równie dobrze moglibyśmy zakodować niestandardowe właściwości w DTO. –

Odpowiedz

0

Teraz nie używam Entity Framework po jego błędzie z buforowaniem danych.Spójrz na Fluent NHibernate. W ORM można dostosować mapowanie dynamicznych właściwości OData v4 do typu użytkownika. Użyj pakietu nuget Newtonsoft.Json.

Twoja klasa:

public class Item 
{ 
    [Key] 
    public Guid ItemId { get; set; } 

    // dynamic properties for the open type 
    public IDictionary<string, object> DynamicProperties { get; set; } 

    ... 
} 

i StoreDynamicProperties niestandardowy typ klasy Fluent NHibernate:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data; 
using System.Data.Common; 
using Newtonsoft.Json; 
using NHibernate.UserTypes; 
using NHibernate.SqlTypes; 

[Serializable] 
public class StoreDynamicProperties : IUserType 
{ 
    private JsonSerializerSettings _settings = new JsonSerializerSettings(); // { TypeNameHandling = TypeNameHandling.All }; 

    public new bool Equals(object x, object y) 
    { 
     if (x == null && y == null) 
      return true; 

     if (x == null || y == null) 
      return false; 

     var xdocX = JsonConvert.SerializeObject((IDictionary<string, object>)x, _settings); 
     var xdocY = JsonConvert.SerializeObject((IDictionary<string, object>)y, _settings); 

     return xdocY == xdocX; 
    } 

    public int GetHashCode(object x) 
    { 
     if (x == null) 
      return 0; 

     return x.GetHashCode(); 
    } 

    public object NullSafeGet(IDataReader rs, string[] names, object owner) 
    { 
     if (names.Length != 1) 
      throw new InvalidOperationException("Only expecting one column…"); 

     var val = rs[names[0]] as string; 

     if (val != null && !string.IsNullOrWhiteSpace(val)) 
     { 
      return JsonConvert.DeserializeObject<IDictionary<string, object>>(val, _settings); 
     } 

     return null; 
    } 

    public void NullSafeSet(IDbCommand cmd, object value, int index) 
    { 
     var parameter = (DbParameter)cmd.Parameters[index]; 

     if (value == null) 
     { 
      parameter.Value = DBNull.Value; 
     } 
     else 
     { 
      parameter.Value = JsonConvert.SerializeObject((IDictionary<string, object>)value, _settings); 
     } 
    } 

    public object DeepCopy(object value) 
    { 
     if (value == null) 
      return null; 

     //Serialized and Deserialized using json.net so that I don't 
     //have to mark the class as serializable. Most likely slower 
     //but only done for convenience. 

     var serialized = JsonConvert.SerializeObject((IDictionary<string, object>)value, _settings); 

     return JsonConvert.DeserializeObject<IDictionary<string, object>>(serialized, _settings); 
    } 

    public object Replace(object original, object target, object owner) 
    { 
     return original; 
    } 

    public object Assemble(object cached, object owner) 
    { 
     var str = cached as string; 

     if (string.IsNullOrWhiteSpace(str)) 
      return null; 

     return JsonConvert.DeserializeObject<IDictionary<string, object>>(str, _settings); 
    } 

    public object Disassemble(object value) 
    { 
     if (value == null) 
      return null; 

     return JsonConvert.SerializeObject((IDictionary<string, object>)value); 
    } 

    public SqlType[] SqlTypes 
    { 
     get 
     { 
      return new SqlType[] { new StringSqlType(8000) }; 
     } 
    } 

    public Type ReturnedType 
    { 
     get { return typeof(IDictionary<string, object>); } 
    } 

    public bool IsMutable 
    { 
     get { return true; } 
    } 
} 

aw ItemMap Klasa:

using FluentNHibernate.Mapping; 

public class ItemMap : ClassMap<Item> 
{ 
    public ItemMap() 
    { 
     Table("Items"); 

     Id(item => item.ItemId) 
      .GeneratedBy 
      .GuidComb(); 

     Map(item => item.DynamicProperties) 
      .CustomType<StoreDynamicProperties>() 
      .Column("Properties") 
      .CustomSqlType("varchar(8000)") 
      .Length(8000); 
     ... 
    } 
}