2012-11-07 13 views
8

新しいプロジェクトのデータアクセスレイヤーを作成しようとしているときに、OOP/Design/Genericsの問題(EF 4.3を使用してデータベースにアクセスする)としてしか想像できないものに遭遇しました。サービスレイヤでEntity Frameworkを参照する必要性を避けるにはどうすればよいですか?

は、主に私は、このデータ層との二つのことを達成したい:私は私のプロジェクトに持って

  • 異なるコンテキストのオブジェクトは、同じ接続文字列を共有する必要があります。
  • 一般的な実装を持つリポジトリクラス。

何らかの理由で、サービスレイヤーでEntityFrameworkを参照せずにソリューションをコンパイルできません。私が探しているのはこれを解決する方法です。ここに私が持っているもの:

//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) 
} 

私はデータアクセスとアプリケーション(Windowsフォーム)の間にサービスレイヤーを持っています。私はジェネリックリポジトリを持っているので、ジェネリックサービスを持っていることは良い考えです。リポジトリの構造に非常に似て終わり、では:

//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 
} 

この設定では、コンパイルする唯一の方法は、サービス層のプロジェクトでEntity Frameworkのを参照することです。 Entity Frameworkをそこで参照する必要性を避けるにはどうすればよいですか?

この時点で、私はすべてを投げ捨てて、すべてを再構築したいと思いますが、私のニーズ(DbContext共有接続文字列、コードの複製を避けるための汎用リポジトリ) 。

ありがとうございました。ありがとう。

--edit - 私はこれを理解するためにはquestion--

を投稿ここで私がやったいくつかの余分な手順を含め3時間後、私は上記と同じコードとサンプルプロジェクトを作成し始めました元のプロジェクトの結果を可能な限り模倣するいくつかの実装を加えました。

ドメインクラスプロジェクト、基本データレイヤープロジェクト全体、そしてコンテキストプロジェクトを作成しました。コンテキストクラスがDbContextから直接派生していなくても、コンテキストプロジェクトでEntity Frameworkを参照する必要があることに気付きました。代わりに、DbContextから派生した抽象クラスから派生します。私のコンテキストにはDbSetsとDbContextに関連する他の実装があるので、これは問題ありません。

次はリポジトリプロジェクトです。他のすべての3つ(ドメイン、基本データレイヤー、コンテキスト)を参照する必要があります。私のリポジトリにはコードはありません。すべての機能は祖先にある。リポジトリプロジェクトをコンパイルしようとしましたが、VSはEntity Frameworkを参照する必要があります。私はそれが実際にライブラリを埋め込むことの問題なのだろうかと思います。これが確認されれば、それは驚きになるだろう。 Entity Frameworkライブラリは他のプロジェクトの出力に存在します。なぜ私はここでもそれを参照する必要がありますか? VSがこれを必要としているのは何ですか?

とにかく、テスト目的のために、私は参照を追加しました。結局のところ、私はデータ層の中にいます。私はそれで生きることができます。サービスレイヤーに移動します。わかりやすくするために、すべてのサービスクラスを同じプロジェクトに入れました。

考えられる1つの欠点は、サービス抽象クラスの制約の1つがリポジトリインターフェイスであることです。そのためには、私のサービス層のベースデータレイヤへの参照を追加する必要があります。おそらく既にここには私がリポジトリ参照のみを使うことを可能にする何かがあります。私はオプションがないが、基本データ層を参照する。

最後に、具体的なサービスが作成され、VSから次のエラーメッセージが表示されます。 'System.Data.Entity.DbContext'型は、参照されていないアセンブリで定義されています。アセンブリ 'EntityFramework、Version = 4.3.1.0、Culture = neutral、PublicKeyToken = b77a5c561934e089'への参照を追加する必要があります。

したがって、結局のところ、サービスレイヤーでEntity Frameworkを参照するしか方法はありません。そして、ある時点では、Windows Forms Appを構築するときに、Entity Frameworkも参照する必要があります。

これらの参照がないようにするにはどうすればよいですか?この構造にはどのような改善がありますか?

私の知る限りでは、私のアプリは確かにEntity Frameworkが他の層のどこにでも関わっていることを知る必要はありません。どちらもサービス層ではありません。サービスはリポジトリのみを消費します。リポジトリは、テスト用に偽のデータを提供することさえできます。

誰かが興味がある場合は、私がこれを書いている間に作成したプロジェクトをアップロードしました。これはバイナリを全く持たない1,17Mbのzipファイルです(ただし、Nugetを使って入手したEntity Framework 4.3.1のDLLを除く)。リンク:http://www.mediafire.com/?b45zkedy2j7eocc

また、ありがとうございました。

+0

愚かな質問のようですが、私は確かめる必要があると感じています。データアクセスレイヤーは現在サービスレイヤーでコンパイルされていますか? – tmesser

+0

ハム...全く馬鹿ではない。実際、あなたは私を正しい方向に向けるかもしれないと思います。あなたのコメントを読んだ後、私は問題を示すサンプルプロジェクトを作成しました。私は上記の元の質問の後に結果を入れます。あなたの質問については、データアクセス層は異なるdllにコンパイルされません。 –

答えて

8

BusinessLogicLayerで抽象的なBaseContextを使用する代わりに、インターフェイスを宣言してください。次にデータアクセスレイヤーに実装します。

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 
} 

は、データアクセス層

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) 
} 

では、代わりに、リポジトリの内部コンテキストをインスタンス化のコンテキストおよびサービスのリポジトリを注入するDI/Iocをを使用することができます。

このデカップリングにより、ビジネスロジックレイヤーでEFアセンブリを参照する必要はなくなりますが、ドメインエンティティはEFから完全に独立しているわけではありません。ナビゲーションプロパティの場合、リレーションシップフィックスアップはEFコンテキスト外では機能しません。ですから、あなたは実際に依存関係を隠しているのです!

+0

実際、あなたが提案した変更により、Entity Frameworkを参照せずにサービスレイヤプロジェクトをコンパイルすることができました。リポジトリを介してコンテキストメソッドを直接呼び出す機能がなくなり、DI/IoCに慣れていないため、ちょっと立ち往生しています。私は今のところから自分の道を歩みます。主な質問に答えました。私は関係の修正を必要としないことを指摘したい。リポジトリは、必要に応じてインクルードオプションを使用してオブジェクトを返す責任があります。だから、私のPOCOは本当にPOCOだけです。ありがとう。 –

関連する問題