简体   繁体   English

实体框架中的联接表C# 高效方法

[英]Joined tables in entity framework C# efficient method

I'm developing a software using SQL-Sever which it previous version was on a Access database.我正在使用 SQL-Sever 开发一个软件,它以前的版本在 Access 数据库上。 In Access I could make different queries with joined tables but now that I'm using C# EF method for developing I'm confused and I dont know which is the best method to use joined tables for my application.在 Access 中,我可以对连接表进行不同的查询,但现在我使用 C# EF 方法进行开发,我很困惑,我不知道哪种方法是为我的应用程序使用连接表的最佳方法。

Please tell me which method I must use for better performance that is also the right way to do it.请告诉我必须使用哪种方法才能获得更好的性能,这也是正确的方法。

methods I have in my mind:我想到的方法:

  • Using SQL Views and importing in EF使用 SQL 视图并在 EF 中导入
  • Using SQL stored procedures使用 SQL 存储过程
  • Making join in LINQ or C# coding在 LINQ 或 C# 编码中加入

Also I have mapped my relations in EF Model but the questions is how to use the joined result as IQueryable when I dont have its entity in the model.此外,我已经在 EF Model 中映射了我的关系,但问题是当我在 model 中没有实体时,如何将连接结果用作 IQueryable。 How to use the joined LINQ result?如何使用加入的 LINQ 结果?

It was using MS Access.它使用的是 MS Access。 Whichever method you use now should have considerably greater performance, so I'd go for the simpler which would be the 3rd one.您现在使用的任何一种方法都应该具有更高的性能,所以我会选择 go 来获得更简单的方法,这将是第三种方法。 This is an open-ended question though and it really depends on your requirements.不过,这是一个开放式问题,实际上取决于您的要求。

if by "joined tables" you are referring to a relational model with foreign keys relating records, then Entity Framework is designed specifically to work with this.如果通过“连接表”您指的是具有相关记录的外键的关系 model,那么实体框架专门用于处理此问题。 For the best performance you want to ensure that EF has the relationships between entities mapped, and then leverage projection using Select when reading data to ensure you are only reading as much data as you need at any given time.为了获得最佳性能,您希望确保 EF 具有映射的实体之间的关系,然后在读取数据时使用Select来利用投影,以确保您在任何给定时间只读取所需的数据。

I don't recommend using Sprocs with EF.我不建议将 Sprocs 与 EF 一起使用。 It does support them, but frankly I find mapping relationships is far more flexible.它确实支持它们,但坦率地说,我发现映射关系要灵活得多。 Views can be good for read-only representations of complex data models.视图可以很好地用于复杂数据模型的只读表示。 It doesn't work for cases where you want to insert/update data.它不适用于您要插入/更新数据的情况。 Explicit joins in EF is also an option, but should be used sparingly. EF 中的显式连接也是一种选择,但应谨慎使用。 Mapping the relationships and referencing those relationships in expressions is simple to read and use.映射关系并在表达式中引用这些关系很容易阅读和使用。 When I see developers writing joins in LINQ, they may as well just be using ADO, it's missing out on the flexibility and power offered by EF.当我看到开发人员在 LINQ 中编写连接时,他们可能只是在使用 ADO,它错过了 EF 提供的灵活性和功能。

Common performance pitfalls I see from developers new to EF:我从新接触 EF 的开发人员那里看到的常见性能缺陷:

  • Querying too much data查询数据过多
  • Querying data too often查询数据过于频繁
  • Getting tripped up by lazy loading被延迟加载绊倒
  • Fixing EF errors with ToList使用ToList修复 EF 错误
  • Big queries that should be queued应该排队的大查询
  • Excessive use of async过度使用异步

