• Entity Framework Code First实体关联数据加载


      在项目过程中,两个实体数据之间在往往并非完全独立的,而是存在一定的关联关系,如一对一、一对多及多对多等关联。存在关联关系的实体,经常根据一个实体的实例来查询获取与之关联的另外实体的实例。

      Entity Framework常用处理数据关联加载的方式有3种:延迟加载(Lazy Loading)、贪婪加载(Eager Loading)以及显示加载(Explicit Loading)。

      1、延迟加载(Lazy Loading)

      延迟加载是项目应用中常见的方式,Entity Framework在需要时可以自动为一个实体的实例获取关联的数据。

      Entity Framework自动延迟加载需要满足的条件:

      1>、POCO类必须是public而非sealed;

      2>、集合属性必须的Virtual修饰的,这样Entity Framework才能Override以包含延迟加载的逻辑。

      示例:

      文件类Province.cs:

    using System;
    using System.Collections.Generic;
    
    namespace Portal.Models
    {
        public class Province
        {
            public Province()
            {
                this.Cities = new List<City>();
            }
    
            public int ProvinceID { get; set; }
            public string ProvinceNo { get; set; }
            public string ProvinceName { get; set; }
            public virtual ICollection<City> Cities { get; set; }
        }
    }
    View Code

      文件类City.cs:

    using System;
    using System.Collections.Generic;
    
    namespace Portal.Models
    {
        public class City
        {
            public int CityID { get; set; }
            public Nullable<int> ProvinceID { get; set; }
            public string CityNo { get; set; }
            public string CityName { get; set; }
            public virtual Province Province { get; set; }
        }
    }
    View Code

      文件类Program.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using System.Data.Entity;
    
    using Portal.Models;
    
    namespace Portal
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (var ctx = new PortalContext())
                {
                    var province = ctx.Provinces.Find(3);
    
                    foreach (var city in province.Cities)
                    {
                        Console.WriteLine(city.CityName);
                    }
                }
            }
        }
    }
    View Code

      以上代码在运行之后,执行了两条SQL语句,分别用于读取单条Province记录及与该条记录相关联的City记录。

    exec sp_executesql N'SELECT 
    [Limit1].[ProvinceID] AS [ProvinceID], 
    [Limit1].[ProvinceNo] AS [ProvinceNo], 
    [Limit1].[ProvinceName] AS [ProvinceName]
    FROM ( SELECT TOP (2) 
        [Extent1].[ProvinceID] AS [ProvinceID], 
        [Extent1].[ProvinceNo] AS [ProvinceNo], 
        [Extent1].[ProvinceName] AS [ProvinceName]
        FROM [dbo].[Province] AS [Extent1]
        WHERE [Extent1].[ProvinceID] = @p0
    )  AS [Limit1]',N'@p0 int',@p0=3
    exec sp_executesql N'SELECT 
    [Extent1].[CityID] AS [CityID], 
    [Extent1].[ProvinceID] AS [ProvinceID], 
    [Extent1].[CityNo] AS [CityNo], 
    [Extent1].[CityName] AS [CityName]
    FROM [dbo].[City] AS [Extent1]
    WHERE [Extent1].[ProvinceID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3

      延迟加载的不足:

      延迟加载使用简单,应用程序不需要真正知道数据已经被从数据库中加载出来,但只要将可能导致大量的SQL查询被发送到数据库中执行,数据库进行了不必要的查询。

      2、贪婪加载(Eager Loading)

      贪婪加载:使用Include加载关联的数据,在Entity Framework进行查询时,即同时加载出关联的数据。Entity Framework贪婪加载将使用一条JOIN的SQL语句进行查询。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using System.Data.Entity;
    
    using Portal.Models;
    
    namespace Portal
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (var ctx = new PortalContext())
                {
                    var provinces = ctx.Provinces
                        .Include(p => p.Cities);
    
                    foreach (var province in provinces)
                    {
                        foreach (var city in province.Cities)
                        {
                            Console.WriteLine("{0}-{1}", province.ProvinceName, city.CityName);
                        }
                    }
                }
            }
        }
    }
    View Code

      运行代码所执行的SQL语句:

    SELECT 
    [Project1].[ProvinceID] AS [ProvinceID], 
    [Project1].[ProvinceNo] AS [ProvinceNo], 
    [Project1].[ProvinceName] AS [ProvinceName], 
    [Project1].[C1] AS [C1], 
    [Project1].[CityID] AS [CityID], 
    [Project1].[ProvinceID1] AS [ProvinceID1], 
    [Project1].[CityNo] AS [CityNo], 
    [Project1].[CityName] AS [CityName]
    FROM ( SELECT 
        [Extent1].[ProvinceID] AS [ProvinceID], 
        [Extent1].[ProvinceNo] AS [ProvinceNo], 
        [Extent1].[ProvinceName] AS [ProvinceName], 
        [Extent2].[CityID] AS [CityID], 
        [Extent2].[ProvinceID] AS [ProvinceID1], 
        [Extent2].[CityNo] AS [CityNo], 
        [Extent2].[CityName] AS [CityName], 
        CASE WHEN ([Extent2].[CityID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM  [dbo].[Province] AS [Extent1]
        LEFT OUTER JOIN [dbo].[City] AS [Extent2] ON [Extent1].[ProvinceID] = [Extent2].[ProvinceID]
    )  AS [Project1]
    ORDER BY [Project1].[ProvinceID] ASC, [Project1].[C1] ASC

      Include语句可以在一次查询中使用多次。

    ctx.Categories
        .Include(c => c.Products)
        .Include(c => c.News);

      贪婪加载的不足:

      贪婪加载的优势在于仅执行1次SQL查询即返回所需要的结果。但使用JOIN查询在数据库记录条数较多时,多条简单的SQL查询往往比一条复杂的JOIN查询效率要好。

      使用Include的LINQ查询

    var provinces = ctx.Provinces
        .Include(p => p.Cities)
        .Where(p => p.ProvinceID > 10);
    var provinces = from p in ctx.Provinces.Include(p => p.Cities)
                    where p.ProvinceID > 10
                    select p;
    var expr = from p in ctx.Provinces
               where p.ProvinceID > 10
               select p;
    var provinces = expr.Include(p => p.Cities);

      3、显示加载(Explicit Loading)

      显示加载与延迟加载一样,采用主数据与关联数据独立分开加载。显示加载与延迟加载的区别在于显示加载不会自动的加载关联数据,需要调用方法去加载。

      显示加载是使用DbContext.Entry方法来实现的,Entry方法可以获取DbContext中的实体信息。在使用Entry获取实体信息之后,可以使用Collection或Reference方法获取和操作实体关联的集合属性。如使用Load方法查询集合属性。

      示例1:显示加载,使用Collection获取集合属性

    using (var ctx = new PortalContext())
    {
        var province = ctx.Provinces.Find(3);
        ctx.Entry(province)
            .Collection(p => p.Cities)
            .Query()
            .Load();
    
        foreach (var city in province.Cities)
        {
            Console.WriteLine("{0}-{1}", province.ProvinceName, city.CityName);
        }
    }

      上面的代码运行之后,执行的SQL语句:

    exec sp_executesql N'SELECT 
    [Limit1].[ProvinceID] AS [ProvinceID], 
    [Limit1].[ProvinceNo] AS [ProvinceNo], 
    [Limit1].[ProvinceName] AS [ProvinceName]
    FROM ( SELECT TOP (2) 
        [Extent1].[ProvinceID] AS [ProvinceID], 
        [Extent1].[ProvinceNo] AS [ProvinceNo], 
        [Extent1].[ProvinceName] AS [ProvinceName]
        FROM [dbo].[Province] AS [Extent1]
        WHERE [Extent1].[ProvinceID] = @p0
    )  AS [Limit1]',N'@p0 int',@p0=3
    exec sp_executesql N'SELECT 
    [Extent1].[CityID] AS [CityID], 
    [Extent1].[ProvinceID] AS [ProvinceID], 
    [Extent1].[CityNo] AS [CityNo], 
    [Extent1].[CityName] AS [CityName]
    FROM [dbo].[City] AS [Extent1]
    WHERE [Extent1].[ProvinceID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3

      从代码运行所执行的SQL语句可以看出,其查询数据库的方式与延迟加载是相同的。

      示例2:显示加载,使用Reference方法获取引用属性

    using (var ctx = new PortalContext())
    {
        var city = ctx.Cities.Find(10);
        ctx.Entry(city).Reference(c => c.Province);
        Console.WriteLine("{0}-{1}", city.Province.ProvinceName, city.CityName);
    }

      上面的代码运行之后执行的SQL语句:

    exec sp_executesql N'SELECT 
    [Limit1].[CityID] AS [CityID], 
    [Limit1].[ProvinceID] AS [ProvinceID], 
    [Limit1].[CityNo] AS [CityNo], 
    [Limit1].[CityName] AS [CityName]
    FROM ( SELECT TOP (2) 
        [Extent1].[CityID] AS [CityID], 
        [Extent1].[ProvinceID] AS [ProvinceID], 
        [Extent1].[CityNo] AS [CityNo], 
        [Extent1].[CityName] AS [CityName]
        FROM [dbo].[City] AS [Extent1]
        WHERE [Extent1].[CityID] = @p0
    )  AS [Limit1]',N'@p0 int',@p0=10
    exec sp_executesql N'SELECT 
    [Extent1].[ProvinceID] AS [ProvinceID], 
    [Extent1].[ProvinceNo] AS [ProvinceNo], 
    [Extent1].[ProvinceName] AS [ProvinceName]
    FROM [dbo].[Province] AS [Extent1]
    WHERE [Extent1].[ProvinceID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3

      检查集合属性是否已经加载:

    using (var ctx = new PortalContext())
    {
        var province = ctx.Provinces.Find(3);
        Console.WriteLine("Before load:{0}", ctx.Entry(province).Collection(p => p.Cities).IsLoaded);
    
        ctx.Entry(province)
            .Collection(p => p.Cities)
            .Load();
    
        Console.WriteLine("After load:{0}", ctx.Entry(province).Collection(p => p.Cities).IsLoaded);
    }

      4、集合属性查询

      在使用Entry和Collection方法获取到实体集合属性之后,可以使用Query方法对集合属性进行查询。

      示例:从内存中查询集合属性

    using (var ctx = new PortalContext())
    {
        var province = ctx.Provinces.Find(5);
        var cities = from c in province.Cities
                     where c.CityID > 30
                     select c;
        foreach (var city in cities)
        {
            Console.WriteLine("{0}-{1}", city.CityID, city.CityName);
        }
    }

      代码运行之后执行的SQL语句:

    exec sp_executesql N'SELECT 
    [Limit1].[ProvinceID] AS [ProvinceID], 
    [Limit1].[ProvinceNo] AS [ProvinceNo], 
    [Limit1].[ProvinceName] AS [ProvinceName]
    FROM ( SELECT TOP (2) 
        [Extent1].[ProvinceID] AS [ProvinceID], 
        [Extent1].[ProvinceNo] AS [ProvinceNo], 
        [Extent1].[ProvinceName] AS [ProvinceName]
        FROM [dbo].[Province] AS [Extent1]
        WHERE [Extent1].[ProvinceID] = @p0
    )  AS [Limit1]',N'@p0 int',@p0=5
    exec sp_executesql N'SELECT 
    [Extent1].[CityID] AS [CityID], 
    [Extent1].[ProvinceID] AS [ProvinceID], 
    [Extent1].[CityNo] AS [CityNo], 
    [Extent1].[CityName] AS [CityName]
    FROM [dbo].[City] AS [Extent1]
    WHERE [Extent1].[ProvinceID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=5

      从对City表执行的SQL语句可以看出,其并对加入查询条件,仅只是对之前通过延长加载方式将Province实体的Cities集合属性载人到内存中,然后通过对内存中的Cities数据进行内存查询,并未生成新的包含查询条件的SQL语句。

      示例:在数据库中查询集合属性

    using (var ctx = new PortalContext())
    {
        var province = ctx.Provinces.Find(5);
        var expr = ctx.Entry(province)
            .Collection(p => p.Cities)
            .Query();
        var cities = from c in expr
                     where c.CityID > 30
                     select c;
        foreach (var city in cities)
        {
            Console.WriteLine("{0}-{1}", city.CityID, city.CityName);
        }
    }

      代码运行之后执行的SQL语句:

    exec sp_executesql N'SELECT 
    [Limit1].[ProvinceID] AS [ProvinceID], 
    [Limit1].[ProvinceNo] AS [ProvinceNo], 
    [Limit1].[ProvinceName] AS [ProvinceName]
    FROM ( SELECT TOP (2) 
        [Extent1].[ProvinceID] AS [ProvinceID], 
        [Extent1].[ProvinceNo] AS [ProvinceNo], 
        [Extent1].[ProvinceName] AS [ProvinceName]
        FROM [dbo].[Province] AS [Extent1]
        WHERE [Extent1].[ProvinceID] = @p0
    )  AS [Limit1]',N'@p0 int',@p0=5
    exec sp_executesql N'SELECT 
    [Extent1].[CityID] AS [CityID], 
    [Extent1].[ProvinceID] AS [ProvinceID], 
    [Extent1].[CityNo] AS [CityNo], 
    [Extent1].[CityName] AS [CityName]
    FROM [dbo].[City] AS [Extent1]
    WHERE ([Extent1].[ProvinceID] = @EntityKeyValue1) AND ([Extent1].[CityID] > 30)',N'@EntityKeyValue1 int',@EntityKeyValue1=5

      集合属性Count查询

    using (var ctx = new PortalContext())
    {
        var province = ctx.Provinces.Find(5);
        var expr = ctx.Entry(province)
            .Collection(p => p.Cities)
            .Query();
        Console.WriteLine(expr.Count());
    }

      代码运行生成的SQL语句:

    exec sp_executesql N'SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[City] AS [Extent1]
        WHERE [Extent1].[ProvinceID] = @EntityKeyValue1
    )  AS [GroupBy1]',N'@EntityKeyValue1 int',@EntityKeyValue1=5

      显示加载集合属性的子集:

    using (var ctx = new PortalContext())
    {
        var province = ctx.Provinces.Find(5);
        ctx.Entry(province)
            .Collection(p => p.Cities)
            .Query()
            .Where(c => c.CityNo.Contains("3"))
            .Load();
    }

      代码运行后生成的SQL语句:

    exec sp_executesql N'SELECT 
    [Extent1].[CityID] AS [CityID], 
    [Extent1].[ProvinceID] AS [ProvinceID], 
    [Extent1].[CityNo] AS [CityNo], 
    [Extent1].[CityName] AS [CityName]
    FROM [dbo].[City] AS [Extent1]
    WHERE ([Extent1].[ProvinceID] = @EntityKeyValue1) AND ([Extent1].[CityNo] LIKE N''%3%'')',N'@EntityKeyValue1 int',@EntityKeyValue1=5
  • 相关阅读:
    NET与J2EE比拼
    NET与J2EE比拼
    Linux下添加FTP账号和服务器、增加密码和用户,更改FTP目录
    PS去除图片中文字的方法详细图文教程
    VC下绘图程序Demo
    U盘(auto病毒)类病毒分析与解决方案
    数据库技术源代码
    多边形填充实验
    错误总结1,动态加载部分不能显示的原因
    android实现开机欢迎界面
  • 原文地址:https://www.cnblogs.com/libingql/p/3381571.html
Copyright © 2020-2023  润新知