2012-11-07 8 views
8

Podczas próby utworzenia warstwy dostępu do danych dla nowego projektu wpadłem na to, co mogę sobie tylko wyobrazić jako problem OOP/Design/Generics (przy pomocy EF 4.3, aby uzyskać dostęp do bazy danych).Jak uniknąć konieczności odwoływania się do Entity Framework na mojej warstwie usług?

Głównie chciałem osiągnąć dwie rzeczy z tej warstwy danych:

  • różnych obiektów Kontekst mam na moim projekcie powinny dzielić ten sam ciąg połączenia.
  • Klasa abstrakcyjnego repozytorium z powszechną implementacją.

Z jakiegoś powodu nie mogę skompilować mojego rozwiązania bez odniesienia do EntityFramework w warstwie usługi. To, czego szukam, to sposób na rozwiązanie tego problemu. Oto, co mam:

//Project/Namespace BusinessLogicLayer.DomainClasses 
//POCO classes mapped on Entity Framework. 

//Project/Namespace DataAccessLayer.Base 
//Base classes and interfaces for all data access layer, such as: 

public abstract class BaseContext<TContext> : DbContext where TContext : DbContext 
{ 
    //To allow multiple contexts sharing the same connection string 
    protected BaseContext(): base("name=MyConnectionString") {} 
} 

//Generic interface for a read-only repository 
public interface IReadOnlyRepository<T> : IDisposable where T : class 

//Generic interface for a read/write repository 
public interface IRepository<T> : IReadOnlyRepository<T> where T : class 

//Basic implementation for a read-only repository 
public abstract class BaseReadOnlyRepository<C, T> : IReadOnlyRepository<T> 
    where T : class 
    where C : BaseContext<C>, new() 
{ 
} 

//Basic implementation for a read/write repository 
public abstract class BaseRepository<C, T> : IRepository<T> 
    where T : class 
    where C : BaseContext<C>, new() 
{ 
} 

//Project DataAccessLayer.AccountContext/ Namespace DataAccessLayer 
//Context class: 

public class AccountContext : BaseContext<AccountContext> {} 

//With this, I can have simple repositories: 

public class UserRepository : BaseRepository<AccountContext, User> 
{ //All implementation comes from the base abstract class, unless I need to change it (override methods) 
} 

Mam warstwę usługi między dostępem do danych i aplikacji (Windows Forms). Ponieważ mam ogólne repozytorium, wydaje mi się, że dobrym i logicznym pomysłem jest posiadanie usług ogólnych. W końcu, bardzo podobny do repozytorium strukturze:

//Project/Namespace BusinessLogicLayer.Services 
//Service layer supposed to reference only the repository project and not Entity Framework. 

//Generic interface for a read-only service working with a read-only repository 
public interface IReadOnlyService<T> where T : class {} 

//Generic interface for a read/write service working with a read/write repository 
public interface IService<T> : IReadOnlyService<T> where T : class 

//Base implementation for a read-only service 
public abstract class BaseReadOnlyService<T, R> : IReadOnlyService<T> 
    where T : class 
    where R : IReadOnlyRepository<T>, new() 
{ 
} 

//Base implementation for a read/write service 
public abstract class BaseService<T, R> : IService<T> 
    where T : class 
    where R : IRepository<T>, new() 
{ 
} 

//Concrete sample service 
public class UserService : BaseService<User, UserRepository> 
{ //As with the repository I can change the default behavior of implementation overriding methods 
} 

Z tej konfiguracji, jedynym sposobem, aby skompilować jest odniesienie do Entity Framework w projekcie warstwy usług. Jak mogę uniknąć konieczności odwoływania się do struktury Entity Framework?

W tym momencie jestem gotów nawet wyrzucić to wszystko i odbudować wszystko, ale jest to jedyny sposób, jaki znalazłem, aby działał zgodnie z moimi potrzebami (ciągi udostępniania połączenia DbContext, ogólne repozytorium, aby uniknąć replikacji kodu) .

Doceń każdą pomoc. Dzięki.

--edit - Łącznie tutaj kilka dodatkowych kroków zrobiłem 3 godziny po tym, jak opublikował question--

Aby dowiedzieć się tego, zacząłem tworzyć przykładowy projekt z tego samego kodu powyżej plus niektóre implementacje, aby jak najbardziej naśladować wyniki oryginalnego projektu.

Utworzono projekt klas domen, cały podstawowy projekt warstwy danych, a następnie projekt kontekstowy. Zauważyłem, że muszę odwołać się do Entity Framework w projekcie kontekstowym, mimo że klasa kontekstu nie pochodzi bezpośrednio z DbContext. Zamiast tego wywodzi się z abstrakcyjnej klasy wywodzącej się z DbContext. Jest to ok, ponieważ mój kontekst będzie miał DbSets i wszelkie inne implementacje związane z DbContext.

Następny jest projekt repozytorium. Musi odnosić się do wszystkich pozostałych trzech (domena, podstawowa warstwa danych i kontekst). Moje repozytorium nie ma kodu. Cała funkcjonalność leży na przodku. Próbuję skompilować projekt repozytorium, a VS wymaga od mnie odniesienia do Entity Framework. Zastanawiam się, czy to naprawdę tylko kwestia osadzania bibliotek. Jeśli to się potwierdzi, będzie to niespodzianka. Biblioteka Entity Framework znajduje się na wyjściu innych projektów. Dlaczego musiałbym tutaj również odnosić się do tego? Co sprawia, że ​​VS tego wymaga?