Querying too much data: This covers stuff like eager loading entire entity graphs (related data) and loading entities when all the code wants to do is get a count, check if a row exists, etc. Learn about IQueryable and how you can leverage it to perform counts, exists checks ( Any ) and other operations rather than fetching entities to do those checks.查询太多数据:这包括像急切加载整个实体图(相关数据)和加载实体,当所有代码想要做的是获取计数、检查行是否存在等。了解IQueryable以及如何利用它执行计数、存在检查 ( Any ) 和其他操作,而不是获取实体来进行这些检查。 Passing entities to views for instance as a data container is a common issue when the view such as a search result page only displays a handful of fields.例如,当搜索结果页面等视图仅显示少数字段时,将实体作为数据容器传递给视图是一个常见问题。 leveraging Select to populate a simple view model can speed up queries and reduce resource use.利用Select填充简单视图 model 可以加快查询速度并减少资源使用。 Additionally not factoring for potentially big result sets can kill performance.此外,不考虑潜在的大结果集可能会影响性能。 Searches should always have limits to the total # of rows returned and leverage paging.搜索应始终限制返回的总行数并利用分页。 Loading full entity graphs should only really ever be needed when performing an update.只有在执行更新时才真正需要加载完整的实体图。

Querying too often: This covers relying heavily on lazy loading to load related data, and inefficient coding practices such as loading single entities in loops.查询过于频繁:这包括严重依赖延迟加载来加载相关数据,以及低效的编码实践,例如循环加载单个实体。 Often this leads developers to do silly things like implementing caching, which lead to memory issues and stale data bugs.这通常会导致开发人员做一些愚蠢的事情,比如实现缓存,这会导致 memory 问题和陈旧数据错误。 Caching should only be used for static data is not expected to change.缓存应仅用于 static 数据预计不会更改。 Instead, where you need multiple entities, look to load sets of data in a single query.相反,如果您需要多个实体,请在单个查询中加载数据集。 For instance when you are creating an order where there may be 10 order lines referring to a Product, rather than iterating over each order line and loading the single product to associate with that line, execute 1 query to load the 10 (or fewer) distinct identified products by ID.例如,当您创建一个订单时,可能有 10 个订单行引用一个产品,而不是遍历每个订单行并加载单个产品以与该行关联,执行 1 个查询以加载 10 个(或更少)不同的通过 ID 识别产品。

Getting tripped by Lazy Loading: Probably the biggest unexplained performance hit I see people get caught on is lazy loading calls, either in code they don't expect it, or due to serializing entities.被延迟加载绊倒:我看到人们陷入的最大的无法解释的性能影响可能是延迟加载调用,无论是在他们不期望的代码中,还是由于序列化实体。 Serializers will touch every property on an entity so each and ever lazy load property will trigger a query if it wasn't eagerly loaded.序列化程序将触及实体上的每个属性,因此每个延迟加载属性都会在未急切加载时触发查询。 When loading entities for search results, this can result in many individual queries getting triggered for every single search result returned.在为搜索结果加载实体时,这可能会导致针对返回的每个搜索结果触发许多单独的查询。 Basically, never return entities, and that includes never putting references to entities inside view models.基本上,永远不要返回实体,这包括永远不要在视图模型中放置对实体的引用。

Fixing EF errors with ToList : This can happen when devs have a query where they want to use something like a function in their code or an unmapped property that does formatting or a calculation.使用ToList修复 EF 错误:当开发人员有一个查询,他们想在他们的代码中使用 function 之类的东西或进行格式化或计算的未映射属性时,就会发生这种情况。 EF6 will throw an error that the expression cannot be converted to SQL so the quick and dirty solution is to call ToList before the Where / Select call using that function. EF6 将抛出一个错误,即表达式无法转换为 SQL,因此快速而肮脏的解决方案是在Where / Select调用之前使用该 ZC1C425268E68385D1AB4ZZC17A94F1 调用ToList The consequence of this is that it can result in a query executing that loads all, or a considerable amount more data than expected before applying the condition.这样做的结果是,它可能导致执行的查询加载所有数据,或者在应用条件之前加载比预期更多的数据。 Where EF6 will throw an error, EFCore will materialize this partial query automatically.在 EF6 将引发错误的情况下,EFCore 将自动实现此部分查询。 While it can still apply partial filtering, there is no guarantee that your queries could effectively be running a SELECT * FROM type query against the database.虽然它仍然可以应用部分过滤,但不能保证您的查询可以有效地对数据库运行SELECT * FROM类型查询。 Watch the SQL that EF is generating as you're going through test runs and pay attention to the performance metrics.在您进行测试运行时观看 EF 生成的 SQL,并注意性能指标。

