65.9K
CodeProject 正在变化。 阅读更多。
Home

Entity Framework 模式:选择多个实体

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (25投票s)

2010年12月15日

Ms-PL

4分钟阅读

viewsIcon

178171

downloadIcon

1072

介绍 Entity Framework 中处理选择实体列表的各种模式。

引言

在本文中,我想介绍 Entity Framework 中处理一个常见问题的各种模式。

问题如下:选择一个实体列表;更具体地说,给定一个 ID 列表,我们希望选择相应的实体。

本文提供不同的方法,权衡各自的优缺点。不同的模式包括:

  • 一次选择一个实体
  • 通过 EF 进行 Union 操作
  • 通过 EF 进行 Where-In 操作
  • 连接到另一个表

使用代码

代码示例包含一个 Visual Studio 2010 解决方案。该解决方案包含一个项目,其中为本文介绍的每种模式都有一个类。

该解决方案还包含一个名为“DB Scripts”的解决方案文件夹,其中包含三个文件:SchemaCreation.sql(在预先存在的数据库中创建包含三个表的架构)、DataInsert.sql(在表中插入样本数据)和 SchemaDrop.sql(根据需要删除表和架构)。

我们为整个项目使用相同的数据库架构。这是它的实体模型表示

EntityModel.JPG

一次选择一个实体

此模式最简单。基本上,我们为每个 ID 执行一次数据库查询。

public static Order[] DoSelect(int[] ids)
{
	using (var context = new EFDemoEntities())
	{
		var list = new List<Order>(3);

		foreach (var id in ids)
		{
			var query = from o in context.Order.Include("OrderDetail")
				where o.OrderID == id
				select o;
			var objectQuery = (System.Data.Objects.ObjectQuery)query;
			var sql = objectQuery.ToTraceString();

			list.AddRange(query);
		}

		return list.ToArray();
	}
}

优点

  • 非常简单
  • 每次查询(每个 ID 一个)都会发送相同的参数化 SQL 查询,因此查询会在服务器端进行缓存。

缺点

  • 可伸缩性:每个 ID 有一个查询,因此 ID 越多,查询越多。

如果您需要选择的 ID 数量很少(例如少于 10 个),此模式是可以接受的。超过此数量,您将开始注意到严重的性能问题,因为与数据库服务器的延迟会拖累您。

通过 EF 进行 Union 操作

在这里,我们的目标是将所有内容打包到一个查询中,以实现可伸缩性。

该方法是为每个 ID 执行一次查询,然后将所有查询进行 Union 操作并发送到服务器。

public static Order[] DoSelect(int[] ids)
{
	using (var context = new EFDemoEntities())
	{
		IQueryable<Order> query = null;

		foreach (var id in ids)
		{
		    // Must assign ID to a scope-local variable,
		    // otherwise, only the last ID is used
			int concreteID = id;
			var queryForID = 
			    from o in context.Order.Include("OrderDetail")
				where o.OrderID == concreteID
				select o;

			if (query == null)
			{
				query = queryForID;
			}
			else
			{
				query = query.Union(queryForID);
			}
		}
		var objectQuery = (System.Data.Objects.ObjectQuery)query;
		var sql = objectQuery.ToTraceString();

		return query.ToArray();
	}
}

优点

  • 可伸缩性:只向服务器发送一个查询,但有多少 ID?

缺点

  • 参数化 SQL 查询取决于 ID 的数量,因此如果系统中 ID 的数量变化很大,SQL Server 将难以缓存查询计划。
  • ID 越多,SQL 查询就会越长,这会影响可伸缩性。

此模式仅在 ID 数量始终相同的情况下才有用。

通过 EF 进行 Where-In 操作

在这里,我们的目标是将所有内容打包到一个查询中,以实现可伸缩性。

该方法是通过过滤输入 ID 列表中的 ID 来实现的。这将生成一个 SQL 语句 `WHERE ID IN (X, Y, Z)`。

public static Order[] DoSelect(int[] ids)
{
	using (var context = new EFDemoEntities())
	{
		var query = from o in context.Order.Include("OrderDetail")
			where ids.Contains(o.OrderID)
			select o;
		var objectQuery = (System.Data.Objects.ObjectQuery)query;
		var sql = objectQuery.ToTraceString();

		return query.ToArray();
	}
}

优点

  • 可伸缩性:只向服务器发送一个查询,但有多少 ID?

缺点

  • 参数化 SQL 查询取决于 ID 本身;由于它们最终会出现在临时查询中,因此 SQL Server 将极难缓存查询计划。

这里唯一的问题是 ID 列表本身出现在临时查询中:它没有通过参数传递。因此,您进行的每次具有不同 ID 的查询都会不同,并且查询计划永远不会被缓存。否则,生成的查询很简单,并且只发送一个查询。如果您不关心查询计划缓存,这可能是一个不错的选择。

连接到另一个表

在这里,我们使用第三个表来输入 ID 列表,然后与之连接。基本上,我们将 ID 列表从应用程序服务器移到数据库服务器。

Entity Framework 应该以批量模式插入,因此我们应该只进行一次往返来完成所有插入(实际上,批量大小是有限的,因此在某些时候会发生多次往返)。在执行 Select-Join 后,对于删除也是如此。因此,应该有三次往返,但与 ID 的数量无关。

public static Order[] DoSelect(int[] ids)
{
	using (var context = new EFDemoEntities())
	{
		var batchID = Guid.NewGuid();
		var tempIDs = new List<IDList>(ids.Length);

		foreach (var id in ids)
		{
			var idItem = new IDList { BatchID = batchID, OrderID = id };

			tempIDs.Add(idItem);
			context.IDList.AddObject(idItem);
		}
		context.SaveChanges();

		var query = ((from o in context.Order
				join i in context.IDList on o.OrderID equals i.OrderID
				where i.BatchID == batchID
				select o) as ObjectQuery<Order>).Include("OrderDetail");
		var sql = query.ToTraceString();
		var orders = query.ToArray();

		foreach (var temp in tempIDs)
		{
			context.IDList.DeleteObject(temp);
		}
		context.SaveChanges();

		return orders;
	}
}

优点

  • 可伸缩性:使用批量插入和批量删除,向服务器发送三个查询,但有多少 ID?
  • 向服务器发送相同的查询,因此查询计划会被缓存。

缺点

  • 您需要一个工作表来存储 ID。
  • 如果插入和删除未在同一事务中完成,您需要清理该表。
  • 为了进行选择而进行写入和删除操作效率低下。

此模式提供了出色的可伸缩性属性,但复杂性相当高。

结论

我们介绍了四种解决选择多个实体问题的模式。总结如下:

模式 注释

一次选择一个实体

最简单,可伸缩性差
通过 EF 进行 Union 操作 可伸缩性差
通过 EF 进行 Where-In 操作 可伸缩性好但禁止查询计划缓存
连接到另一个表 可伸缩性和查询计划缓存都很好,但相当复杂

没有绝对的赢家,这就是为什么我们介绍了每种模式。

处理这种情况的最佳方法是将表作为参数传递给存储过程。然而,EF 不支持带有表参数的存储过程,因此这需要在 EF 之外完成。

© . All rights reserved.