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

时间:2017-05-19 07:38:39

标签: c# entity-framework domain-driven-design entity-framework-core

在DDD中,习惯上保护像这样的实体属性:

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使用反射,因此它可以处理所有这些私有。

但是如果你需要附加一个实体而不加载它(这是一件很常见的事情),那该怎么办呢?

var customer = new Customer { Id = getIdFromSomewhere() };       // can't do this!
myContext.Set<Customer>().Attach(customer);

这不会起作用,因为Id setter是私有的。

处理语言和DDD之间不匹配的好方法是什么?

思路:

  • Id公开(并打破DDD)
  • 创建一个构造函数/方法来填充虚拟对象(没有意义)
  • 使用反射(&#34;作弊&#34;)
  • ???

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

你如何处理这种情况?

PS我做了一个简单的基准测试,使用反射创建一百万个实例需要大约10秒。因此,与点击数据库或EF执行的反射相比,额外的开销很小。

2 个答案:

答案 0 :(得分:1)

“习惯”隐含意味着它不是一个硬性规则,所以如果你有特定的理由在你的应用程序中违反这些规则,那么就去做吧。公开属性设置器比对此进行反思更好:不仅因为性能问题,还因为它使得在应用程序中放置不需要的副作用变得更加容易。反思不是解决这个问题的方法。

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

假设您有充分的理由想要更改ID,我实际上认为您自己在评论中提供了答案:

  

所以你会有方法来控制你的对象会发生什么   这样做,约束属性,使它们不暴露   被设定或修改为“威利不可能”。

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

编辑: 阅读this之后,我尝试自己进行更多测试,您可以拥有以下内容:

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.
    //...
}

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

答案 1 :(得分:0)

这就是我正在做的事情,使用反射。我认为这是最糟糕的选择。

server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;

server_name xxx.xx.xxx.xxx; # Replace with your domain

root /usr/share/nginx/html;
index index.html index.htm;

client_max_body_size 10G;

location ~* \.png {
    root html;
    index index.html;
    include /etc/nginx/mime.types;
}

location /Images{
    root html;
}

location /blog {
    proxy_pass http://localhost:2368;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
} 
}

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

  • EF使用反射来处理很多东西
  • 数据库读取比这些反射调用慢得多(我在问题中提到的基准测试显示这个性能是多么微不足道,创建一百万个实例大约需要10秒)
  • 域名是完全DDD - 你不能在一个奇怪的状态下创建一个实体,或者在不经过构造函数的情况下创建一个实体(我在上面做了但我为特定情况作了欺骗,就像EF那样)