码迷,mamicode.com
首页 > 其他好文 > 详细

Entity Framwork——Left Join

时间:2014-07-07 19:31:23      阅读:296      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   使用   strong   

在Entity Framework中使用left outer join 想必你们很多人都用过,但是为什么要那么用,我们一般也没去深究,这里稍微做一下探讨,不对的希望大家指正。

先准备我们的数据对象,这里使用最简单的one to many ,一个Category 对应多个Product,代码如下:

class Category
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
}
class Product
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public int CategoryId { get; set; }
}

查询代码:

 

var categories = new List<Category>()
{
    new Category() {CategoryId = 1, CategoryName = "Category-1"},
    new Category() {CategoryId = 2, CategoryName = "Category-2"},
    new Category() {CategoryId = 3, CategoryName = "Category-3"}
};

var products = new List<Product>()
{
    new Product() {ProductId = 1, CategoryId = 1, ProductName = "product c1-p1"},
    new Product() {ProductId = 2, CategoryId = 1, ProductName = "product c1-p2"},
    new Product() {ProductId = 3, CategoryId = 2, ProductName = "product c2-p3"},
    new Product() {ProductId = 4, CategoryId = 2, ProductName = "product c2-p4"},
    //new Product() {ProductId = 5, CategoryId = 3, ProductName = "product c3-p5"}
};

var result = from c in categories
    join p in products on c.CategoryId equals p.CategoryId into g
    from item in g.DefaultIfEmpty()
    select c;

 

这个地方,我们一般有3个地方不明白,

1. g.DefaultIfEmplty()这个函数是什么意思?为什么要使用它?

2. into g,这个g到底是一个什么格式的数据?

3. 使用Into 和不使用 into 有什么区别?为什么要使用into?

接下来我通过一些代码,来解答以上三个问题:

1.  DefaultIfEmplty 这个函数是什么意思?为什么要使用它?

首先我们从字面上就可以看出这个函数大体是干啥的,如果为空就返回默认值,然后去看看微软官方是怎么解释这个函数的,

方法的签名:

public static IEnumerable<TSource> DefaultIfEmpty<TSource>(
	this IEnumerable<TSource> source
)
这个函数的解释:
Returns the elements of the specified sequence or the type parameter‘s default value in a singleton collection if the sequence is empty.
返回指定序列的元素;如果序列为空,则返回单一实例集合中的类型参数的默认值。
我相信很多人看到这个解释后,都一头雾水,本来有点明白的,结果一看反而不明白了,先看这个方法的第一个参数有一个前缀this, 就知道这是一个扩展方法了,
而且是扩展了所有实现了IEnumerabe<TSource>接口的类,说白了就是对序列元素的一个扩展(数组,list, 等我们经常使用的那些),看例子:
var categories = new List<Category>();
var defaultCategories = categories.DefaultIfEmpty();

Console.WriteLine("category‘s count: {0}",categories.Count);
Console.WriteLine("defaultCategories‘s count: {0}",defaultCategories.Count());

Console.WriteLine("loop defaultCategories--------------");
foreach (var category in defaultCategories)
{
    if (category == null)
        Console.WriteLine("category is null");
    else
        Console.WriteLine(category.CategoryId);
}

/*
    This code produces the following output:
    category‘s count: 0
    defaultCategories‘s count: 1
    loop defaultCategories--------------
    category is null
    */
我们可以看到,categories有0个元素,但是defaultCategories有1个元素,然后循环defaultCategories发现,它有一个null的元素,看到这里这个方法的作用,
我相信都看的非常清楚了。
2. into g,这个g到底是一个什么格式的数据?废话不多说,看代码:
var result = (from c in categories
    join p in products on c.CategoryId equals p.CategoryId into g
    select g).FirstOrDefault();

foreach (var ret in result)
{
    Console.WriteLine(ret.ProductName);
}

/*
    * This code produces the following output:
    * product c1-p1
    * product c1-p2
    */

我们可以看到g 实际上是一个List<Product>的集合。

3. 使用Into 和不使用 into 有什么区别?为什么要使用into?

不使用 INTO 时查询出来的数据是一个平面二维的数据表,类似于这样的

Category-1 product c1-p1
Category-1 product c1-p2
Category-2 product c2-p3
Category-2 product c2-p3

用代码验证一下:

var result = from c in categories
    join p in products on c.CategoryId equals p.CategoryId
    select new {c.CategoryName, p.ProductName};

foreach (var ret in result)
{
    Console.WriteLine("{0}----{1}",ret.CategoryName,ret.ProductName);
}
/*
* This code produces the following output:
* Category-1----product c1-p1
* Category-1----product c1-p2
* Category-2----product c2-p3
* Category-2----product c2-p4
*/
当使用INTO 是数据查询出来是一个类似字典的集合<Category, List<Product>>这样的一个集合,本来想弄个表格来展示的,折腾了半天怎么也不能合并行,只好作罢
改用文字描述吧:
Category-1, Products:{product 1, product2}; 
Category-2, Products: {product 3, product 4};
大体就是上面这个样子了,同样我们整段代码瞧瞧:
var result = from c in categories
    join p in products on c.CategoryId equals p.CategoryId into g
    select new {c, g};

