エンティティフレームワークと多対多関係のアクセスを簡素化するにはどうすればよいですか?

c# entity-framework entity-framework-core

質問

ここに私がしたいことがあります:

var user = db.User.First(conditions);
user.Book.First();

ここに私はそれをどうやってしなければならないのですか?

var user = db.User.Include("Book").First(conditionsForUser);
user.Book.First();

私がこれを単純化したいのは、私が関係にアクセスするたびに何が含まれているのかを指定したくないからです。非常に扱いにくいようです。

例:以前にユーザーを検索した場合、次のことができるようにしたいと考えています。

user.Book.First()
user.Blog.First()
user.SomeOtherHasManyRelationship.Where(conditions)

これまで私がこれまで持っていたことは次のとおりです。

    public object RelationshipFor(string relationship)
    {
        using (var db = User.DbContext())
        {
            var relationshipType = TypeRepresentedBy(relationship); // unused for now, not sure if I need the type of the relationship
            var myTable = ((ICollection)db.Send(RelationshipName)); // RelationshipName is "User" in this instance.
            var meWithRelationship = myTable.Where(i => i.Send(IdColumn) == Id).Include(relationship);  // currently, myTable doesn't know about 'Where' for some reason.
            return meWithRelationship.Send(relationship);
        }
    }

それがどのように使用されるのかは次のようになります。

user.RelationshipFor("Book") // returns a list of books

私は私のコードの中で私がuser.Book.First()を行うことを可能にするそれをさらに抽象化するいくつかの他のロジックを持っています。うまくいけば、私はActiveRecordスタイルの荒れ果てた後、多くのAPIをモデリングしているので、これを多くソースをオープンする権限を得ることができます。

私が動的なものに対処するのを助けるために作った拡張機能は、痛みを伴うものではないことに注意してください: https : //github.com/NullVoxPopuli/csharp-extensions

更新1:

    public object RelationshipFor(string relationship)
    {
        using (var db = User.DbContext())
        {
            var myTable = (DbSet<DatabaseModels.User>)db.Send(RelationshipName);
            var myInclude = myTable.Include(i => i.Send(relationship));
            var meWithRelationship = myInclude.First(i => (long)i.Send(IdColumn) == Id);
            return meWithRelationship.Send(relationship);
        }
    }

今のところ、私はちょうど何かを働かせるためにユーザーのキャストをハードコーディングしました。私のエラーは今です:

Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'.

人気のある回答

これは些細な問題ではなく、「1つのサイズはすべてに適合」というアプローチはありません。あなたが実際に思っているのは、多くの理由でEF7に含まれていなかった遅延ロードです。

表示するコードが何をすべきかはわかりませんが、コレクションレベルで「包含するエンティティ」を指定するリポジトリパターンを導入することもできます。

public class UserRepository
{
    private readonly IQueryable<User> _dataSet;

    public UserRepository(IQueryable<User> userDataSet)
    {
        _dataSet = userDataSet;
    }

    public IQueryable<User> Include()
    {
        return _dataSet.Include(u => u.Book)
                       .Include(u => u.Blog);
    }
}

また、多くのロジックを汎用基本クラスに移すことができ、 Include()メソッドだけでInclude()ようになりInclude() 。たとえば、表示している文字列(または列挙型、または...など)を操作して、関連するエンティティを選択するだけで、次のものを含めることができます。

public class GenericRepository
{
    // ...

    public IQueryable<User> Include(string includeGroup = null)
    {
        return IncludeGroup(includeGroup);
    }

    protected virtual IncludeGroup(string includeGroup)
    {
        return _dataSet;
    }
}

そしてUserRepository

protected override IQueryable<User> IncludeGroup(string includeGroup)
{
    switch (includeGroup.ToUpperInvariant())
    {
        case "BOOK":
            return _dataSet.Include(u => u.Book)
                           .Include(u => u.Book.Author);
        case "BLOG":
            return _dataSet.Include(u => u.Blog);
        default:
            return base.Include(includeGroup);
    }
}

そして、次のように使用してください:

var userRepo = new UserRepository(db.User);

var userWithBooks = userRepo.Include("Book");

var firstUser = userWithBooks.FirstOrDefault(u => u.Name == "Foo");

var firstUserFirstBook = firstUser.Book.FirstOrDefault();

1つの方法は常にすべてのナビゲーションプロパティを(再帰的に)含めることですが、それは必要か否かにかかわらず、すべてのクエリが関連するすべてのテーブルへの大規模な結合となるため、クエリ効率の面で恐ろしいアプローチになります。



Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