リポジトリパターンを実装するために、LINQからSQLプロバイダーに分離されたエンティティクラス。どうやって?
-
04-07-2019 - |
質問
リポジトリパターンを確認しましたが、過去に使用していて気分が良くなったアイデアをいくつか認識しました。
ただし、ここでこのパターンを使用するアプリケーションを作成したいと思います。ただし、リポジトリプロバイダーからエンティティクラスをデカップリングしたい。
複数のアセンブリを作成します:
- "インターフェース" IRepositoryインターフェイスを含む一般的なインターフェイスをホストするアセンブリ
- "エンティティ" Product、User、Orderなどのエンティティクラスをホストするアセンブリ。このアセンブリは、「インターフェイス」によって参照されます。一部のメソッドはそのような型またはそれらの配列を返すため、アセンブリ。また、メインアプリケーションアセンブリ(Webアプリケーションなど)によって参照されます
- 1つ以上のリポジトリプロバイダーアセンブリ。それぞれに(少なくとも)IRepositoryインターフェイスを実装するクラスが含まれ、特定のデータストアで機能します。データストアには、SQL Server、Oracleサーバー、MySQL、XMLファイル、Web / WCFサービスなどが含まれます。
LINQ to SQLの研究は、すべてを実装するのにかかる時間の観点から非常に生産的であるように見えますが、生成されたクラスとCustomDataContextクラスの間に深い依存関係があることがわかります。
このようなシナリオでLINQ to SQLを使用するにはどうすればよいですか
解決
これがまさにあなたが望むものかどうかはわかりませんが、Rob ConeryのMVC Storefrontコードを見てみたいと思うかもしれません。彼は、linqプロバイダーでリポジトリパターンのバリアントを使用します。彼はLINQ to Sqlオブジェクトをドメインオブジェクトにマップし、ドメインオブジェクトをリポジトリプロバイダーからサービスレイヤーに返します。サービスレイヤーはプロバイダーをラップし、返されたデータに対してビジネスレイヤーに到達する前に何らかのロジックを実行できるようにします。
私には、プロバイダーにDTOを返させてから、DTOをリポジトリ/サービスレイヤーのドメインオブジェクトにマップしたいようです。この場合、LINQ to SQLプロバイダーをDTOにマップし、それらを返してから、DTOをリポジトリ/サービスレイヤーのドメインオブジェクトにマップできます。これは正常に機能するはずですが、マッピングレイヤーが2つになり、退屈になる場合があります。
この場合、次のようになります。 ProductService。IProductRepositoryを受け取ります。 IProductRepositoryのメソッドを呼び出して、DTOを取得します。次に、DTOを実際のビジネスオブジェクトにマップし、呼び出し元のコードに返します。
他のヒント
データベースを任意のクラスにマッピングする外部XMLファイルを作成できます。
<?xml version="1.0" encoding="utf-8"?>
<Database Name="DbName"
xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
<Table Name="DbTableName">
<Type Name="EntityClassName" >
<Column Name="ID" Type="System.Int64" Member="Id"
DbType="BigInt NOT NULL IDENTITY" IsPrimaryKey="true"
CanBeNull="false" />
<Column Name="ColumnName" Type="System.String" Member="PropertyA"
DbType="VarChar(1024)" CanBeNull="true" />
</Type>
</Table>
</Database>
そして、XMLをDataContextクラスに渡します:
using (var cn = GetDbConnection())
{ var mappingSrc = XmlMappingSource.FromReader(xmlReader);
using (var db = new DataContext(cn, mappingSrc))
{ var q = from entity in db.GetTable<EntityClassName>()
where entity.PropertyA = "..."
select entity.ID;
}
}
これについての素晴らしいブログ投稿(多くの優れたコード)をここで見つけました: http://iridescence.no/post/Linq-to-Sql-Programming-Against-an-Interface-and-the-Repository-Pattern.aspx
POCO(Plain Old CLR Objects)サポートが必要だと思います。 LINQ to SQLには、 Close2Poco というアダプターがあります。
ただし、Entity Frameworkに切り替えることをお勧めします。現時点では、 POCOアダプター、ただし v2はそのままでサポートされる予定です。
LINQ to SQL生成コードを使用する必要はありません。必要なColumnAttributesを使用して独自のクラスを装飾するか、外部XMLマッピングファイルを使用できます。
最も簡単な方法は、エンティティをdatacontextから切り離すことです。必要なエンティティをロードし、DataContextから切り離し、好きなように使用し、後でAttach()を使用して保存するためにDataContextと連結します。
残念ながら、LINQにはエンティティをデータコンテキストから分離する方法はありませんが、それらを複製するだけでうまく機能します。最も簡単な方法は次のようなものです:
public static T CloneEntity<T>(T source)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
using (Stream stream = new MemoryStream())
{
dcs.WriteObject(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)dcs.ReadObject(stream);
}
}
WCFで似たようなことをしました
1 DBMLで、シリアル化モードを単方向に設定します
2テーブルのすべての列をUpdateCheck = falseに設定します
3次のようなサービスを記述します。
public class Service1 : IService1
{
public Company GetCompany(int companyId)
{
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
return (from c in dc.Companies where c.CompanyId == companyId select c).Single();
}
}
public void SaveCompany(Company company)
{
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
dc.Companies.Attach(company, true);
dc.SubmitChanges();
}
}
public void InsertCompany(Company company)
{
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
dc.Companies.InsertOnSubmit(company);
dc.SubmitChanges();
}
}
}
4サービス参照を追加します
まったく同じシナリオではありませんが、XMLファイルに基づいてOOモデルを生成するカスタムツールの作成に取り組んでいます。私のアプローチはLINQ to SQLを舞台裏で使用することです。コードを自動的に生成するため、MySQLデータソースなどの別のメカニズムを使用するのは簡単です。 LINQ to SQLではサポートされていないため、データアクセスコードを手動で記述する必要がありますが、OOモデルを使用するクライアントコードは何らかの方法で変更されます。
Entityクラスは、「インターフェース」で宣言されるIProduct、IUser、IOrderなどのインターフェースを実装できますか?アセンブリ?このように、IRepositoryインターフェースはビジネスオブジェクトインターフェース(つまり、IProductなどのコレクションを返す)と「インターフェース」のみを参照します。アセンブリは、他の実装固有のアセンブリから分離されます。