0

In DDD it is customary to protect an entity's properties like this:

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 uses reflection so it can handle all those privates.

But what if you need to attach an entity without loading it (a very common thing to do):

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

This won't work because the Id setter is private.

What is a good way to deal with this mismatch between the language and DDD?

Ideas:

  • make Id public (and break DDD)
  • create a constructor/method to populate a dummy object (makes no sense)
  • use reflection ("cheat")
  • ???

I think the best compromise, is to use reflection, and set that private Id property, just like EF does. Yes it's reflection and slow, but much faster than loading from the database. And yes it's cheating, but at least as far as the domain is concerned, there is officially no way to instantiate that entity without going through the constructor.

How do you handle this scenario?


PS I did a simple benchmark and it takes about 10s to create a million instances using reflection. So compared to hitting the database, or the reflection performed by EF, the extra overhead is tiny.

grokky
  • 8,537
  • 20
  • 62
  • 96
  • "In DDD it is customary to protect an entity's properties like this:" Where did you heard that? Every case is based on the domain. – Karolis Kajenas May 19 '17 at 07:54
  • @Karolis It's not a rule, I wrote "customary". When you do it though, there is a drawback. How does one deal with that drawback? – grokky May 19 '17 at 07:55
  • How did you came up with idea that it is customary? – Karolis Kajenas May 19 '17 at 07:58
  • @Karolis Prefer not to argue about tangential stuff. Though I'd really appreciate help on the topic itself. – grokky May 19 '17 at 08:02
  • I want to know the purpose of using private setters in the first place? How is that dictated by your domain? P.S. private setters are obsolete way of populating props from ctor. You just write `Prop{get;}` – Karolis Kajenas May 19 '17 at 08:06
  • @Karolis Here's something from the highly respected [Julie Lerman herself](http://thedatafarm.com/data-access/entity-framework-private-constructors-and-private-setters/). – grokky May 19 '17 at 08:10

2 Answers2

1

"customary" implicitly means it's not a hard set rule, so if you have specific reasons to break those rules in your application, go for it. Making the property setter public would be better than going into reflection for this: not only because of performance issues, but also because it makes it much easier to put unwanted side-effects in your application. Reflection just isn't the way to deal with this.

But I think the first question here is why you would want the ID of an object to be set from the outside in the first place. EF uses the ID primarily to identify objects and you should not use the ID for other logic in your application than just that.

Assuming you have a strong reason to want to change the ID, I actually think you gave the answer yourself in the source you just put in the comments:

So you would have methods to control what happens to your objects and in doing so, constrain the properties so that they are not exposed to be set or modified “willy nilly”.

You can keep the private setter and use a method to set the ID.

EDIT: After reading this I tried doing some more testing myself and you could have the following:

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

This code leaves the private setters as they were (you will need the methods for editing of course) and only the required changes are pushed to the db afterwards. As is also explained in the link above, only changes made after attaching are used and you should make sure you don't manually set the state of the object to modified, else all properties are pushed (potentially emptying your object).

Community
  • 1
  • 1
RoelofJ
  • 65
  • 9
  • No I don't want to change the ID - I want to attach an entity to the context, without loading it. I agree with your first comment though, do what works. – grokky May 19 '17 at 08:38
  • @grokky
    Why can't you use the public constructor in this, like: `var customer = new Customer(getIdFromSomewhere(), getNameFromSomewhere()); myContext.Set().Attach(customer); `//or with another public ctor requiring only ID `var customer = new Customer(getIdFromSomewhere()); myContext.Set().Attach(customer);`
    – RoelofJ May 19 '17 at 09:47
  • I definitely could. But what if the constructor has 10 arguments? That was only a simple example. Name, telephone, city, account code, etc. The idea is to save time by attaching an entity only to make queries work. That clashes strongly with DDD of course, so I'm looking for a middle way, or to break DDD like you suggested which is a good option in this case. – grokky May 19 '17 at 09:52
  • @grokky hmm, what kind of queries would you like to do on the object? Do you want to edit/delete the object (and therefore don't want to load the entire object from the db first)? – RoelofJ May 19 '17 at 10:32
  • Perhaps... think of those cases where you need to update entity A with a reference to entity B. You don't need to load B from the database, you only need to attach it to the context (via its ID) before you update A. Standard stuff, except when B's properties are private. I think reflection is the best bad option. – grokky May 19 '17 at 11:28
  • @grokky I edited the answer to show how I think you might be able to do that, let me know if this works for you and if not, how so. – RoelofJ May 19 '17 at 11:34
  • Yeah that special constructor would work, but I don't think it solves the problem, it just pushes the problem somwhere else. I added an answer to show how I'm doing it, without compromising DDD. – grokky May 19 '17 at 13:57
  • @grokky I can't comment anywhere but here, so I'll have to post it here instead of below your answer, sorry for that. I think using the constructor with only id is a choice that's easier to maintain than using reflection, see also: https://softwareengineering.stackexchange.com/questions/143205/reflection-is-using-reflection-still-bad-or-slow-what-has-changed-with-ref (answer of tdammers). I agree the performance hit is in this case not so relevant. – RoelofJ May 19 '17 at 14:27
  • 1
    In the end, defining constructors and setting access modifiers is only a tool to help keep your objects from getting corrupted by accident. Reflection is another tool and can be used to circumvent these restrictions when they prevent certain required paths. You can set the constructor and add a summary explaining when it can be used or you can use reflection to circumvent these restrictions and setup the object yourself. Both will work I think, so I guess the rest is a matter of preference. I've changed the code in my answer to reflect the setting of customer to order as shown in your answer. – RoelofJ May 19 '17 at 14:27
0

This is what I'm doing, using reflection. I think it's the best bad option.

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();

The implementations of those two reflection methods aren't important. What is important:

  • EF uses reflection for lots of stuff
  • Database reads are much slower than these reflection calls (the benchmark I mentioned in the question shows how insignificant this perf hit is, about 10s to create a million instances)
  • Domain is fully DDD - you can't create an entity in a weird state, or create one without going through the constructor (I did that above but I cheated for a specific case, just like EF does)
grokky
  • 8,537
  • 20
  • 62
  • 96