Linq to Entities 蛋疼锦集 第一期

 【开发环境】

框架:EF4.1(Linq to Entities) MVC3.0 
数据库:mySQL
 
【场景描述】
1、两表主键关联,主表与从表的关系为一对多(即从表至少有两个主键)。[其实是多对多表中,主表和中间表的关系]看图:
主表:M表:

 

从表:C表

 
很容易看出,M表主键id和C表的第一个主键id关联。
 
2、现在我造一些数据,并用图画出需要返回的数据格式。看图:
 
M表数据

 

C表数据

 

返回数据(主表同时带出对应从表  分页后的数据,如图为每页5条数据)

[不知道读者注意到没有,该返回数据中,没有分页栏,

因为,分页跳转等不是本文重点,为减轻DEMO,

说明问题,仅在后台进行分页,前台返回对应结果即可]

【Just Code It】

1、Entity

    public class M
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
public string Keywords { get; set; }

[ForeignKey("Id")]
public virtual ICollection<C> c { get; set; }
}

 

[Table("c")]
public class C
{
[Key]
[Column(Order = 1)]
public long Id { get; set; }

[Key]
[Column(Order = 2)]
public string code { get; set; }
}

 

2、Model

public class resultModel
{
public long id { get; set; }
public string keywords { get; set; }
public IEnumerable<string> codes { get; set; }
}

 

3、Controller

private XLContext db = new XLContext();

public ViewResult Index(int pageIndex=1,int pageSize=5)
{
var result = db.M
.Select(p =>new resultModel{
id = p.Id,
keywords = p.Keywords,
codes = p.c.Select(c => c.code)
})
.OrderBy(p => p.id)
.Skip((pageIndex-1)*pageSize)
.Take(pageSize)
.ToList();

return View(result);
}

 

4、View页面就不给出来了,不是重点。问题来了。看图:

 

 

【问题分析】

仅仅根据报错信息,空对象,很难看出哪里出的问题,当然有经验的人肯定一眼就看出来问题的关键。

经过简单排除,我将问题范围缩小到Skip、Take、ToList

继续排除,如下几种情况都可以通过:

var result =r2.Skip(5);

var result =r2.Skip(5).Take(5);

var result =r2.Take(5);

var result =r2.ToList();

 

如下几种情况不通过:

var result =r2.Skip(5).ToList();

var result =r2.Take(5).ToList();

var result =r2.Skip(5).Take(5).ToList();

 

也就是说,使用了Skip()和Take()方法之后,不可以ToList()。

这是为什么呢?

跑到MSDN上查了相关信息:支持的和不支持的方法 (LINQ to Entities)

资料显示,其实,LINQ to Entities肯定是支持skip和take的,不然怎么分页啊,傻!

突然联想到,难道是因为LINQ to Entities不支持复杂类型?

我这里确实用到了复杂类型:

  public class resultModel
{
public long id { get; set; }
public string keywords { get; set; }
public IEnumerable<string> codes { get; set; }
}

Model里面包含了 IEnumerable<string>.于是将其替换成 :

public class resultModel
{
public long id { get; set; }
public string keywords { get; set; }
//public IEnumerable<string> codes { get; set; }
public int counts { get; set; }
}

在controller层也做如下修改:

  var result = db.M
.Select(p =>new resultModel{
id = p.Id,
keywords = p.Keywords,
counts = p.c.Count
})
.OrderBy(p => p.id)
.Skip((pageIndex-1)*pageSize)
.Take(pageSize)
.ToList();

此时,顺利取出数据,并正确带出了counts数目。也就印证了,问题的关键:

LINQ to Entities不支持复杂类型

但是,我又该如何解决问题呢?我需要取出那样子的结构,怎么去实现呢?

如果可以实现拼接string的话, IEnumerable<string>可以用带分隔符的string来替代。

在MSDN上查了很多资料,LINQ to Entities也没有找到什么方法可以支持拼接string。

无奈,谷哥提示我,实在没办法就舍弃掉关联,使用Join语句。

所以,产生了以下解决办法:

先通过GroupJoin查询出符合条件的主表主键,进行排序后分页。再通过关联,取出从表数据。

这样,就避免了关联后分页。

代码如下: 

public ViewResult Index(int pageIndex=1,int pageSize=5)
{
//取出符合条件的id,分页
var IdList = db.M.GroupJoin(db.C,
m => m.Id,
c => c.Id,
(m, c) => m.Id)
.OrderBy(p => p)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToList();

//根据上面已经分好页的id,取出所有数据和关联数据
var result = db.M
.Where(p => IdList.Contains(p.Id))
.Select(q => new resultModel
{
id = q.Id,
keywords = q.Keywords,
codes = q.c.Select(c => c.code)
})
.ToList();

return View(result);
}

OK.

这样做确实解决了问题。得到了想要的结果。

【Demo 下载】

但是,效率上真心打折扣了!希望有大牛给出更好的解决方案!还有各位的各种吐槽,非常欢迎!

下篇将分析和这个问题有点关系的第二个棘手的问题。慢慢等吧!

posted @ 2011-11-30 22:46  Tiny Xu  阅读(3186)  评论(21)    收藏  举报