Big queries that should be queued.应该排队的大查询。 Very heavy queries such as reports and very open-ended searches (Ie text searches, etc.) should consider using a background process and a queue to prevent too many requests getting kicked off simultaneously.非常繁重的查询,例如报告和非常开放的搜索(即文本搜索等)应该考虑使用后台进程和队列来防止同时启动太多请求。 Queries that need to touch a lot of records should be executed against a synchronized read-only replica, or consider dirty reads if there is no replica, provided these are practical for the data being reported on.需要接触大量记录的查询应该针对同步的只读副本执行,或者如果没有副本则考虑脏读,前提是这些对于正在报告的数据是实用的。 Queries that touch a lot of rows result in, and wait for a lot of locks, plus result in a lot of I/O requests from the database server.涉及大量行的查询会导致并等待大量锁,此外还会导致来自数据库服务器的大量 I/O 请求。 Queues help ensure that a lot of these don't get kicked off at once.队列有助于确保其中很多不会立即启动。 The more of these that run, the more they interfere with each other (and other queries) compounding the performance problems.这些运行的越多,它们相互干扰(和其他查询)就越多,从而加剧了性能问题。

Excessive use of async .过度使用async EF supports async operations to help make things like web servers more responsive while potentially heavy queries are running. EF 支持async操作,以帮助使 web 服务器之类的东西在运行潜在的繁重查询时更具响应性。 Unfortunately many devs think that this makes queries "faster", or that they should be consistent and use async operations everywhere.不幸的是,许多开发人员认为这会使查询“更快”,或者它们应该保持一致并在任何地方使用异步操作。 It doesn't, and they shouldn't.它没有,他们也不应该。 Asynchronous queries are nominally slower that synchronous ones, and should be used for queries that need to take a bit longer, freeing up the web server to serve other requests while it waits.异步查询名义上比同步查询慢,并且应该用于需要更长时间的查询,释放 web 服务器在等待时为其他请求提供服务。 async is not a performance boost, and should not be the default suggestion when investigating performance issues. async不是性能提升,在调查性能问题时不应该是默认建议。 Eliminate all of the above scenarios before resigning to the query just needing to take longer.在退出查询之前消除所有上述情况,只需要花费更长的时间。

To really keep a tab on EF and performance issues, it helps a lot to get familiar with SQL profiling.要真正关注 EF 和性能问题,熟悉 SQL 分析会大有帮助。 This captures all queries that EF executes and you can look for any weird extra queries, or "heavy" queries.这将捕获 EF 执行的所有查询,您可以查找任何奇怪的额外查询或“繁重”查询。 (lots of row hits or slow execution times) Queries with high row "touches" should be inspected even if tests seem fast enough. (大量的行命中或缓慢的执行时间)即使测试看起来足够快,也应该检查具有高行“接触”的查询。 These can be problematic in production if a few of them get kicked off at once, or run under load with other operations competing for locks.如果它们中的一些被立即启动,或者在负载下运行,而其他操作竞争锁,那么这些在生产中可能会出现问题。

Edit: For Order & OrderType example:编辑:对于订单和订单类型示例:

