关联引用
对于关联引用对象,就是访问某个对象的关联对象。使用如下的代码即可加载关联的另一端:
//对像上下文
var context = new StudentAndClassContext();
//查询班级
var cla = context.Classes.FirstOrDefault();
if (cla != null)
foreach (var student in cla.Students)
{
Console.WriteLine($"{student.StudentId}号学生,名字是:{student.Name}");
}
在Obase中,实体之间的关联通常是通过导航属性来实现的。默认情况下,这些导航属性使用的是延迟加载(Lazy Loading)机制,这意味着当你首次访问一个实体的导航属性时,Obase会自动发起一个额外的数据库查询来获取相关数据。这种加载方式在访问关联对象时是透明的,你就像访问普通对象属性一样,不需要额外的代码。
为了使延迟加载生效,导航属性通常被标记为virtual。这是因为Obase使用动态代理来实现延迟加载,而virtual关键字允许Obase在运行时动态地覆盖这些属性,从而能够在它们被访问时触发额外的数据库查询。
然而,延迟加载虽然方便,但在某些情况下可能会导致性能问题。特别是当需要加载大量关联对象时,每个导航属性的访问都会触发一次数据库查询,这会导致大量的数据库往返和不必要的性能开销。此外,延迟加载还可能导致N+1查询问题,即当遍历一个集合时,为每个集合项加载关联对象会导致大量额外的查询。
为了解决这个问题,开发者有时会选择关闭延迟加载,或者将导航属性标记为非virtual,以避免不必要的数据库查询。但这样做意味着在需要关联对象时,你必须显式地加载它们。这就是Include方法的用武之地。通过使用Include方法,你可以在查询主实体时显式地包含其关联对象,从而避免延迟加载可能带来的性能问题。
/// <summary>
/// 学生与班级对象上下文
/// </summary>
public class StudentAndClassNoLazyLoadContext : ObjectContext<StudentAndClassNoLazyLoadConfger>
{
/// <summary>
/// 学生对象集合
/// </summary>
public ObjectSet<Student> Students { get; set; }
/// <summary>
/// 班级对象集合
/// </summary>
public ObjectSet<Class> Classes { get; set; }
}
/// <summary>
/// 学生与班级对象上下文配置提供者
/// </summary>
public class StudentAndClassNoLazyLoadConfger : SqlContexConfiger
{
/// <summary>
/// 获取特定于数据库服务器(SQL Server、Oracle等)的数据提供程序工厂
/// </summary>
protected override DbProviderFactory DbProviderFactory => MySqlClientFactory.Instance;
/// <summary>
/// 连接字符串
/// </summary>
protected override string ConnectionString =>
"server=127.0.0.1;database=obasedemo;uid=root;pwd=Sql*2008;pooling=false;CharSet=utf8;port=3306;SslMode=none;";
/// <summary>
/// 使用指定的建模器创建对象数据模型。
/// </summary>
/// <param name="modelBuilder"></param>
protected override void CreateModel(ModelBuilder modelBuilder)
{
var classConfiguration = modelBuilder.Entity<Class>();
modelBuilder.Entity<Student>();
var studentClassAss = modelBuilder.Association<Student, Class>();
studentClassAss.ToTable("Student");
classConfiguration.AssociationReference<Student, Class>("Students", true).HasEnableLazyLoading(false);
}
}
这里将关联引用配置为不延迟加载,所以需要使用Include方法进行加载。
//对像上下文
var context = new StudentAndClassNoLazyLoadContext();
//查询班级
var cla = context.Classes.FirstOrDefault();
var stu = cla?.Students;
if (stu == null || cla.Students.Count == 0)
{
Console.WriteLine("No Load Association!");
}
//强制包含
cla = context.Classes.Include(p => p.Students).FirstOrDefault();
stu = cla?.Students;
if (stu != null)
{
foreach (var student in cla.Students)
{
Console.WriteLine($"{student.StudentId}号学生,名字是:{student.Name}");
}
}
多级关联引用加载:
public class Article{
public string Code{get;set;}
public string Title{get;set;}
public List<ArticleOwnedTag> DishesTags{get;set;}\
}
public class Tag{
public string Code{get;set;}
public string Name{get;set;}
}
public class ArticleOwnedTag{
public string ArticleCode{get;set;}
public string TagCode{get;set;}
public Article Article{get;set;}
public Tag Tag{get;set;}
}
当我们想要查询Article实体并同时加载其关联的DishesTags集合时,为了保持数据的完整性,我们还需要确保DishesTags中的Article和Tag实体也被加载。在Obase中,我们可以通过链式调用Include方法来实现这一点。Include方法接受一个lambda表达 式(如c => c.DishesTags.Select(q=>q.Article))或(如c => c.DishesTags.Select(q=>q.Tag))者来指定需要加载的关联对象。这种方式不仅提高了查询效率,还减少了数据库往返次数,从而优化了整体的数据加载过程。