首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将实体附加到EF上下文,而不加载它们,而不牺牲DDD

将实体附加到EF上下文,而不加载它们,而不牺牲DDD
EN

Stack Overflow用户
提问于 2017-05-19 07:38:39
回答 2查看 260关注 0票数 0

在DDD中,通常保护实体的属性如下:

代码语言:javascript
复制
public class Customer
{
  private Customer() { }
  public Customer(int id, string name) { /* ...populate properties... */ }
  public int Id { get; private set; }
  public string Name { get; private set; }
  // and so on...
}

EF使用反射,这样它就可以处理所有这些私事。

但是,如果您需要在不加载实体的情况下附加一个实体(这是一件非常常见的事情):

代码语言:javascript
复制
var customer = new Customer { Id = getIdFromSomewhere() };       // can't do this!
myContext.Set<Customer>().Attach(customer);

这不起作用,因为Id设置器是私有的。

如何处理语言和DDD之间的这种不匹配?

想法:

  • 使Id公开(并破坏DDD)
  • 创建构造函数/方法来填充虚拟对象(没有意义)
  • 使用反射(“欺骗”)
  • ???

我认为最好的折衷办法是使用反射,并设置私有Id属性,就像EF一样。是的,它反射速度慢,但比从数据库加载要快得多。是的,这是欺骗,但至少就域而言,不经过构造函数就无法实例化该实体。

您如何处理这种情况?

PS --我做了一个简单的基准测试,使用反射创建100万个实例需要花费大约10s的时间。因此,与命中数据库或EF执行的反射相比,额外的开销非常小。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-05-19 13:56:15

这就是我在做的,用反射。我认为这是最好的选择。

代码语言:javascript
复制
var customer = CreateInstanceFromPrivateConstructor<Customer>();
SetPrivateProperty(p=>p.ID, customer, 10);
myContext.Set<Customer>().Attach(customer);

//...and all the above was just for this:
order.setCustomer(customer);
myContext.SaveChanges();

这两个反射方法的实现并不重要。重要的是:

  • EF在很多方面都使用反射。
  • 数据库读取要比这些反射调用慢得多(我在问题中提到的基准显示了这个perf的命中有多小,大约10s来创建100万个实例)。
  • 域是完全DDD的-您不能创建一个处于奇怪状态的实体,或者创建一个没有经过构造函数的实体(我在上面这样做了,但是我在一个特定的情况下作弊,就像EF一样)
票数 0
EN

Stack Overflow用户

发布于 2017-05-19 08:31:10

“习惯”含蓄地意味着它不是一条硬规则,所以如果你有具体的理由在你的应用程序中违反这些规则,那就去做吧。公开属性设置比考虑这一点更好:这不仅是因为性能问题,还因为它使得在应用程序中放置不必要的副作用变得更加容易。反思不是处理这件事的方法。

但我认为这里的第一个问题是,为什么首先要从外部设置对象的ID。EF主要使用ID来标识对象,您不应该将ID用于应用程序中的其他逻辑。

假设您有强烈的理由想要更改ID,我实际上认为您是在您刚刚在评论中输入的源代码中给出了答案:

因此,您将有方法来控制对象发生了什么,并在这样做时,约束属性,使其不暴露于设置或修改“willy nilly”。

您可以保留私有setter并使用方法来设置ID。

编辑:在阅读了this之后,我尝试自己做一些更多的测试,您可以拥有以下内容:

代码语言:javascript
复制
public class Customer
{
  private Customer() { }
  public Customer(int id) { /* only sets id */ }
  public Customer(int id, string name) { /* ...populate properties... */ }
  public int Id { get; private set; }
  public string Name { get; private set; }
  // and so on...

  public void SetName(string name)
  {
      //set name, perhaps check for condition first
  }
}

public class MyController
{
    //...
    var customer = new Customer(getIdFromSomewhere());
    myContext.Set<Customer>().Attach(customer);
    order.setCustomer(customer);
    myContext.SaveChanges(); //sets the customer to order and saves it, without actually changing customer: still read as unchanged.
    //...
}

这段代码使私有设置程序保持原样(当然,您需要编辑方法),然后只将所需的更改推送到db。正如上面的链接所解释的那样,只使用附加后所做的更改,并且您应该确保不手动将对象的状态设置为修改,否则所有属性都会被推送(可能清空对象)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44064187

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档