10

新しいプロジェクトのデータ アクセス レイヤーを作成しようとしているときに、OOP/デザイン/ジェネリックの問題 (データベースへのアクセスに EF 4.3 を使用) としてしか想像できないことに遭遇しました。

主に、このデータ層で 2 つのことを達成したいと考えていました。

  • プロジェクトにあるさまざまな Context オブジェクトは、同じ接続文字列を共有する必要があります。
  • 共通の実装を持つ抽象リポジトリ クラス。

何らかの理由で、サービス レイヤーで 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、コードの複製を避けるための汎用リポジトリ)。

どんな助けにも感謝します。ありがとう。

--編集--質問を投稿してから3時間後に行ったいくつかの追加の手順をここに含めます--

これを理解するために、元のプロジェクトの結果を可能な限り模倣するために、上記と同じコードにいくつかの実装を加えたサンプル プロジェクトの作成を開始しました。

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

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

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

考えられる欠陥の 1 つは、サービス抽象クラスの制約の 1 つがリポジトリ インターフェイスであることです。そのためには、サービス レイヤーのベース データ レイヤーへの参照を追加する必要があります。おそらく、ここですでに、リポジトリ参照のみを使用できるようにすることができます。ベースデータレイヤーを参照するしかありません。

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

というわけで、結局はサービス層でEntity Frameworkを参照するしか方法はありません。また、ある時点で、Windows フォーム アプリをビルドするときに、Entity Framework も参照する必要があります。

これらの参照を避けるにはどうすればよいですか? この構造をどのように改善できますか?

私が知っていることは、私のアプリは、Entity Framework が他のレイヤーのどこかに関与していることを知る必要がないということです。サービス層も同様です。サービスはリポジトリのみを使用します。リポジトリは、テスト用に偽のデータを提供することさえあります。

誰かが興味を持っている場合に備えて、これを書いているときに作成したプロジェクトをアップロードしました。これは 1,17Mb の zip ファイルであり、バイナリはまったく含まれていません (Nuget で取得した Entity Framework 4.3.1 dll を除く)。リンク: http://www.mediafire.com/?b45zkedy2j7eocc .

繰り返しますが、助けてくれてありがとう。

4

1 に答える 1

9

に抽象を含める代わりに、インターフェースを宣言しますBaseContextBusinessLogicLayer次に、それをデータアクセス層に実装します。

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コンテキスト外では機能しません。ある意味で、あなたは実際に依存関係を隠しています!

于 2012-11-08T03:45:27.570 に答える