2009-10-26 10 views
15

Zastanawiam się, czy istnieje jakiś łatwy sposób na rozszerzenie NHibernate w celu wspierania dyskryminowanej unii F #. Nie tylko jeden typ IUserType lub ICompositeUserType, ale coś generycznego, które mogę ponownie wykorzystać niezależnie od rzeczywistej zawartości DU.Dyskryminowane związki w NHibernate

Na przykład załóżmy, że mam właściwość o nazwie RequestInfo, który jest zdefiniowany jako: unia

type RequestInfo = 
    | Id of int 
    | Name of string 

kompiluje to do klasy abstrakcyjnej RequestInfo, betonem podklasy Identyfikator i nazwisko. Mogę uzyskać wszystkie te informacje dobrze z F # odbicie. W takim przypadku mógłbym zapisać go w bazie danych za pomocą "RequestInfo_Tag", "RequestInfo_Id", "RequestInfo_Name".

Jako że jestem nowicjuszem NHibernate, z jakimi problemami będę się starał zastosować to podejście? Czy bardziej skomplikowane sprawy będą niemożliwe do rozwiązania? Na przykład, co z zagnieżdżonymi dyskryminowanymi związkami? Czy istnieje sposób, w jaki mogę "przekazać" odczyt pozostałej części związku do innego typu ICompositeUserType?

Co ważniejsze, czy to zepsuje moje możliwości wyszukiwania? Czyli będę musiał znać rzeczywiste nazwy kolumn w DB; Nie będę w stanie zrobić Criteria.Eq (SomeDiscUnion) i wszystko to uporządkować?

Nie szukam kompletnej odpowiedzi "podaj kod", tylko kilka ogólnych rad, jeśli warto to robić dalej (i kilka wskazówek jak), czy powinienem ponownie przemyśleć mój model.

Dzięki!

P.S. Nie bądź niegrzeczny, ale jeśli twoja odpowiedź składa się z "użyj C#", nie jest to zbyt pomocne.

+1

Bardzo interesujące pytanie. Jestem dość biegły w NHibernate i F #, poradzę sobie z tym, kiedy dostanę trochę czasu. –

+0

Co najmniej z mojego doświadczenia, miałem ciężki czas używając NHibernate z F #, głównie dlatego, że NHibernate wymaga, aby obiekty miały domyślny konstruktor (który jest często nie w przypadku typów złącz, typów rekordów i większości klas) i opiera się na zmienności, aby przypisać wartości do klas (co często jest denerwujące w radzeniu sobie z definicjami klasy F #). Częściej niż nie, dostaję bazę danych do gry z F # przez toczenie własnego mirco-ORM. – Juliet

+0

Tak, w naszym ostatnim projekcie po prostu trzymaliśmy się starych typów obiektów, aby ładnie się bawić z NHibernate. Tym razem chciałbym się trochę rozgałęzić i wypróbować bardziej realistyczne modele. – MichaelGG

Odpowiedz

7

Nie byłem na tyle odważny, aby spróbować użyć NHibernate z systemem typu F #, ale może to pomóc w spojrzeniu z perspektywy tego, co faktycznie zostało wygenerowane przez kompilator F #.

Jeśli spojrzysz na swój dyskryminowany związek w odbiciu, generowane są trzy klasy (i więcej, jeśli zliczysz prywatne serwery proxy debugowania).

public abstract class RequestInfo : IStructuralEquatable, IComparable, IStructuralComparable 

Pierwsza klasa, RequestInfo, jest abstrakcyjna i jest faktycznie implementowana przez inne typy w unii.

// Nested Types 
    [Serializable, DebuggerTypeProxy(typeof([email protected])), DebuggerDisplay("{__DebugDisplay()}")] 
    public class _Id : Program.RequestInfo 
    { 
     // Fields 
     [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode] 
     public readonly int id1; 

     // Methods 
     [CompilerGenerated, DebuggerNonUserCode] 
     public _Id(int id1); 
    } 
    [Serializable, DebuggerTypeProxy(typeof([email protected])), DebuggerDisplay("{__DebugDisplay()}")] 
    public class _Name : Program.RequestInfo 
    { 
     // Fields 
     [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode] 
     public readonly string name1; 

     // Methods 
     [CompilerGenerated, DebuggerNonUserCode] 
     public _Name(string name1); 
    } 

więc kiedy zrobić:

let r=Id(5) 
let s=Name("bob") 

r i s są przypadki _id i _name odpowiednio.

