EF6:使用AutoMapper将SelectMany作为左连接与运行时投影?

时间:2014-07-23 20:00:50

标签: linq entity-framework automapper

我正在尝试简化一些实体框架代码,这些代码从“用户”投影,然后根据查询将各种其他表LEFT JOIN连接到它。其中一个查询查看所有用户,并包含指示他们参与的程序(1或0)的数据,并返回UserProgram对象中的组合数据。它们通过电子邮件地址关联,而不是通过外键关联。它目前看起来像这样:

public class User 
{
   public virtual int Id {get; set;}
   public virtual String Email {get; set;}
   //...
}

public class Programmember
{    
   public virtual int Id {get; set;}
   public virtual int Programid {get; set;}
   public virtual String Email {get; set;}
   //...
}

public IEnumerable<UserProgram> GetUsersWithProgram(int programid)
{
    IQueryable<User> userQueryable = ...
    var query = userQueryable
        .SelectMany(
            user=> ctx.ProgramMembers.Where(
                program => program.Email == user.Email
                    && program.programid == programid)
                .DefaultIfEmpty()), // LEFT JOIN                
                (user,program) => 
                     new UserProgram {
                          Email = user.Email,
                          Programname = program.Name
                          // lots of other fields...
                     }

我想用AutoMapper替换对“new UserProgram”的调用,将两个对象组合成一个“DTO”对象,但我不知道是否可能。我见过的解决方案是使用.Project().To<UserProgram>()来构造SELECT,但要求我可以在用户和程序之间导航。 (我没有将其设置为从用户导航到程序,因为两个表之间的关系有点特殊,并且与外键无关。)

有没有办法在不明确更新UserProgram对象的情况下执行此操作?显然,我无法直接调用Mapper.Map,因为这不会映射到Linq-to-Entities查询。

1 个答案:

答案 0 :(得分:0)

你能尝试一下吗?

var query =     from c in userQueryable
                join program in ctx.ProgramMembers.Where(p=> p.programid == programid) ON c.Email    equals program.Email into joined
                from program in joined.DefaultOrEmpty()
                select new
                {
                    Email = c.Email,
                    ProgramName = program == null ? "(null)" : program.Name 
                };

或者您可以使用ToLookUp();

var ctx_lookUp = ctx.ProgramMembers.Where(p=>p.programid == programid).ToLookup(item => item.Email);

var query = from c in userQueryable
            select new
            {
               Email = c.Email,
               programName = ctx_lookup[c.Email].SingleOrDefault("(null)").Select(item=>item.Name)
            };

SingleOrDefault() - return one object or 0, if your database will have more that 1, then you will get exception :-)

请测试一下,我现在没有VS,而且我在没有调试器的情况下编写它。如果某件事不起作用,请回答。