2009-03-29 10 views
9

Mam jednostkę Foo w Entity Framework. Ale robię to dziedziczy po IFoo, więc moja logika biznesowa zna tylko IFoo - w ten sposób oddzielając Entity Framework.Czy mogę odejść od Entity Framework z dala od moich jednostek?

Problem polega na tym, że Foo ma kolekcję jednostek Bar. Ta kolekcja jest typu EntityCollection<Bar>.

Jeśli umieściłem tę kolekcję w postaci IFoo, to zależnie od architektury Entity, IFoo. Więc pomyślałem o umieszczeniu go jako ICollection<IBar>, ale to nie kompiluje (naturalnie).

Jedynym rozwiązaniem można myślę o to, aby przejść do konkretnej Foo realizacji generowanego przez projektanta Entity Framework i zmienić kolekcji z EntityCollection<Bar> do ICollection<IBar> tam. Ale boję się myśli o implikacjach, które będą miały na Entity Framework "za kulisami".

Czy jest jakiś sposób dla mnie do zdefiniowania IFoo i IBar niezależnie od Entity Framework przy jednoczesnym zachowaniu Foo i Bar jako podmioty, które je wdrażają EF? Czy IFoo i IBar mają nawet sens, jeśli nie mogę osiągnąć tej niezależności, do której dążę?

+0

Jaki jest dokładny powód, dla którego chcesz IFoos i IBars zamiast Foos i Bars? – Inferis

+0

Mam na celu ignorancję wytrwałości. Zobacz akceptowaną odpowiedź poniżej. – urig

Odpowiedz

8

Ogólna koncepcja masz na myśli to „Trwałość ignorancja” (PI), mimo że na ogół stosuje się bezpośrednio do podmiotów zamiast kodu, który zużywa podmioty.

W każdym przypadku Hibernate i NHibernate natywnie obsługują PI, ale początkowa wersja Microsoft Entity Framework tego nie robi.MS złapało za to dużo błędów, a PI jest prawdopodobnie najczęściej omawianą cechą następnej wersji (kiedykolwiek tak jest).

Jeśli chodzi o to, co próbujesz zrobić z interfejsami, czy zbiór pasków musi zostać zmodyfikowany po pobraniu? Jeśli odpowiedź brzmi "tak", nie ma łatwej odpowiedzi. Nawet kowariancja nie mogła ci tu pomóc, ponieważ ICollection<T> ma metodę dodawania.

Jeśli kolekcja jest tylko do odczytu, można rozważyć wystawienie jej jako IEnumerable<IBar>. Metoda Enumerable.Cast sprawia, że ​​jest to dość wygodne.

interface IFoo 
{ 
    IEnumerable<IBar> Bars { get; } 
} 

partial class Foo : IFoo 
{ 
    IEnumerable<IBar> IFoo.Bars 
    { 
     get { return Bars.Cast<IBar>(); } 
    } 
} 

Również znam at least one effort aby aktualny wersję EF wsparcie trwałości ignorancji.

+0

Dlaczego nie można wprowadzić modyfikacji? AttachTo może dołączyć obiekt do kontekstu, gdzie po modyfikacji można dokonać. – aleemb

+0

Czy EF 4 to robi? – BigJoe714

2

Jestem programistą Java, więc nie mogę komentować z żadnym autorytetem w Entity Framework. Mogę powiedzieć, że rozwiązania ORM takie jak Hibernate umożliwiają utrzymywanie POJO bez konieczności uciekania się do wspólnych klas abstrakcyjnych, interfejsów lub modyfikacji kodu bajtowego. Obsługuje relacje takie jak 1: m, które powołujesz się na Foo i Bar, bez konieczności używania specjalnych klas kolekcji.

Specjalny sos jest uzbrojony w konfigurację odwzorowania i samą hibernację.

Ten fragment, który czytałem o Entity Framework sugeruje, że jest to rozwiązanie ORM o tym samym celu: Trwałość POCO. Nie widziałem żadnej wzmianki o interfejsach. Nie widzę potrzeby ich z twojego przykładu, ponieważ jest zbyt abstrakcyjny.

Podejrzewam, że możliwe jest uzyskanie tej niezależności między obiektami biznesowymi a warstwą trwałości bez konieczności uciekania się do tych interfejsów, ponieważ wiem, że to robi Hibernate. Powiedziałbym, że rozwiązanie JDBC Springa również to robi, ponieważ nie ma potrzeby stosowania wspólnych interfejsów. Używają konstrukcji RowMapper do przesyłania danych z zapytania do obiektu.

Chciałbym móc doradzić ci dokładnie, jak to zrobić z Entity Framework, ale być może dasz sobie z serca sprawę, że można to zrobić.

+0

Dzięki za szczegółową odpowiedź. Mam pozytywne doświadczenia w przeszłości z NHibernate, wersją .NET Java Hibernate. Nie jestem ekspertem od EF, ale rozumiem, że jego mapowanie jest oparte na atrybutach, a nie uzewnętrzniane w pliku XML. Właśnie dlatego potrzebuję abstrakcji za pomocą interfejsu. Myślę, że;) – urig

+0

Być może masz rację. Po prostu nie widziałem żadnych dowodów na to w krótkim artykule z Google. Przepraszam, że nie jestem bardziej pomocny. – duffymo

2

Użyj częściowej klasy, aby oddzielić logikę i reguły od automatycznie generowanych obiektów EF. W poniższym przykładzie klasa FooEntityObject jest podzielona na dwie części za pomocą częściowego słowa kluczowego. Użyłem tej techniki przed EF i LINQ do SQL. Klasy cząstkowe mogą być przechowywane w osobnych plikach, więc jeśli ponownie wygenerujesz obiekt EF, twój niestandardowy kod nie zostanie nadpisany.

interface IFoo 
{ 
    public ICollection<IBar> GetBars(); 
} 

public partial class FooEntityObject : IFoo 
{ 
    public ICollection<IBar> GetBars() 
    { 
     // convert EntityCollection<Bar> into ICollection<IBar> here 
    } 
} 


public partial class FooEntityObject 
{ 
    EntityCollection<Bar> Bars{get;set;} 
} 
2

Niedawno napisałem obszerną wiadomość na ten temat: Persistence Ignorance in ADO.NET Entity Framework. Możesz spojrzeć na EFPocoAdapter. To właśnie robi i ostatecznie zostanie wycofane do wersji EF v2.

Za to, co jest warte, używam EFPocoAdapater i działa dobrze dla mnie.

2

Zrobiliśmy dokładnie to samo dla LINQ do SQL. Usunąłem problem z kolekcją, pisząc klasę, która owija kod IList i rzuca do i od poprawnego typu zgodnie z wymaganiami. To wygląda trochę tak:

public class ListWrapper<TSource, TTarget> : IList<TTarget> 
    where TTarget : class 
    where TSource : class, TTarget 
{ 
    private IList<TSource> internalList; 

    public ListWrapper(IList<TSource> internalList) 
    { 
     this.internalList = internalList; 
    } 

    public void Add(TTarget item) 
    { 
     internalList.Add((TSource)item); 
    } 

    public IEnumerator<TTarget> GetEnumerator() 
    { 
     return new EnumeratorWrapper<TSource, TTarget>(internalList.GetEnumerator()); 
    } 

    // and all the other IList members 
} 

EnumeratorWrapper podobnie owija się IEnumerator i przeprowadza casting. W LINQ to SQL częściowe klas możemy narazić właściwość tak:

public IList<ICustomer> Foos 
{ 
    get 
    { 
     return new ListWrapper<Foo, IFoo>(this.Foos_internal); 
    } 
} 

Wszelkie zmiany odsłoniętej liście zostaną wykonane na wewnętrznej EntitySet więc pozostają one zsynchronizowane.

To działa dobrze, ale mam wrażenie, że to całe podejście sprawia więcej problemów niż jest warte, jestem wielkim fanem NHibernate i silnym wyznawcą P.I. ale włożyliśmy w to dużo dodatkowego wysiłku i nie widzieliśmy żadnej przewagi. Używamy wzorca repozytorium, aby odciąć rzeczywisty dostęp do DataContext, który według mnie jest kluczową częścią oddzielenia się od LINQ do SQL.

Powiązane problemy