Tak więc odpowiedź na Twoje pytanie jest prawdopodobnie odpowiedzią na jedno z poniższych pytań:

  • Jak mogę mapować do klasy abstrakcyjnej w NHibernate?
  • Jak mogę NHibernate użyć metody fabrycznej?
  • Jak mogę utworzyć mapę Nhibernate na niezmienne obiekty?
  • Jak zaimplementować niestandardowy typ w NHibernate (prawdopodobnie z IUserType).

Niestety, nie jestem wystarczająco doświadczony, aby dać ci spójną odpowiedź na którekolwiek z tych pytań, ale jestem pewien, że ktoś tutaj wykonał przynajmniej jedno z tych trzech rozwiązań.

Chciałbym myśleć, że można użyć tych samych metod, co w przypadku strategii dziedziczenia, na przykład przy użyciu kolumny dyskryminacyjnej, ale obawiam się, że brak domyślnego konstruktora sprawia, że ​​jest to problematyczne. Więc jestem skłonny pomyśleć, że używanie niestandardowego typu jest rozwiązaniem.

Po pewnym fiddling, oto (ewentualnie wadliwy i czy uszkodzony) niestandardowy typ użytkownika:

type RequestInfo = 
    | Id of int 
    | Name of string 

type RequestInfoUserType() as self = 
    interface IUserType with 
     member x.IsMutable = false 
     member x.ReturnedType = typeof<RequestInfo> 
     member x.SqlTypes = [| NHibernate.SqlTypes.SqlType(Data.DbType.String); NHibernate.SqlTypes.SqlType(Data.DbType.Int32); NHibernate.SqlTypes.SqlType(Data.DbType.String) |] 
     member x.DeepCopy(obj) = obj //Immutable objects shouldn't need a deep copy 
     member x.Replace(original,target,owner) = target // this might be ok 
     member x.Assemble(cached, owner) = (x :> IUserType).DeepCopy(cached) 
     member x.Disassemble(value) = (x :> IUserType).DeepCopy(value) 

     member x.NullSafeGet(rs, names, owner)= 
      // we'll use a column as a type discriminator, and assume the first mapped column is an int, and the second is a string. 
      let t,id,name = rs.GetString(0),rs.GetInt32(1),rs.GetString(2) 
      match t with 
       | "I" -> Id(id) :> System.Object 
       | "N" -> Name(name) :> System.Object 
       | _ -> null 
     member x.NullSafeSet(cmd, value, index)= 
      match value with 
       | :? RequestInfo -> 
        let record = value :?> RequestInfo 
        match record with 
         | Id(i) -> 
          cmd.Parameters.Item(0) <- "I" 
          cmd.Parameters.Item(1) <- i 
         | Name(n) -> 
          cmd.Parameters.Item(0) <- "N" 
          cmd.Parameters.Item(2) <- n 
       | _ -> raise (new ArgumentException("Unexpected type")) 

     member x.GetHashCode(obj) = obj.GetHashCode() 
     member x.Equals(a,b) = 
      if (Object.ReferenceEquals(a,b)) then 
       true 
      else 
       if (a=null && b=null) then 
        false 
       else 
        a.Equals(b) 
    end 

Ten kod może z pewnością być bardziej uniwersalne, a prawdopodobnie powinien być na aktualnej warstwie domeny, ale pomyślałem, że przydałoby się ukłuć przy implementacji F # IUserType.

Plik mapowania by następnie zrobić coś takiego:

<property name="IdOrName" type="MyNamespace.RequestInfoUserType, MyAssembly" > 
    <column name="Type"/> 
    <column name="Id"/> 
    <column name="Name"/> 
</property> 

Prawdopodobnie można uciec bez kolumnie „Type” z lekkim uszczypnąć do kodu niestandardowego UserType.

Nie wiem, jak te niestandardowe typy użytkowników działają z zapytaniami/ICriteria, ponieważ tak naprawdę nie pracowałem wcześniej z niestandardowymi typami użytkowników.

+0

Tak, odwzorowanie pojedynczej instancji powinno być stosunkowo łatwe. Ale co, gdy DU zawiera innego DU lub coś takiego? Wydaje mi się, że potrzebuję zaawansowanego złożonego typu użytkownika, który jest rekursywny, aby tego rodzaju obiekty poprawnie mapowały we wszystkich przypadkach. Jestem również zaniepokojony tym, jak to pokazuje się przy podejmowaniu kryteriów. – MichaelGG

+0

Nie jestem pewien, jak bym sobie z tym poradził, ale podejrzewam, że mógłbyś użyć głębiej dopasowanego wzoru podobnego do mojego przykładu powyżej. – JasonTrue

Powiązane problemy