24

遅延読み込み機能を備えた Linq to SQL に非常に興味があります。私のプロジェクトでは、AutoMapper を使用して DB モデルをドメイン モデルにマップしました (からDB_RoleInfoDO_RoleInfo)。以下のように私のリポジトリコードで:

    public DO_RoleInfo SelectByKey(Guid Key)
    {
        return SelectAll().Where(x => x.Id == Key).SingleOrDefault();
    }

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

SelectAllメソッドは正常に実行されますが、 を呼び出すSelectByKeyとエラーが発生します:

メソッド「RealMVC.Data.DO_RoleInfo MapDB_RoleInfo,DO_RoleInfo」を SQL に変換できませんでした。

Automapper が Linq を完全にサポートしていないということですか?

Automapper の代わりに、以下の手動マッピング コードを試しました。

public IQueryable<DO_RoleInfo> SelectAll()
{
    return from role in _ctx.DB_RoleInfo 
    select new DO_RoleInfo 
    {
        Id = role.id,
        name = role.name,
        code = role.code
    };
}

この方法は、私が望むように機能します。

4

2 に答える 2

67

執筆時点では@Aaronaughtの答えは正しかったのですが、世界は頻繁に変化し、AutoMapperもそれに伴いました。その間、QueryableExtensionsコード ベースに が追加され、式に変換されるプロジェクションのサポートが追加され、最後に SQL が追加されました。

コア拡張メソッドはProjectTo1です。コードは次のようになります。

using AutoMapper.QueryableExtensions;

public IQueryable<DO_RoleInfo> SelectAll()
{
    Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
    return _ctx.DB_RoleInfo.ProjectTo<DO_RoleInfo>();
}

手動マッピングのように動作します。(このCreateMapステートメントはデモンストレーション用です。通常は、アプリケーションの起動時に一度マッピングを定義します)。

したがって、マッピングに必要な列のみがクエリされ、結果はIQueryable元のクエリ プロバイダー (linq-to-sql、linq-to-entities など) を保持したままになります。WHEREしたがって、それはまだ構成可能であり、これはSQL の句に変換されます。

SelectAll().Where(x => x.Id == Key).SingleOrDefault();

1 Project().To<T>() v. 4.1.0 より前

于 2012-09-11T08:43:41.233 に答える
26

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_RoleInfoLinq はクラスについて何も知りません。マッピングを逆方向に行う方法がわかりません。場合によっては、それが不可能な場合もあります。確かに、このコードを見て「ああ、それは簡単です。で「管理者」または「エグゼクティブ」を検索するだけです」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 に公開されている実際のエンティティを隠そうとしないでください。ドメイン モデルは、パブリック インターフェイスです。作成するクエリは、非公開実装の一側面です。違いを理解し、関心事の適切な分離を維持することが重要です。

于 2010-02-06T05:18:16.340 に答える