简体   繁体   English

Linq to Entities:左联接并过滤

[英]Linq to Entities : Left Join and filter

I have 3 tables : 我有3张桌子:

  • USERS_TABLE(userid, name) USERS_TABLE(用户名,名称)
  • ACTIVITY_HISTORY_TABLE(userid, datefrom,dateto,activityId) ACTIVITY_HISTORY_TABLE(用户ID,datefrom,dateto,activityId)
  • ACTIVITY_TABLE (activityId, name) ACTIVITY_TABLE(activityId,名称)

In EF these are the below entities coresponding to the tables 在EF中,以下是与表相对应的实体

  • User(UserId,Name, AssignedActivities) AssignedActivities beeing the navigation property User(UserId,Name,AssignedActivities)AssignedActivities是导航属性
  • ActivityHistory (UserId, DateFrom,DateTo,activityId,TheActivity) TheActivity being the navigation property ActivityHistory(UserId,DateFrom,DateTo,activityId,TheActivity)TheActivity是导航属性
  • Activity (ActivityId, Name) 活动(ActivityId,名称)

I need to return a List with all my users including the activities they are doing at the time of the requests (ActivityHistory.Datefrom <= now and ActivityHistory.DateTo >= now). 我需要返回一个列表,其中包含我所有的用户,包括他们在请求时正在执行的活动(ActivityHistory.Datefrom <=现在和ActivityHistory.DateTo> =现在)。

Users with no current activity are also to be returned. 没有当前活动的用户也将被返回。

Users that have multiple activities must not be duplicated 具有多个活动的用户不得重复

I need "User.AssignedActivities" and "ActivityHistory.TheActivity" to be loaded (no lazy loading). 我需要加载“ User.AssignedActivities”和“ ActivityHistory.TheActivity”(不能延迟加载)。

So far I have 4 solutions that all fail one of my needs 到目前为止,我有4种解决方案都无法满足我的需求之一

1 - Only returns users that have activities at the given time and not all of the users 1-仅返回在给定时间有活动的用户,而不是所有用户

public List<User> GetAll()
{
  using (Entities _db = new Entities())
  {
    return _db.USERS_TABLE
       .Include("ActivityHistory")
       .Include("ActivityHistory.Activity")
       .Where(x => x.ActivityHistory.Any(y => y.DateFrom <= DateTime.Now && y.DateTo >= DateTime.Now))
       .ToList();    
   }
}

2 - Returns the correct list of Users (complete with no duplicates) but activities are not loaded (System.ObjectDisposedException when manipulating User.AssignedActivities in the returned list) 2-返回正确的用户列表(完整,没有重复),但未加载活动(在返回的列表中操作User.AssignedActivities时,出现System.ObjectDisposedException)

(solution seen here : EF Query With Conditional Include ) (解决方案在这里: 带有条件Include的EF查询

public List<User> GetAll()
{
  using (Entities _db = new Entities())
  {
    var query = from users in _db.USERS_TABLE
                select new
                {
                    users,
                assignments = from ActivityAssigned in users.AssignedActivities
                              .Where(y => y.DateFrom <= now && y.DateTo >= now)
                              .DefaultIfEmpty()
                              select ActivityAssigned
            };

var query2 = query
    .AsEnumerable()
    .Select(x => x.users);

return query2.ToList();
   }
}

3 - Returns duplicates when users have many activities and the activies are not loaded (System.ObjectDisposedException when manipulating User.AssignedActivities in the returned list) 3-当用户有许多活动且未加载活动时返回重复项(在返回的列表中处理User.AssignedActivities时,出现System.ObjectDisposedException)

(Solution from Steve Ruble here below) (下面是史蒂夫·卢布的解决方案)

public List<User> GetAll()
{
  using (Entities _db = new Entities())
  {
    var query = 
     from u in _db.USERS_TABLE
     select new {
     User = u,
     CurrentActivities = u.ActivityHistory
                          .Where(y => 
                                 y.DateFrom <= DateTime.Now
                              && y.DateTo >= DateTime.Now)
                          .Select(ah => new 
                          {
                            ActivityHistory = ah,
                            Activity = ah.Activity
                          }
     }

return query.AsEnumerable().Select(x=> x.User).ToList();    
}
}   

4 - Does what I want but takes forever to do it, using the repository of the ActivityHistory entity: 4-使用ActivityHistory实体的存储库执行我想做的但要花很长时间的事情:

public List<User> GetAll()
{
  using (Entities _db = new Entities())
  {
List<Users> MyUsersList = new List<Users>();

MyUsersList = _db.USERS_TABLE
    .Include("ActivityHistory")
    .Include("ActivityHistory.Activity")
    .ToList();

for (int i = 0; i < MyUsersList.Count(); i++)
{
    List<ActivityHistory > UserCurrentActivityList = new List<ActivityHistory >();

    ActivityListRepository activityListRepo = new ActivityListRepository();

    UserCurrentActivityList = activityListRepo.GetUserCurrentActivity(MyUsersList[i].UserId);

    MyUsersList[i].AssignedActivities = UserCurrentActivityList;
}

return MyUsersList; 
   }
}               

Help :) ! 救命 :) !