W każdym razie do celów testowych dodałem odniesienie. W końcu jestem w warstwie danych. Moge z tym zyc. Przejście do warstwy usługi. Dla uproszczenia ustawiłem wszystkie klasy usług w tym samym projekcie.

Jedną z możliwych wad jest to, że jednym z ograniczeń dla klas abstrakcji usług jest interfejs repozytorium.To wymaga, żebym dodał odniesienie do podstawowej warstwy danych w mojej warstwie usług. Być może już istnieje coś, co mogę zrobić, że pozwala mi używać tylko referencji repozytorium. Nie mam innego wyjścia, jak odwołać się do bazowej warstwy danych.

Wreszcie, moja konkretna usługa jest tworzona, a VS wyświetla następujący komunikat o błędzie: Typ "System.Data.Entity.DbContext" jest zdefiniowany w zespole, do którego nie odwołuje się. Należy dodać odwołanie do złożenia "EntityFramework, wersja = 4.3.1.0, Culture = neutral, PublicKeyToken = b77a5c561934e089".

Ostatecznie, jedynym sposobem na kontynuowanie jest odniesienie się do Entity Framework na warstwie usług. I w pewnym momencie, podczas budowania aplikacji Windows Forms, będę musiał również odwołać się do Entity Framework.

Co należy zrobić, aby uniknąć posiadania tych referencji? Jakie ulepszenia mogę mieć w tej strukturze?

Wiem, że moja aplikacja z pewnością nie musi wiedzieć, że Entity Framework jest zaangażowany wszędzie na innych warstwach. Ani warstwa usługi. Usługi będą zużywać repozytoria. Repozytoria mogą nawet dostarczać fałszywych danych do testów.

Jeśli ktoś jest zainteresowany, wysłałem projekt, który stworzyłem, pisząc to. Jest to plik zip 1,17Mb bez żadnych plików binarnych (z wyjątkiem Entity Framework 4.3.1 dll, który dostałem przez Nuget). Link: http://www.mediafire.com/?b45zkedy2j7eocc.

Jeszcze raz, dzięki za pomoc.

+0

Wydaje się, że to głupie pytanie, ale czuję potrzebę upewnienia się: czy warstwa dostępu do danych została skompilowana z warstwą usługi w obecnej formie? – tmesser

+0

Humm ... Wcale nie głupi. Właściwie myślę, że mógłbyś skierować mnie we właściwym kierunku. Po przeczytaniu komentarza utworzyłem przykładowy projekt demonstrujący problem. Umieściłem wyniki po pierwotnym pytaniu powyżej. Jeśli chodzi o twoje pytanie, żadna warstwa dostępu do danych nie jest kompilowana do różnych bibliotek dll. –

Odpowiedz

8

Zamiast przedstawiać abstrakcję BaseContext w swoim BusinessLogicLayer, zadeklaruj interfejs. Następnie zaimplementuj go w warstwie dostępu do danych.

public interface IDataContext : IDisposable 
{ 
    int SaveChanges(); 
} 

//Generic interface for a read-only repository 
public interface IReadOnlyRepository<T> : IDisposable where T : class 

//Generic interface for a read/write repository 
public interface IRepository<T> : IReadOnlyRepository<T> where T : class 

//Basic implementation for a read-only repository 
public abstract class BaseReadOnlyRepository<C, T> : IReadOnlyRepository<T> 
    where T : class 
    where C : IDataContext 
{ 
} 

//Basic implementation for a read/write repository 
public abstract class BaseRepository<C, T> : IRepository<T> 
    where T : class 
    where C : IDataContext 
{ 
} 


public interfaces IAccountContext : IDataContext 
{ 
    //other methods 
} 

Następnie w warstwie dostępu do danych

public abstract class BaseContext : DbContext, IDataContext 
{ 
    //To allow multiple contexts sharing the same connection string 
    protected BaseContext(): base("name=MyConnectionString") {} 
} 

public class AccountContext : BaseContext, IAccountContext {} 

//With this, I can have simple repositories: 

public class UserRepository : BaseRepository<AccountContext, User> 
{ //All implementation comes from the base abstract class, unless I need to change it (override methods) 
} 

Można użyć DI/IoC wstrzyknąć kontekstu i repozytorium służbom zamiast instancji kontekstu wewnątrz repozytorium.

To oddzielenie spowoduje, że nie będzie potrzeby odwoływania się do zespołu EF w warstwie logiki biznesowej, ale pamiętaj, że twoje encje domeny nie są całkowicie niezależne od EF. Na przykład właściwości nawigacyjne, naprawy relacji nie będą działać poza kontekstem EF. Więc w pewnym sensie ukrywasz zależność !!

+0

Rzeczywiście proponowane zmiany pozwoliły mi skompilować projekt warstwy usług bez odwoływania się do Entity Framework. Trochę utknąłem, ponieważ utraciłem możliwość wywoływania metod kontekstowych bezpośrednio w repozytorium i nie jestem zaznajomiony z DI/IoC. Będę pracował z miejsca, w którym jestem teraz. Odpowiedzi na główne pytanie. Chcę podkreślić, że nie będę potrzebować naprawiania relacji. Repozytorium będzie odpowiedzialne za zwracanie obiektów za pomocą opcji Uwzględnij, gdy będzie to konieczne. Moje POCO to tak naprawdę tylko POCO. Dzięki. –

Powiązane problemy