For example as you said we have a table for Order and one for OrderType which each has its own DBSet.例如,正如您所说,我们有一个 Order 表和一个 OrderType 表,每个表都有自己的 DBSet。 Now we want to join them and get a new model out of it in EF to reference in our LINQ expressions (OrderJoinTable), How is this possible please show how you create this model in EF?现在我们想加入他们并在 EF 中从中获得一个新的 model 以在我们的 LINQ 表达式(OrderJoinTable)中引用,这怎么可能请说明如何在 9EFD8FC3 中创建这个 Z20F35E630DAF44DBFA4C3F68FZ3

We have an Order entity pointing to an Order table, and an OrderType entity pointing to an OrderType table.我们有一个指向 Order 表的 Order 实体和一个指向 OrderType 表的 OrderType 实体。 In your example you have DbSets for both Order and OrderType.在您的示例中,您有 Order 和 OrderType 的 DbSet。 I would assume that the Order table contains a FK to the OrderType, OrderTypeId我假设 Order 表包含 OrderType、OrderTypeId 的 FK

Initial Entities:初始实体:

public class Order
{
   [Key]
   public int OrderId { get; set; }
   public int OrderTypeId { get; set; }
   public string OrderNumber { get; set; }
   /* Other order fields... */
}

public class OrderType
{
   [Key]
   public int OrderTypeId { get; set; }
   public string Name { get; set; }
}

In the DbContext you have two DbSets:在 DbContext 中有两个 DbSet:

public DbSet<Order> Orders { get; set; }
public DbSet<OrderType> OrderTypes { get; set; }

From your question you want to populate something like a list of orders displaying the names of the order types rather than just their keys, so "joining" the two.根据您的问题,您想要填充类似订单列表的内容,其中显示订单类型的名称而不仅仅是它们的键,因此“加入”两者。 Yes, this is certainly possible with EF, but it somewhat defeats the whole benefit of using an ORM over plain old ADO.是的,这在 EF 中当然是可能的,但它在某种程度上抵消了使用 ORM 而不是普通的旧 ADO 的全部好处。 With EF, we set up the relationship between entities (navigation properties) so that we can resolve this information automatically.使用 EF,我们设置了实体之间的关系(导航属性),以便我们可以自动解析此信息。 Looking back at the entity declarations:回顾实体声明:

public class Order
{
   [Key]
   public int OrderId { get; set; }
   public int OrderTypeId { get; set; }
   public string OrderNumber { get; set; }
   /* Other order fields... */

   [ForeignKey("OrderTypeId")]
   public virtual OrderType OrderType { get; set; }
}

By setting up this navigation property, EF has all it needs to be able to build a query that can return the order type details when you query an order.通过设置此导航属性,EF 拥有构建查询所需的一切,该查询可以在您查询订单时返回订单类型详细信息。 You'll still likely have an OrderType DbSet since we'll want to look up Order Types to assign to lookup lists and associate against an order;您仍然可能有一个 OrderType DbSet,因为我们要查找订单类型以分配给查找列表并与订单相关联; However, for something like OrderLines which only exist as part of an order, these would not need DbSets, they would just be accessed through their respective orders via the navigation properties.但是,对于像 OrderLines 这样仅作为订单的一部分存在的东西,它们不需要 DbSet,它们只需通过导航属性通过它们各自的订单进行访问。

what the Query looks like:查询的样子:

To get the order with it's order type:要使用其订单类型获取订单:

var order = context.Orders.Include(x => x.OrderType).Where(x => x.OrderId == orderId).Single();

This is an eager load scenario using Include .这是使用Include的急切加载场景。 It will retrieve the single order, and also populate it's OrderType.它将检索单个订单,并填充它的 OrderType。 From there you can get the order type name using order.OrderType.Name.从那里您可以使用 order.OrderType.Name 获取订单类型名称。 Eager loading is optional.急切加载是可选的。 As long as the OrderType navigation is declared virtual and lazy loading isn't disabled on the DbContext, EF can load lazy-load properties on demand.只要 OrderType 导航声明为虚拟且未在 DbContext 上禁用延迟加载,EF 就可以按需加载延迟加载属性。 OrderType would not be fetched with the order in the above expression, but accessing the .OrderType property on an order would signal EF to run another query to fetch and populate the order type for that order. OrderType 不会使用上述表达式中的订单获取,但访问订单上的.OrderType属性将向 EF 发出信号以运行另一个查询以获取并填充该订单的订单类型。 This only works as long as the DbContext that loaded the order has not yet been exposed.这仅在加载订单的 DbContext 尚未公开时才有效。 Lazy loading can result in performance issues if relied on heavily.如果严重依赖延迟加载,可能会导致性能问题。