foreach (var ret in result)
{
    Console.Write("{0}: ",ret.c.CategoryName);
    foreach (var product in ret.g)
    {
        Console.Write("  {0}",product.ProductName);
    }
    Console.WriteLine();
}

/*
* This code produces the following output:
* Category-1:   product c1-p1  product c1-p2
* Category-2:   product c2-p3  product c2-p4
*/
 
OK,三个问题,我们都明白了,现在开始说正事,先看看inner join, 也就是不用DefaultIfEmpty方法的时候
Inner Join
var result = from c in categories
    join p in products on c.CategoryId equals p.CategoryId into g
    from d in g
    select new {CName= c.CategoryName, PName= d.ProductName};

foreach (var ret in result)
{
    Console.WriteLine("{0}---{1}",ret.CName,ret.PName);
}

/*
* This code produces the following output:
* Category-1---product c1-p1
* Category-1---product c1-p2
* Category-2---product c2-p3
* Category-2---product c2-p4
*/
Left Join
var result = from c in categories
    join p in products on c.CategoryId equals p.CategoryId into g
    from d in g.DefaultIfEmpty()
    select new {CName= c.CategoryName, PName= d==null? "no products" : d.ProductName};

foreach (var ret in result)
{
    Console.WriteLine("{0}---{1}",ret.CName,ret.PName);
}

/*
* This code produces the following output:
* Category-1---product c1-p1
* Category-1---product c1-p2
* Category-2---product c2-p3
* Category-2---product c2-p4
* Category-3---no products
*/
看到不同之处了没,有三个个地方不同,
第一个:使用inner join的时候,我们直接使用 [from d in g], 但是在left join时用的是[from d in g.DefaultIfEmpty()], 
第二个:我们在left join 的select 子句中,去判断了d是否等于null:[select new {CName= c.CategoryName, PName= d==null? "no products" : d.ProductName}; ] .
    为什么要这么做,就是应为如果当一个category没有找到匹配的product集合时,我们通过DefaultIfEmpty返回了一个含有一个null 元素的List<Product> 集合,
    然后我们[from d in g.DefaultIfEmpty()], 那么这个d就有可能为Null了,
    这就是我们为什么要去判断d 是否为空的原因了。
第三个:这个不同就是输出信息的不同了,最后多了个[Category-3---no products],从这里我们终于看到了有一个category 没有对应的产品,这就是left outer join

终于把Linq to object 的Left Join说明白了,但是还是跟我们的Entity Framework没一根毛的关系,我们创建一个Category, Product表,然后通过DB first来创建我们的entity 实体,

下面是建表语句:

CREATE TABLE Category
(CategoryId INT PRIMARY KEY IDENTITY(1,1),
CategoryName VARCHAR(255));


CREATE TABLE Product
(ProductId INT PRIMARY KEY IDENTITY(1,1),
ProductName VARCHAR(255),
CategoryId INT);

ALTER TABLE Product
ADD CONSTRAINT FK_Category_Product
FOREIGN KEY(CategoryId)
REFERENCES Category(CategoryId);

INSERT INTO Category
VALUES(Category-1);

INSERT INTO Category
VALUES(Category-2);

INSERT INTO Category
VALUES(Category-3);

INSERT INTO Product(ProductName, CategoryId)
VALUES(Product c1-p1, 1);

INSERT INTO Product(ProductName, CategoryId)
VALUES(Product c1-p2, 1);

INSERT INTO Product(ProductName, CategoryId)
VALUES(Product c1-p3, 2);

INSERT INTO Product(ProductName, CategoryId)
VALUES(Product c1-p4, 2);

GO

怎么通过db first 建立entity 实体,这里不讲,准备工作做好后,来看下面代码:

using (var ctx = new EFInActionEntities())
{
    var result = from c in ctx.MyCategories
        join p in ctx.MyProducts on c.CategoryId equals p.CategoryId into g
        from d in g.DefaultIfEmpty()
        select new {CName = c.CategoryName, PName = d == null ? "No Products" : d.ProductName};

    foreach (var ret in result)
    {
        Console.WriteLine("{0}--{1}", ret.CName,ret.PName);
    }
}

/*
* Category-1--Product c1-p1
* Category-1--Product c1-p2
* Category-2--Product c1-p3
* Category-2--Product c1-p4
* Category-3--No Products
*/

可以看到,输出的结果,和Linq to Object 时是一样的,看看生成的SQL吧:

SELECT [Extent1].[CategoryId]   AS [CategoryId],
       [Extent1].[CategoryName] AS [CategoryName],
       CASE
         WHEN ([Extent2].[ProductId] IS NULL) THEN NNo Products
         ELSE [Extent2].[ProductName]
       END                      AS [C1]
FROM   [dbo].[Category] AS [Extent1]
       LEFT OUTER JOIN [dbo].[Product] AS [Extent2]
         ON [Extent1].[CategoryId] = [Extent2].[CategoryId]

差不多就这些了,不知道你们明白了没有,关键点就是要搞明白 into 语句, 和into 语句得到的数据格式!

Entity Framwork——Left Join,布布扣,bubuko.com

Entity Framwork——Left Join

标签:style   blog   http   color   使用   strong   

原文地址:http://www.cnblogs.com/codisan/p/3813330.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!