如何簡化與實體框架的有多種關係的訪問?

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風格的crud之後建模了很多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'.

熱門答案

這不是一個小問題,也沒有“一刀切”的方法。你實際上看起來是延遲加載,由於很多原因,它不包含在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()方法。例如,您可以在顯示(或枚舉或...)時使用字符串,以僅選擇要包括的相關實體:

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

一種替代方案是始終包括所有導航屬性(遞歸地),但就查詢效率而言,這將是一種可怕的方法,因為每個查詢將是對所有相關表的一個大規模連接,無論是否必要。



許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因