如何只获取查询中的部分包含属性,并在进行更改时仍然更新它?

huangapple go评论72阅读模式
英文:

How can I fetch just some include properties from my query and still update it if changes are made?

问题

你可以尝试以下的方式,只获取你需要的属性,同时仍然可以保存 StripePayout 的更改:

var payout = await _dbContext.StripePayouts
    .Where(p => p.Id == payoutId)
    .Select(p => new 
    {
        // StripePayout 的属性
        Id = p.Id,
        FailureResubmitStatus = p.FailureResubmitStatus,
        HasFetchedTransfers = p.HasFetchedTransfers,
        StripePayoutId = p.StripePayoutId,

        // 从 StripeAccount 只获取需要的属性
        StripeAccountId = p.StripeAccount.AccountId,

        // 从 User 只获取需要的属性
        UserEmail = p.User.Email,
    })
    .FirstOrDefaultAsync();

这将允许你只选择所需的属性,而不获取整个实体,同时仍然可以保存 StripePayout 的更改。

英文:

I have this query

var payout = await _dbContext.StripePayouts
    .Include(p => p.StripeAccount)
    .Include(p => p.User)
    .Where(p => p.Id == payoutId)
    .FirstAsync();

and I only need 2 properties from 'User' and just 1 property from StripeAccount, so I don't want to fetch the whole entity as they are both large. But I also need to save properties from StripePayouts, so I can't use a select on it.

QUESTION - Is there a way I can write this query so that I only fetch what I need instead of all properties from all of the included entities, but still be able to save changes to StripePayout?

Ex. I only need a single property from StripeAccount named "AccountId" and with User I only need a property called "Email".

Something like this?

.Include(p => p.User.Select(q => new {
    Name = q.UserName,
    Email = q.UserEmail
})

I know I can do this below, but I have to be able to save some of those properties from StripePayout (ex. HasFetchedTransfers) if they need to be changed. No values from the child entities (StripeAccount, User) have to be changed.

var payout = await _dbContext.StripePayouts
    .Select(p => new 
    {
        Id = p.Id,
        FailureResubmitStatus = p.FailureResubmitStatus,
        HasFetchedTransfers = p.HasFetchedTransfers,
        StripePayoutId = p.StripePayoutId,
        // no changes to these values below 
        StripeAccountId = p.StripeAccount.AccountId,
        UserEmail = p.User.Email,
        UserName = p.User.UserName,
    })
    .FirstOrDefaultAsync(p => p.Id == payoutId);

答案1

得分: 1

关于 EF 和应用程序方面的一般建议是避免将实体视为数据传输,实体在不同操作(读取和写入)之间传递。

在 web 应用程序中,例如,可以使用类似你在结尾处示例中定义和投影的 View Model,有选择地从聚合结构中提取所需信息。然后,如果/当你要持久化对该实体的更改时,只重新加载需要更新的实体或实体。你不需要重新加载整个聚合根来完成此操作。对于厚客户端应用程序也是同样的情况。我们不需要整个聚合结构来向用户显示信息,而且在信息首次读取和用户选择提交更改之间仍然存在延迟时间。

对于这一点的额外考虑是检测和处理并发更新。例如,如果记录上有类似行版本或时间戳的东西,可以在更新过程中进行比较,以警示在获取数据快照时和进行更新时已记录的更改。检测这样的详细信息需要返回以获取更新时的当前持久化状态,这不应被视为不必要或昂贵。例如,从数据库按 PK 读取单个记录实际上是你能够进行的最快速的操作之一,而且这也是确保你不会处理可能过时的状态的唯一方法。如果存在另一个用户/实例可能在这两个时间点之间读取并更新了该行的可能性,应用程序应该处理这种可能性。

英文:

My general advice when it comes to EF and applications is to avoid treating Entities as a data transport where they travel between separate actions. (reads and writes)

In a web application for instance use a View Model defined and projected just like your example at the end to selectively pull the information from the aggregate structure that you need. Then if/when you go to persist changes to that entity, reload just the entity or entities you need to update. You don't need to re-load the entire aggregate root to do so. The same applies to thick client applications. We don't need an entire aggregate structure to display information to a user and there is still a lag time between when the information was first read, and when that user chooses to submit any changes.

An added consideration for this is detecting and handling concurrent updates. For example if there is something like a RowVersion or Timestamp on the record that can be compared during the update to warn about changes that have been recorded between the time that the data snapshot was taken and the update is made. Detecting details like this requires going back to get the current persisted state at the time of update, and this shouldn't be anything frowned upon as unnecessary or expensive. Reading a single record from the database by PK for instance is about as fast an operation as you're going to get, and it is the only way to ensure you aren't potentially dealing with stale state. If there is a possibility that another user/instance could have read and updated that row between those two points in time, the application should handle that possibility.

huangapple
  • 本文由 发表于 2023年6月9日 01:41:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76434424.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定