实体框架中是否可以获得0..1到0..1的关系? [英] Is it possible to capture a 0..1 to 0..1 relationship in Entity Framework?

查看:107
本文介绍了实体框架中是否可以获得0..1到0..1的关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法为实体框架中的可空的外键关系建立可空的反向导航属性?在数据库说明中, 0..1至0..1 关系。



我尝试过如下,但是我收到错误消息:


无法确定类型Type1和Type2之间的关联的主体结束。必须使用关联流畅的API或数据注释来明确地配置此关联的主体。




  public class Type1 {

public int ID {get;组; }

public int? Type2ID {get;组; }
public Type2 Type2 {get;组; }
}

public class Type2 {

public int ID {get;组; }

public int? Type1ID {get;组; }
public Type1 Type1 {get;组;
}

我知道整数列只能存在于一个表或其他表中但肯定可以覆盖所有必要的情况?例如:

  Type1 Type2 
======== ========== =====
ID ID | Type1ID
-------- ---------------
1 1 | null
2 2 | 2

我试过使用数据注释(例如 [ForeignKey] code>一方面, [InverseProperty] 在这两个),但没有一个似乎帮助这个问题。



如果可能,数据注释解决方案将优于Fluent API。另外,如果这有帮助,则从域的角度看, c> int> 属性并不是绝对必要的。



有一个有趣的工作。我试过了,但有两个可选的关联:



驱动程序的映射配置中:

  this.HasOptional(t => t.Car).WithMany()。HasForeignKey(d => d.CarId); 

Car 的映射配置中:

  this.HasOptional(t => t.Driver).WithMany()。HasForeignKey(c => c.DriverId ); 

(没有数据注释替代)



我发现EF在创建新的驱动程序和汽车时只会在数据库中设置一个外键值。您必须单独设置和保存两个关联,管理自己的事务。使用现有对象,您仍然必须设置两个外键,尽管可以将其保存在一个 SaveChanges 调用中。



更好的选择?让我们看看...



选项2:



这是一对多关联,如你所指的链接。该模型需要外部约束,但创建关联是原子的。而且你还有一端的引用和另一端的集合。它可以轻松地与EF映射。



选项3:



您可以创建一个连接表 CarDriver 有两个外键,分别为 Car Driver 包含其独特的主键:





这是一个常规的多对多关联。默认情况下,EF将其映射为一个类模型,其中 Car 驱动程序具有指向彼此的集合属性,并且连接表没有被直接映射:

  public class Car 
{
public int CarId {get ;组; }
public string Name {get;组; }
public virtual ICollection< Driver>司机{get;组;
}

public class Driver
{
public int DriverId {get;组; }
public string Name {get;组; }
public virtual ICollection< Car>汽车组; }
}

现在创建关联是一个原子操作。将该模型与EF映射完全是可能的。相互引用已经消失,但是您仍然可以将集合属性的 FirstOrDefault()作为代理引用。



<但是有一个很重要的问题。现在每个对象都可以具有相关对应的任何数量。如果您创建一个关联,则需要一个编码业务规则,该规则检查所涉及的对象是否还没有任何关联。也许这个选项比选项2更糟糕。但是我提到了下一个选项:



选项4



选项3是原子的,但它也需要外部约束。要进行关联排除, CarDriver 中的两列都应该具有唯一键,因此每个汽车或驱动程序只能在表中出现一次。通过这些索引,该模型本身实现了双向可选的1:1关联。任何在其上工作的代码都必须遵守规则。安全和健全...



但是你想要吗?



你永远不会在EF代码中配置选项4。没有流畅的地图和数据注释。您必须使用迁移要做,或者首先工作数据库。



如果数据库被多个应用程序使用,最好的选择可能是定义数据模型中的约束。如果规则不太可能改变,这也可能是一个可行的选择。但是,如果只有一(或两个)应用程序在数据库上工作,并且业务规则可能会在将来更改,我更愿意使用更加宽松的数据模型和编码规则。编码业务逻辑比数据模型更容易改变。



此外,即使使用选项4,在尝试使用之前,您需要业务逻辑来检查关联的存在



结论



选项1最接近于你要什么。但是我不喜欢设置这两个外键的义务,它很容易被未来的开发人员遗忘或忽略。



但是,选项2和3对于编码的业务规则可以被遗忘。而替代品1结束,收藏品是不自然的。选项3对我有一些吸引力,因为 Car 驱动程序在数据库中完全独立,关联是一个记录,不可空的外键(DBA也喜欢这样)。



选项4具有相同的吸引力,当多个应用程序必须实现时,这是最好的选择外部约束需要强加在选项2和3上。而且,即使编码规则被遗忘,数据库约束也是最终的捕获。但是不能轻易地通过EF代码优先实现。


Is there a way to make a nullable reverse navigation property for a nullable Foreign Key relationship in Entity Framework? In database parlance, a 0..1 to 0..1 relationship.

I've tried as below, but I keep getting the error message:

Unable to determine the principal end of an association between the types 'Type1' and 'Type2'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

public class Type1 {

    public int ID { get; set; }

    public int? Type2ID { get; set; }
    public Type2 Type2 { get; set; }
}

public class Type2 {

    public int ID { get; set; }