For better performance you can utilize projection with .Select to populate view models / DTOs rather than relying on whole entities.为了获得更好的性能,您可以使用带有.Select的投影来填充视图模型/DTO,而不是依赖于整个实体。 If we want to perform a search listing orders with their order type name, we can declare an OrderSummaryViewModel as such:如果我们想使用订单类型名称执行搜索列出订单,我们可以声明一个 OrderSummaryViewModel,如下所示:

// Something like your OrderJoinTable?
[Serializable]
public class OrderSummaryViewModel
{
   public int OrderId { get; set; }
   public string OrderNumber { get; set; }
   public string OrderType { get; set; }
}

Then when pulling those records:然后在提取这些记录时:

var orders = context.Orders.Select( x => new OrderSummaryViewModel
{
    OrderId = x.OrderId,
    OrderNumber = x.OrderNumber,
    OrderType = x.OrderType.Name
}).ToList();

Note that this does not need to resort to eager loading, and populates view models so we don't need to worry about lazy loading either.请注意,这不需要使用预加载,并且会填充视图模型,因此我们也不需要担心延迟加载。 We can access navigation properties within our Linq expression to filter as needed and populate our display models without worrying about explicit joins.我们可以访问 Linq 表达式中的导航属性,以根据需要进行过滤并填充我们的显示模型,而无需担心显式连接。

Explicit joins would be reserved for cases where we need to link purely unrelated entities to one another, such as with cases where an entity has a relationship to one of several possible entities.显式连接将保留用于我们需要将纯粹不相关的实体彼此链接的情况,例如实体与几个可能实体之一有关系的情况。 For example if I have an Address table which had an EntityId key that could contain a CustomerId or a BusinessId from a Customer or Business table respectively.例如,如果我有一个 Address 表,它的 EntityId 键可以分别包含来自 Customer 或 Business 表的 CustomerId 或 BusinessId。 However, this kind of table structure is highly inefficient and usually only exists when people are convinced it "saves space" or some other optimization.然而,这种表结构效率非常低,通常只有在人们确信它“节省空间”或其他一些优化时才会存在。 It will suffer from performance issues with no FK associations, and be prone to "breakages" without proper constraints.它将遭受没有 FK 关联的性能问题,并且在没有适当约束的情况下容易“损坏”。 Still, when dealing with legacy systems with stuff like this you can still leverage explicit joins between entities.尽管如此,在处理带有此类东西的遗留系统时,您仍然可以利用实体之间的显式连接。

EF can map relationships for many-to-one references, (like above) 1-to-many child collections, (such as Order to OrderLines) 1-to-1 relations (Order to OrderDeliveryDetails) and many-to-many relations (such as Customer to Address where 1 customer can have many addresses, and 1 address may be linked to many customers, all using a CustomerAddress table (CustomerId+AddressId)) You can find examples on how each of these scenarios can be mapped out with EF. EF 可以 map 关系进行多对一引用,(如上) 1 对多子 collections,(如 Order 到 OrderLines) 1 对 1 关系(Order to OrderDeliveryDetails)和多对多关系(例如客户到地址,其中 1 个客户可以有多个地址,并且 1 个地址可能链接到多个客户,所有这些都使用 CustomerAddress 表 (CustomerId+AddressId))您可以找到有关如何使用 EF 映射每个场景的示例.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM