2 番目の関数を次のように変更します。
public IEnumerable<DO_RoleInfo> SelectAll()
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return from role in _ctx.DB_RoleInfo.ToList()
select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role);
}
AutoMapper は Linq to SQL で問題なく動作しますが、遅延クエリの一部として実行することはできません。Linq クエリの最後に追加するToList()
と、AutoMapper セグメントをクエリの一部として変換しようとするのではなく、すぐに結果を評価します。
明確化
遅延実行( 「遅延読み込み」ではない)の概念は、結果の型をデータ エンティティではないものに変更すると意味がありません。次の 2 つのクラスを検討してください。
public class DB_RoleInfo
{
public int ID { get; set; }
public string Name { get; set; }
}
public class DO_RoleInfo
{
public Role Role { get; set; } // Enumeration type
}
ここで、次のマッピングを検討してください。
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>
.ForMember(dest => dest.Role, opt => opt.MapFrom(src =>
(Role)Enum.Parse(typeof(Role), src.Name)));
SelectAll
このマッピングはまったく問題ありません (タイプミスをしない限り) が、修正した投稿ではなく元の投稿にメソッドを書いたとしましょう。
public IQueryable<DO_RoleInfo> SelectAll()
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return from role in _ctx.DB_RoleInfo
select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role);
}
これは実際には機能しますが、それ自体を「クエリ可能」と呼ぶことで嘘をつきます。これに対してこれを書き込もうとするとどうなりますか:
public IEnumerable<DO_RoleInfo> SelectSome()
{
return from ri in SelectAll()
where (ri.Role == Role.Administrator) ||
(ri.Role == Role.Executive)
select ri;
}
これについて本当によく考えてください。Linqwhere
to SQLは、実際のデータベース クエリにどのように変換できるのでしょうか?
DO_RoleInfo
Linq はクラスについて何も知りません。マッピングを逆方向に行う方法がわかりません。場合によっては、それが不可能な場合もあります。確かに、このコードを見て「ああ、それは簡単です。列で「管理者」または「エグゼクティブ」を検索するだけです」Name
と思うかもしれませんが、それを知っているのはあなただけです。 Linq to SQL に関する限り、クエリはまったくナンセンスです。
誰かがあなたにこれらの指示を与えたと想像してください:
スーパーに行って、モートン・トンプソンの七面鳥を作るための材料を持ち帰ってください。
あなたが以前にそれを作ったことがない限り、そしてほとんどの人がそうでない場合、その指示に対するあなたの反応はおそらく次のようになるでしょう:
市場に行けば、特定の材料を名前で入手できますが、そこにいる間は、私が与えた条件を評価することはできません. 最初に基準を「マッピング解除」する必要があります。このレシピに必要な材料は次のとおりです。
要約すると、これはLinq to SQL と AutoMapper の間の単純な非互換性ではありません。これら 2 つのライブラリのいずれかに固有のものではありません。非エンティティ型へのマッピングを実際にどのように行うかは問題ではありません。マッピングを手動で簡単に行うこともできますが、Linq to SQL に一連の指示を与えているため、同じエラーが発生します。特定のエンティティタイプへの固有のマッピングを持たない謎のクラスを扱い、もはや理解できないもの。
この問題は、O/R マッピングと遅延クエリ実行の概念の基本です。プロジェクションは一方向の操作 です。いったん投影すると、クエリ エンジンに戻って「ちなみに、ここにいくつかの条件があります」と言うことができなくなります。手遅れです。あなたができる最善のことは、すでに与えられているものを利用して、追加の条件を自分で評価することです.
最後になりましたが、回避策をご紹介します。マッピングから実行できるようにしたいのが行のフィルターのみである場合は、次のように記述できます。
public IEnumerable<DO_RoleInfo> SelectRoles(Func<DB_RoleInfo, bool> selector)
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return _ctx.DB_RoleInfo
.Where(selector)
.Select(dbr => Mapper.Map<DB_RoleInfo, DO_RoleInfo>(dbr));
}
これは、マッピングを処理し、マップされたエンティティではなく、元のエンティティでフィルターを受け入れるユーティリティ メソッドです。さまざまな種類のフィルターがあり、常に同じマッピングを行う必要がある場合に便利です。
個人的には、最初にデータベースから取得する必要があるものを決定し、次にプロジェクション/マッピングを実行し、最後にさらにフィルタリングを行う必要がある場合は、クエリを適切に書き出す方がよいと思います(これはべきではない)、結果をorToList()
でToArray()
具体化し、ローカル リストに対してさらに条件を書き込みます。
AutoMapper やその他のツールを使用して、Linq によって SQL に公開されている実際のエンティティを隠そうとしないでください。ドメイン モデルは、パブリック インターフェイスです。作成するクエリは、非公開実装の一側面です。違いを理解し、関心事の適切な分離を維持することが重要です。