Entity Framework Contains() 只在非空 int 时使用。

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

Entity Framework Contains() only if non-null int

问题

我认为这是正确的,但想要检查一下,因为我对EF仍然不太熟悉,有时还在编写非最佳查询。

对于这些类:

public class Country : IOrganization
{
    public int Id { get; private set; }

    public ICollection<Campaign>? Campaigns { get; private set; }
}

public class Campaign : IOrganization
{
    public int Id { get; private set; }

    public State? State { get; private set; }
    public int? StateId { get; set; }

    public Country? Country { get; private set; }
    public int? CountryId { get; set; }
}

每个Campaign都由一个State或一个Country拥有。不会同时拥有两者,也不会都没有。

对于即将删除的Countries列表,我需要获取关联的子Campaigns列表。所以我正在执行以下操作,检查非空的CountryId,然后使用Contains()。

List<Country> deletedCountries = ChangeTracker.Entries<Country>()
    .Where(e => e.State == EntityState.Deleted)
    .Select(e => e.Entity).ToList();

List<Campaign> campaignsIndirectDelete = await Campaigns
        .Where(campaign => campaign.CountryId != null)
        .Where(campaign => deletedCountries
            .Select(country => country.Id)
            .Contains(campaign.CountryId!.Value))
        .ToListAsync();

这是实现此目标最有效的方法吗?

更新:

上述代码位于我的DbContext.SaveChanges()/SaveChangesAsync()中。它用于处理Entity Framework无法正确处理级联删除的情况。(请不要在此问题中讨论如何结构化模型以使Entity Framework能够级联删除,因为这与此问题无关。)

我需要这些列表首先删除每个子集合,然后删除集合对象本身。这限制了我可以做的事情。

英文:

I think this is right but want to check as I'm still not totally comfortable with EF and so am still sometimes writing non-optimal queries.

For the classes:

public class Country : IOrganization
{
	public int Id { get; private set; }

	public ICollection&lt;Campaign&gt;? Campaigns { get; private set; }
}

public class Campaign : IOrganization
{
	public int Id { get; private set; }

	public State? State { get; private set; }
	public int? StateId { get; set; }

	public Country? Country { get; private set; }
	public int? CountryId { get; set; }
}

Every campaign is owned by a State or a Country. Never both, never neither.

For a list of Countries (that I'm about to delete) I need to get the associated list of child Campaigns. So I'm doing the following, checking for a non-null CountryId and then using Contains().

List&lt;Country&gt; deletedCountries = ChangeTracker.Entries&lt;Country&gt;()
	.Where(e =&gt; e.State == EntityState.Deleted)
	.Select(e =&gt; e.Entity).ToList();

List&lt;Campaign&gt; campaignsIndirectDelete = await Campaigns
		.Where(campaign =&gt; campaign.CountryId != null)
		.Where(campaign =&gt; deletedCountries
			.Select(country =&gt; country.Id)
			.Contains(campaign.CountryId!.Value))
		.ToListAsync();

Is this the most efficient way to accomplish this?

Update:

The above code is in my DbContext.SaveChanges()/SaveChangesAsync(). It's there to handle deletes where Entity Framework can't correctly handle cascading deletes. (Please avoid discussion in this question about the best way to structure models so Entity Framework can cascade deletes as that is off-topic to this question.)

I need these lists to first delete a child collection in each. Then I delete the collections objects too. So that constrains what I can do.

答案1

得分: 1

你的方法似乎是正确的,但是它过于复杂了。由于您没有分享实际删除的代码,而只是确定需要删除的部分,很难从效率的角度评论您的实际删除。

然而,绝对最高效的删除方法可能是类似于以下内容:

delete from campaign where CountryId in (1, 2, 3)

或者

update campaign set IsDeleted = 1 where CountryId in (1, 2, 3)

然而,有时通过提高效率来获得的收益并不是很大。因此,在优化效率之前,请确保您确实存在需要解决的效率问题。

现在,如果您想简化代码,那么您可以指定

ON DELETE CASCADE

对于您的FOREIGN KEY约束,因此,如果您真的要删除记录(而不是通过将删除字段标记为true来对其进行软删除),则RDBMS将在后台自动执行删除,您无需解决任何问题,除非您真的有性能问题。

如果对于国家有一个IsDeleted字段,那么您也可以通过手动级联删除/更新campaigns来update/delete它。您还可以在Country记录的IsDeleted字段更改时使用触发器来相应地更新与其关联的campaigns。

英文:

Your approach seems to be correct, but it's overcomplicating the problem. Since you have not shared the code of the actual delete, but only the part which determines what needs to be deleted, it's difficult to comment on your actual deletion from the perspective of efficiency.

Yet, the absolute most efficient removal is that which translates to something like

delete from campaign where CountryId in (1, 2, 3)

or

update campaign set IsDeleted = 1 where CountryId in (1, 2, 3)

Yet, sometimes you do not win that much by improving efficiency. So, before you are to optimize efficiency make sure that you actually have an efficiency problem to fix.

Now, if you want to simplify your code, then you could specify

ON DELETE CASCADE

for your FOREIGN KEY constraints, so, if you are deleting the record for real (rather than soft-deleting it by marking a deleted field as true), then the RDBMS will perform the deletion automatically in the background and you will have nothing to resolve, unless you really have a performance issue.

If you have an IsDeleted field for countries, then you can also update/delete the campaigns to manually cascade your deletion. You can also use a trigger upon the change of an IsDeleted field of Country records to update the campaigns associated to it accordingly.

huangapple
  • 本文由 发表于 2023年6月5日 23:24:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76407888.html
匿名

发表评论

匿名网友

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

确定