什么是延迟加载?
延迟加载顾名思义就是:推迟加载的时机,当真正使用的时候才加载。
通常在创建一个大对象时,有些属性我们可以在使用到的时候才去创建(设置属性的值),这个可以有效的提升系统性能。
示例:
//定义了一个Hero类型 public class Hero { public string Name{get;set;} public string FullName{get;set;} public Skill objSkill; public Hero(string name) { Name=name; FullName="Super "+name; objSkill=new Skill(name); } } //定义一个Skill类型 public class Skill { public string Name{get;set;} public int Power{get;set;} public Skill(string name) { Name=name; Power=name.Length; } } public class Program { public static void Main(string[] args) { Hero hero=new Hero("qi da sheng"); //此时我只想获取Hero的FullName,但是同时调用Skill的构造方法,加载了Skill的属性, //初始化Skill需要在内存中开辟一定的空间,造成没必要的空间浪费 Console.WriteLine(hero.FullName); //思考:如果实现在调用Skill.Name的时候才去真正创建Skill对象呢? } }
改进一:
//定义了一个Hero类型 public class Hero { public string Name{get;set;} public string FullName{get;set;} //public Skill objSkill; private Skill _skill; public Skill objSkill { get {return _skill??(new _skill(Name));} } public Hero(string name) { Name=name; FullName="Super "+name; //objSkill=new Skill(name); } } //定义一个Skill类型 public class Skill { public string Name{get;set;} public int Power{get;set;} public Skill(string name) { Name=name; Power=name.Length; } } public class Program { public static void Main(string[] args) { Hero hero=new Hero("qi da sheng"); //此时获取Hero的FullName时并不会去创建Skill的实例 Console.WriteLine(hero.FullName); //真正用到Skill.Name时才会创建Skill的实例,从而实现了延迟加载效果? Console.WriteLine(hero.ObjSkill.Power); //思考2:有没有其他更好的方法? } }
改进二:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Demo_Lazy { //定义了一个Hero类型 public class Hero { public string Name { get; set; } public string FullName { get; set; } private readonly Lazy<Skill> skill; public Skill objSkill { get { return skill.Value; } } public Hero(string name) { Name = name; FullName = "Super " + name; skill = new Lazy<Skill>(() => new Skill(Name)); } } //定义一个Skill类型 public class Skill { public string Name { get; set; } public int Power { get; set; } public Skill(string name) { Name = name; Power = name.Length; } } public class Program { public static void Main(string[] args) { Hero hero = new Hero("qi da sheng"); //此时获取Hero的FullName时并不会去创建Skill的实例 Console.WriteLine(hero.FullName); //真正用到Skill.Name时才会创建Skill的实例,从而实现了延迟加载效果 Console.WriteLine(hero.objSkill.Power); Console.Read(); } } }
Lazy<T>的优势
那么既然我们已经可以用属性缓存的方法实现, 为什么还要引入Lazy<T> ?
至少Lazy<T> 有以下几点优势:
- 它具有 LazyThreadSafetyMode, 但是我们一般不使用它, 除非是很关键的情况下(在此略去181个字)
- 它使属性的定义行更加简单
- 从语义上来讲, 它更加明确, 更加具有可读性
- 它允许null为有效值
EF使用延迟加载必须满足以下两个条件:
1、类是由Public修饰,不能是封闭类,也就是说,不能带有Sealded修饰符
2、导航属性标记为Virtual。
public class Score { [Key] public int Id { get; set; } public int StudentScore { get; set; }//学生分数 public int StudentID { get; set; }//学生ID public int CourseID { get; set; }//课程ID public virtual Student Student { get; set; }//virtual关键字修饰,用于延迟加载 提高性能 只有显式调用时 才会加载 并可以代表一个Student对象 也就是 属性==对象 public virtual Course Course { get; set; }//virtual关键字修饰,用于延迟加载 提高性能 只有显式调用时 才会加载 并可以代表一个Course对象 也就是 属性==对象 }
如果您不想使用延迟加载,您可以关闭Lazy Loading,将LazyLoadingEnabled设为false,如果导航属性没有标记为virtual,Lazy Loading也是不起作用的。
public StudentContext(): base("StudentContext")//指定连接字符串 { this.Configuration.LazyLoadingEnabled = false; //关闭延迟加载 }