EntityFrameWork6“外键约束失败”关于无效的外国钥匙
我将实体定义为这样:
public class Entity : BaseModel // Has the already ID defined
{
private int? companyId;
public Company? Company { get; set; }
public int? CompanyId {
get => this.companyId == 0 ? null : this.companyId; // I tried this for debugging purposes to force this value to "null" -> made no difference
set => this.companyId = value;
}
}
public class Company : BaseModel // Has the already ID defined
{
public IEnumerable<Entity> Entities { get; set; } = new List<Entity>();
}
无论如何,如果我将companiD
设置为null
,我的db会引发一条邮件,并带有消息:“外键约束失败”。如果companiD
设置为例如123
,则该关系将相应解决。
我的意思是,这是有道理的,EF在我的数据库中找不到null
,但是我如何否则要设置一个可选值?我仅使用代码首先注释,因此我的上下文的on ModeLcreating
是完全空的。
I have my entity defined like this:
public class Entity : BaseModel // Has the already ID defined
{
private int? companyId;
public Company? Company { get; set; }
public int? CompanyId {
get => this.companyId == 0 ? null : this.companyId; // I tried this for debugging purposes to force this value to "null" -> made no difference
set => this.companyId = value;
}
}
public class Company : BaseModel // Has the already ID defined
{
public IEnumerable<Entity> Entities { get; set; } = new List<Entity>();
}
Anyway, if I set the CompanyId
to null
, my DB throws an exception with the message: "FOREIGN KEY constraint failed". If the CompanyId
is set to, e.g. 123
, the relationship is resolved accordingly.
I mean, it makes sense, that EF cannot find null
in my DB, but how do I want to set an optional value otherwise? I am using code first annotations only, hence my OnModelCreating
of my context is completely empty.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,您如何加载实体?您是否是通过ID加载实体并试图与公司分离它,还是您加载了一家公司的实体并试图删除一个协会?
通常,在使用带有导航属性的关系时,您要通过导航属性而不是FK属性来取消缔合(或删除它们)。例如,如果要加载公司并希望取消缔合您应该急切地加载实体的一个实体之一,则从集合中删除所需的实体:
前提是将公司与实体之间的关系正确设置为可选的Hasmany,然后提供这些代理已加载,EF应该可以解决将EntityToreMove的FK设置为null。
如果您想从实体方面做到这一点:
那也应取消实体的关联。如果这些不起作用,那么您的映射可能是为所需关系设置的,尽管我正在从内存中撤出此映射,因此我可能需要启动一个示例来验证。 :)您还应该检查任何可能在尝试删除一个代码时将其设置为0的代码,无论是由于某些映射还是次要化而发生的代码。当实体以独立状态传递或避免到控制器方法中时,可能会发生这样的怪异行为。 (应该避免)
更新:这样的代码可能非常危险,并导致出乎意料的问题,例如您遇到的内容:
update()
通常用于独立实体,它将自动处理所有值在实体中进行了修改。如果模型已经是通过上下文跟踪的实体(并且为更改跟踪设置了上下文),那么这几乎是不必要的。但是,呼叫链中的某些内容或构建模型(IE实体)的任何地方都将无效的FK设置为0而不是#NULL。在视图中,这本可以从表单等中进行的,并基于删除选择的默认值发送到控制器作为整数值。理想情况下,实体类别不应用于从视图到控制器等的数据传输形式,而不是使用POCO视图模型或DTO。要纠正当前代码的行为,您可以尝试以下操作:在这种情况下,我们从DBContext加载实体。如果使用DBContext跟踪的实体调用此方法,则dbentity将与实体相同。在这种情况下,随着更改跟踪客户/客户ID参考应已删除。我们不需要设置实体状态或调用更新。 Savechanges应该坚持更改。相反,如果实体是一个独立的副本,则(可能基于该值的情况为基于0的情况),则参考将有所不同。在这种情况下,应将修改后实体中的允许值复制到dbentity中,然后我们可以检查该独立实体中的客户ID中的#null或0,如果是的,请在保存之前从dbentity中删除客户参考。
这里的警告是:
这不会用作纯通用实现。要更新“实体”类,我们需要了解这些关系(例如客户)的知识,因此该数据服务,存储库或您需要进行什么实施是具体的和非代码。它可以扩展用于共同功能的通用基类,但我们不能依靠纯通用解决方案。 (通用方法起作用的工作,其中实施是在受支持的类中相同的。)
这也意味着要删除尝试在实体类中处理零的尝试。它应该只是:
明确标记外国钥匙是一种好习惯,可以避免惊喜,而当您最终发现自己需要违反在简单场景中可容纳EF的约定。
How are you loading the entities in the first place? Are you loading an Entity by ID and trying to dis-associate it from a company, or have you loaded a company with it's entities and trying to remove one association?
Normally when working with relations where you have navigation properties, you want to de-associate them (or delete them) via the navigation properties, not the FK properties. For instance if loading a company and wanting to de-associate one of the entities you should eager-load the entities then remove the desired one from the collection:
Provided that the relationship between Company and Entity is set up properly as an optional HasMany then provided these proxies are loaded, EF should work out to set the entityToRemove's FK to null.
If you want to do it from the Entity side:
That too should de-associate the entities. If these don't work then it's possible that your mapping is set up for a required relationship, though I am pulling this from memory so I might need to fire up an example to verify. :) You also should be checking for any code that might set that CompanyId to 0 when attempting to remove one, whether that might be happening due to some mapping or deserialization. Weird behaviour like that can occur when entities are passed around in a detached state or deserialized into controller methods. (which should be avoided)
Update: Code like this can be very dangerous and lead to unexpected problems like what you are encountering:
Update()
is typically used for detached entities, and it will automatically treat all values in the entity as Modified. If model was already an entity tracked by the Context (and the context is set up for change tracking) then it is pretty much unnecessary. However, something in the calling chain or wherever has constructed the model (i.e. Entity) has set the nullable FK to 0 instead of #null. This could have been deserialized from a Form etc. in a view and sent to a Controller as an integer value based on a default for a removed selection. Ideally entity classes should not be used for this form of data transfer from view to controller or the like, instead using a POCO view model or DTO. To correct the behaviour as your code currently is, you could try the following:In this case we load the entity from the DbContext. If this method was called with an entity tracked by the DbContext, the dbEntity would be the same reference as entity. In this case with change tracking the Customer/CustomerId reference should have been removed. We don't need to set entity state or call Update. SaveChanges should persist the change. If instead the entity was a detached copy deserialized, (likely the case based on that 0 value) the reference would be different. In this case, the allowed values in the modified entity should be copied across to dbEntity, then we can inspect the CustomerId in that detached entity for #null or 0, and if so, remove the Customer reference from dbEntity before saving.
The caveats here are:
This won't work as a pure Generic implementation. To update an "Entity" class we need knowledge of these relationships like Customer so this data service, repository, or what-have-you implementation needs to be concrete and non-generic. It can extend a Generic base class for common functionality but we cannot rely on a purely Generic solution. (Generic methods work where implementation is identical across supported classes.)
This also means removing that attempt at trying to handle Zero in the Entity class. It should just be:
Marking Foreign Keys explicitly is a good practice to avoid surprises when you eventually find yourself needing to break conventions that EF accommodates in simple scenarios.