    public int? Type1ID { get; set; }
    public Type1 Type1 { get; set; }
}

I understand that the integer column can only exist in one table or the other, but surely it should be possible to cover all necessary cases? E.g:

Type1      Type2
========   ===============
ID         ID   | Type1ID
--------   ---------------
1          1    | null
2          2    | 2

I've tried using data annotations (e.g. [ForeignKey] on one end, [InverseProperty] on both), but none of those seem to help the matter.

If possible, a data annotation solution would be preferred over Fluent API. Also, the int? property isn't strictly necessary from a domain perspective for either class, if that helps.

There is an interesting work-around here which implies it isn't possible to capture this kind of relationship in Entity Framework (effectively, an item that is optionally part of a collection) - if so, is there any documentation that would support this?.

解决方案

"This can't be hard" is what I though when I read your question. But again I found that one-to-one associations are treacherous bastards. Here we go.

I assume that by 0..1 – 0..1 you mean that two objects can exist independent of each other, but may also be exclusively associated to one another.

Lets make it concrete. Car and Driver. Imagine a pool of many cars and drivers, among them CarA and a DriverA. Now suppose you want CarA to get associated to DriverA, and your implementation is that DriverA links himself to CarA. But as soon as DriverA does this, you want CarA to be for DriverA only, CarA's association is not optional any more, so it should be set as well, immediately.

How to implement that?

Option 1:

If this is the working model:

public class Car
{
    public int CarId { get; set; }
    public string Name { get; set; }
    public int? DriverId { get; set; }
    public virtual Driver Driver { get; set; }
}

public class Driver
{
    public int DriverId { get; set; }
    public string Name { get; set; }
    public int? CarId { get; set; }
    public virtual Car Car { get; set; }
}

technically, DriverA can have a foreign key to CarA and CarA a foreign key to DriverB.

Therefore, when the foreign key DriverA-CarA is established you should "simulaneously" establish the reverse foreign key CarA-DriverA. That is something you should do in code, meaning that it's a business rule. And in reality, it's not an atomic operation, so you must make sure that it's done in one database transaction.

The class model at least supports the use case, but it's too permissive. It needs to be constrained. More importantly, it won't work with EF. EF complaints about having to set a principal end. And if you do that, EF will not create a bidirectional association.

An alternative mapping was proposed here. I tried that but with two optional associations:

In the Driver's mapping configuration:

this.HasOptional(t => t.Car).WithMany().HasForeignKey(d => d.CarId);

In the Car's mapping configuration:

this.HasOptional(t => t.Driver).WithMany().HasForeignKey(c => c.DriverId);

(There is no data annotation alternative)

I found that EF only sets one foreign key value in the database when creating a new driver and car. You have to set and save both associations separately, managing your own transaction. With existing objects you still have to set both foreign keys, although this can be saved in one SaveChanges call.

Better options? Let's see...

Option 2:

This is the one-to-many association as mentioned in the link you refer to. This model needs external constraints, but creating the association is atomic. And you've still got a reference on one end and a collection on the other end. And it maps easily with EF.

Option 3:

You could create a junction table CarDriver that has two foreign keys, to Car and Driver, both of which comprise its unique primary key:

This is a regular many-to-many association. By default, EF would map this as a class model in which Car and Driver have collection properties pointing to each other, and the junction table is not mapped directly:

public class Car
{
    public int CarId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Driver> Drivers { get; set; }
}

public class Driver
{
    public int DriverId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Car> Cars { get; set; }
}

Now the creation of association is an atomic operation. It's perfectly possible to map this model with EF. The mutual references are gone, but you still can get the FirstOrDefault() of the collection properties as a surrogate reference.

But there's an important gotcha. Now each object can have any number of associated counterparts. If you create an association, you need a coded business rule which checks if the the involved objects don't have any associations yet. Maybe this option is even worse than option 2. But I mentioned it because of the next option:

Option 4

Option 3 is atomic, but it also needs external constraints. To make an association exclusive, both columns in CarDriver should have unique keys, so each car or driver can only occur once in the table. By these indexes the model implements a bidirectionally optional 1:1 association all by itself. Any code working on it has to obey the rules. Safe and sound...

But do you want that?

You're never going to configure option 4 in EF code-first. Not with fluent mapping nor data annotations. You'd have to use Migrations to do it, or work database-first.

If the database is used by multiple applications your best option may be to pin down the constraints in the the data model. If the rule is not likely to change this may also be a viable option. However, if only one (or two) applications work on the database and the business rule may change in the future I'd prefer a more liberal data model accompanied by coded rules. Coded business logic is a lot easier to change than a data model.

Moreover, even with option 4 you need business logic to check the existence of the association before you try to create it, otherwise an ugly database exception will do this for you.

Conclusions

Option 1 is closest to what you want. But I don't like the obligation to set both foreign keys, it's easily forgotten or ignored by future developers.

But Option 2 and 3 have even heavier requirements in terms of coded business rules that can be forgotten. And the collections are unnatural as surrogate "1" ends. Option 3 has some appeal to me because Car and Driver are completely independent in the database and the association is a record with non-nullable foreign keys (DBAs tend to like that too).

Option 4 has the same appeal, and it is the best option when multiple applications would have to implement the external constraints that need to be imposed on option 2 and 3. Also, even if coded rules are forgotten, the database constraints are a final catch. But it can't easily be implemented by EF code-first.

这篇关于实体框架中是否可以获得0..1到0..1的关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