[英]Entity Framework Core: Update relation with Id only without extra call
I'm trying to figure out how to deal with 'Single navigation property case' described in this doc: 我正在试图弄清楚如何处理本文档中描述的“单一导航属性案例” :
Let's say we have 2 models. 假设我们有2个型号。
class School
{
public ICollection<Child> Childrens {get; set;}
...
}
and 和
class Child
{
public int Id {get; set;}
...
}
So it's many-to-one relationship created by convention, without explicit foreign key in a Child
. 因此,它是由约定创建的多对一关系,在Child
没有显式外键。
So the question is if we have Child
instance and know School.Id
is there a way to update this relation without extra call to database to obtain School
instance. 所以问题是我们是否有Child
实例并且知道School.Id
是否有一种更新此关系的方法,而无需额外调用数据库来获取School
实例。
So the question is if we have
Child
instance and knowSchool.Id
is there a way to update this relation without extra call to database to obtainSchool
instance. 所以问题是我们是否有Child
实例并且知道School.Id
是否有一种更新此关系的方法,而无需额外调用数据库来获取School
实例。
Yes, it's possible. 是的,这是可能的。 You can create a fake stub School
entity instance with Id
only, Attach
it to the DbContext
(this way telling the EF that it is existing ), Attach
the Child
instance for the same reason, and then add the Child
to the parent collection and call SaveChanges
: 您可以创建一个假的存根 School
与实体实例Id
唯一, Attach
它到DbContext
(这样讲,它是现有的EF), Attach
的Child
实例出于同样的原因,然后添加Child
以父集合和呼叫SaveChanges
:
Child child = ...;
var schoolId = ...;
var school = new School { Id = schoolId };
context.Attach(school);
context.Attach(child);
school.Childrens.Add(child);
context.SaveChanges();
Update: Actually there is another cleaner way, since even if the entity has no navigation or FK property, EF Core allows you to access/modify the so called Shadow Properties 更新:实际上还有另一种清洁方式,因为即使实体没有导航或FK属性,EF Core也允许您访问/修改所谓的阴影属性
Shadow properties are properties that do not exist in your entity class. 阴影属性是实体类中不存在的属性。 The value and state of these properties is maintained purely in the Change Tracker. 这些属性的值和状态仅在Change Tracker中维护。
as soon as you know the name. 一旦你知道这个名字。 Which in your case, without configuration would be by convention "SchoolId"
. 在您的情况下,没有配置将按照惯例"SchoolId"
。
So no fake School
entity instance is needed, just make sure the Child
is attached and then simply set the shadow property through ChangeTracker API: 因此,不需要伪School
实体实例,只需确保附加了Child
,然后通过ChangeTracker API简单地设置shadow属性:
context.Attach(child);
context.Entry(child).Property("SchoolId").CurrentValue = schoolId;
context.SaveChanges();
No, there isn't ANY way you could do that by using ORM and strong typing that the ORM offers you, w/o 不,没有任何方法可以通过使用ORM和ORM为您提供的强类型来做到这一点,没有
SchoolId
on Child
) 至少一个ForeignKey / Principal属性( SchoolId
on Child
) performing a raw query (which beats the idea of having ORM for strong typing) and being DB agnostic at the same time 执行原始查询(这比使用ORM进行强类型操作的想法)和同时与DB无关
// Bad!! Database specific dialect, no strong typing ctx.Database.ExecuteSqlCommandAsync("UPDATE Childs SET schoolId = {0}", schoolId);
When you choose to use an ORM you have to accept certain technical limitations of the ORM framework in question. 当您选择使用ORM时,您必须接受相关ORM框架的某些技术限制。
If you want to follow Domain Driven Design (DDD) and remove all db specific fields form your entities, it won't be easy to use your domain models as entities. 如果要遵循域驱动设计(DDD)并从实体中删除所有特定于数据库的字段,则将域模型用作实体将非常简单。
DDD and ORM don't have very good synergies, there are way better approaches for this, but require a different architectural approach (namely: CQRS+ES (Command Query Responsibility Segregation with Event Sourcing). DDD和ORM没有很好的协同作用,有更好的方法,但需要不同的架构方法(即:CQRS + ES(命令查询责任分离与事件源)。
This works much better with DDD, since the Events from the EventSourcing are just simple (and immutable) message classes which can be stored as serialized JSON in the database and replayed to reconstruct the domain entity's state. 这对DDD来说效果要好得多,因为来自EventSourcing的事件只是简单(和不可变)的消息类,它们可以作为序列化的JSON存储在数据库中并重放以重建域实体的状态。 But that's a different story and one could write whole books about this topic. 但这是一个不同的故事,人们可以写关于这个主题的整本书。
The above scenario is only possible in a single DB operation, if your Child
objects a navigation property/"back reference" to the parent. 如果您的Child
将导航属性/“后向引用”对象表示父对象,则上述方案仅在单个数据库操作中可用。
class School
{
public ICollection<Child> Childrens {get; set;}
...
}
and 和
class Child
{
public int Id {get; set;}
// this is required if you want do it in a single operation
public int SchoolId { get; set; }
// this one is optional
public School { get; set; }
...
}
Then you can do something like: 然后你可以这样做:
ctx.Childs.Add(new Child { Id = 7352, SchoolId = 5, ... });
Of course you first have to know the school Id and know it's valid, otherwise the operation will throw an exception if SchoolId
is an invalid value, so I wouldn't recommend this approach. 当然,您首先必须知道学校ID并知道它是有效的,否则如果SchoolId
是无效值,则操作将抛出异常,因此我不推荐这种方法。
If you only have the childId
and not adding a whole new child you'll still have to get the child first. 如果你只有childId
并且没有添加一个全新的孩子,你仍然必须childId
孩子。
// childId = 7352
var child = ctx.Childs.FirstOrDefault(c => c.Id == childId);
// or use ctx.Childs.Find(childId); if there is a chance that
// some other operation already loaded this child and it's tracked
// schoolId = 5 for example
child.SchoolId = schoolId;
ctx.SaveChanges();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.