我如何简化与实体框架具有一对多的关系的访问? [英] How do I simplify the access of a has-many relationship with the entity framework?

查看:116
本文介绍了我如何简化与实体框架具有一对多的关系的访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我想做的事:

  VAR用户= db.User.First(条件); 
user.Book.First();

下面是目前我怎么也得做。

  VAR用户= db.User.Include(尚书)第一(conditionsForUser)。 
user.Book.First();



我为什么要简化究其原因,是因为我不希望有什么规定包括我要访问的关系,每次。 。似乎很麻烦



如:我想刚好能做到以下几点,因为我以前检索的用户:

  user.Book.First()
user.Blog.First()
user.SomeOtherHasManyRelationship.Where(条件)

下面是我到目前为止有:

 公共对象RelationshipFor(字符串关系)
{
使用(VAR DB = User.DbContext())
{
VAR关系类型= TypeRepresentedBy(关系); //未使用的现在,不知道如果我需要的关系
变种为myTable的类型=((ICollection的)db.Send(RelationshipName)); // RelationshipName是在这种情况下用户。
VAR meWithRelationship = myTable.Where(I => i.Send(IdColumn)==编号).INCLUDE(关系); //目前,为myTable不知道去哪儿出于某种原因。
返回meWithRelationship.Send(关系);
}
}

然后,将如何使用将是以下

  user.RelationshipFor(尚书)//返回书
名单

我在我的代码一些其他的逻辑,抽象进一步这样可以让我做 user.Book.First()
希望我能获得许可,开源这个有很多,因为我模拟了很多的ActiveRecord式污物后的API。



请注意,我使用我设置的扩展我,以帮助应对动态性痛苦少: https://开头github上。 COM / NullVoxPopuli / CSHARP的扩展



更新1:



 公共对象RelationshipFor使用(串关系)
{
(VAR DB = User.DbContext())
{
VAR myTable的=(DbSet< DatabaseModels.User>) db.Send(RelationshipName);
VAR myInclude = myTable.Include(I => i.Send(关系));
VAR meWithRelationship = myInclude.First(I =>(长)i.Send(IdColumn)== ID);
返回meWithRelationship.Send(关系);
}
}



目前,我已经硬编码的投企图用户只得到的东西的工作。
我的错误是现在:

 无法转换类型'System.Linq.Expressions.MethodCallExpressionN对象键入System.Linq.Expressions.MemberExpression。 


解决方案

这不是一个简单的问题,而且也没有一刀切的做法。什么,你似乎实际上是后延迟加载,这是不包括在EF7原因是多方面的



我不知道你展示这些代码是应该做的,但其中一个方案是引入一个仓库模式,在您指定的实体包括在集合级别:

 公共类UserRepository 
{
私人只读的IQueryable<使用者> _数据集;

公共UserRepository(IQueryable的<使用者> userDataSet)
{
_dataSet = userDataSet;
}

公共IQueryable的<使用者>包括()
{
返回_dataSet.Include(U => u.Book)
.INCLUDE(U => u.Blog);
}
}

和可以移动大量的逻辑来通用基类,让你只用包括()方法。例如,您可以处理字符串为您展示(或枚举,或......),只选择与实体包括:

 公共类GenericRepository 
{
// ...

公共IQueryable的<使用者>包括(字符串includeGroup = NULL)
{
返回IncludeGroup(includeGroup);
}

受保护的虚拟IncludeGroup(字符串includeGroup)
{
返回_dataSet;
}
}



然后在 UserRepository

 保护覆盖的IQueryable<使用者> IncludeGroup(字符串includeGroup)
{
开关(includeGroup.ToUpperInvariant())
{
案书:
返回_dataSet.Include(U =>ü .Book)
.INCLUDE(U => u.Book.Author);
案BLOG:
返回_dataSet.Include(U => u.Blog);
默认:
返回base.Include(includeGroup);
}
}



,然后用它是这样的:

  VAR userRepo =新UserRepository(db.User); 

VAR userWithBooks = userRepo.Include(尚书);

VAR firstUser = userWithBooks.FirstOrDefault(U => u.Name ==富);

VAR firstUserFirstBook = firstUser.Book.FirstOrDefault();

一个替代方法是始终包括所有的导航性能(递归),但是这将是一个可怕的做法在查询效率方面,由于每个查询将是一台庞大的加盟所有相关表,无论是必要。


Here is what I want to do:

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

Here is currently how I have to do that.

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

The reason why I want to simplify this, is because I don't want to have to specify what is included every time I want to access a relationship. Seems very cumbersome.

e.g.: I would like to just be able to do the following, given I have previously retrieved a user:

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

Here is what I have so far:

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

And then how that would be used would be the following:

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

I have some other logic in my code which abstracts that further which would allow me to do user.Book.First(). Hopefully I can get permission to open source a lot of this, as I'm modelling a lot of the api after ActiveRecord-style crud.

Note, that I'm using I set of extensions I made to help dealing with dynamicness less painful: https://github.com/NullVoxPopuli/csharp-extensions

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

For now, I've hard coded the cast of the user in an attempt to just get something working. My error now is:

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

解决方案

This is not a trivial problem, and there's no "one size fits all" approach. What you actually seem to be after is lazy loading, which was not included in EF7 for many reasons.

I don't know what the code you show is supposed to do, but one option would be to introduce a repository pattern, where you specify the "entities to include" at the collection level:

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

And you can move lots of the logic to a generic base class, leaving you with just the Include() method. You can for example work with strings as you show (or enums, or ...), to only select related entities to include:

public class GenericRepository
{
    // ...

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

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

And then in 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);
    }
}

And then use it like this:

var userRepo = new UserRepository(db.User);

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

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

var firstUserFirstBook = firstUser.Book.FirstOrDefault();

One alternative would be to always include all navigation properties (recursively), but that would be a horrible approach in terms of query efficiency, as every query will be one massive join to all related tables, whether that is necessary or not.

这篇关于我如何简化与实体框架具有一对多的关系的访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