_db.Database.EnableLazyLoading = false;
_db.Database.UseProxyObjects = false;
var now = DateTime.Now; //DON'T REFACTOR THIS LINE!!!
var query = from user in _db.USERS_TABLE
            from activity in user.ACTIVITY_HISTORY
            group activity by user into g
            select new {
               User = g.Key,
               CurrentActivities = g.Where(activity =>
                           activity.DateFrom <= now && activity.DateTo >= now)
            };
return query.ToList().Select(x => x.User).ToList();

Edit: Fixed...I hope. 编辑:固定...我希望。 However I would advise against using this query. 但是,我建议不要使用此查询。 Its hugely hacky. 它非常hacky。 I STRESS that if you do want to have a lookup for CurrentActivities I would use a data structure to store that. 我强调,如果您确实想对CurrentActivities查找,我将使用数据结构来存储它。

The fact that this works is held together by some nuances of how EF works. EF工作原理的某些细微差别将这一工作原理结合在一起。 It is hugely bad practice and YOU WILL end up with a lot of problems down the line. 这是非常糟糕的做法,您最终会遇到很多问题。

If what you want is a list of all users, with the current activity or null for each user, this should do it. 如果您想要的是所有用户的列表(具有当前活动) 每个用户为null ,则应这样做。 The trick is in the DefaultOrEmpty , which gets translated into a LEFT OUTER JOIN on the SQL side. 诀窍在于DefaultOrEmpty ,它在SQL端被转换为LEFT OUTER JOIN。

from u in _db.USERS_TABLE
from aht in u.ActivityHistory
             .Where(y => y.DateFrom <= DateTime.Now && y.DateTo >= DateTime.Now)
             .DefaultIfEmpty()
select new {
    User = u,
    CurrentActivityHistory = ah
    CurrentActivity = ah.Activity
}

EDIT 编辑

If what you want is a list of all users, with all the current ActivityHistory records, and with the activity eagerly loaded for each ActivityHistory, you can do this: 如果您想要的是所有用户的列表,所有当前的ActivityHistory记录以及为每个ActivityHistory紧急加载的活动,则可以执行以下操作:

var query = 
 from u in _db.USERS_TABLE
 select new {
     User = u,
     CurrentActivities = u.ActivityHistory
                          .Where(y => 
                                 y.DateFrom <= DateTime.Now
                              && y.DateTo >= DateTime.Now)
                          .Select(ah => new 
                          {
                            ActivityHistory = ah,
                            Activity = ah.Activity
                          }
 }

Note concerning the materialized entities: 关于实体化实体的注释:

Consider the value of this variable: 考虑以下变量的值:

var record = query.First(); var record = query.First();

The property record.User.ActivityHistory will be populated with a collection containing the same ActivityHistory objects as record.CurrentActivities , even if the user has many more non-current ActivityHistory records in the database. 属性record.User.ActivityHistory将填充一个包含与record.CurrentActivities相同的ActivityHistory对象的record.CurrentActivities ,即使用户在数据库中还有更多非当前ActivityHistory记录。 In other words, if you need to get all the ActivityHistory records for a User , you will need to make another query, rather than relying on lazy-loading to pull them in. 换句话说,如果您需要获取User 所有 ActivityHistory记录,则需要进行另一个查询,而不是依靠延迟加载来获取它们。

And finally, after much too many hours spent on this subject I finally found my solution (which I believe is somehow close to the Relationship Fixup concept, but i might be saying an absurdity here). 最后,在花了太多时间在这个问题上之后,我终于找到了解决方案(我认为这在某种程度上与“关系修正”的概念很接近,但是在这里我可能会说荒谬)。

public List<User> GetAll()
{
   using (Entities _db = new Entities())
   {
      _db.Configuration.LazyLoadingEnabled = false;

      var now = DateTime.Now;

      var currentActivities = _db.ActivityHistory 
                                .Where(x => x.DateFrom <= now && x.DateTo >= now)
                                .Include("TheActivity")
                                .ToList();

      var Controlers = _db.User.ToList();

      return Controlers;
   }
}

Actually the answer was Here all the time : 实际上答案一直在这里

Issue two separate queries: one for the Movies, one for the Reviews, and let relationship fix-up do the rest. 发出两个单独的查询:一个用于“电影”,一个用于“评论”,其余的工作由关系修复完成。

Any comment appreciated thought. 任何评论赞赏的想法。 Thanks. 谢谢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM