No
The argument was made however that this is a flawed approach and that my DAOs should not be structurally tied to my domain model. I am trying to understand if this is so and why.
Because you cannot inherently rely on your domain and entity models to match. In a comment on a (now deleted) answer you said:
Appreciate the response, but find my self disagreeing with the reasons. "The two rarely match": I find this to not be true in practice, especially if starting the data structure from scratch I will be making them match
Just because two things happen to look alike (in a particular way) does not mean they should share a tangible connection in that likeness.
Changing the topic for a simpler example, just because Animal
and User
both have a string Name
property, does not mean that you're required to abstract this into a common interface. There's no inherently relationship between the name of an animal and that of a user, even though they both have a name.
It's possible that in the future, one of these will change (e.g. we will store users' first and last names separately) and the other will not (animals have one name). Had we made a shared interface, we would be stuck having to untangle this shared commonality as the two names did not have as much in common as we initially thought they did.
The same argument applies to your use case of entities and domain objects. Just because they match today, doesn't mean they will match tomorrow. Let's use a simple example of a person:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string NationalIdentificationNumber { get; set; }
}
public class PersonEntity : Person { }
This works the way you expect it to. EF will use PersonEntity
's properties (including inherited ones) and will store everything.
I'll give you two examples of why this is a problematic situation.
1. What happens when you no longer want to store all data in the database?
Suppose the requirements change, and you're expected to always fetch a person's latest personal information from a government API, using the person's national identificaton number. This means that you'll only store the person's NIN in the database, and the other properties are retrieved from a different source at a later stage.
In the above setup, you can't do that. EF will still store all the properties, and there's no way to put an ignore attribute on an inherited property.
Even if you tell EF to ignore certain properties (I suspect some type of configuration option exists), it makes no sense to inherit from a class whose properties you then want to hide - it's a strong code smell of using bad inheritance strategy.
Instead, you shouldn't have been using inheritance, but simply two separate classes (which happen to have the same properties):
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string NationalIdentificationNumber { get; set; }
}
public class PersonEntity
{
public string Name { get; set; }
public int Age { get; set; }
public string NationalIdentificationNumber { get; set; }
}
Now, you are able to alter one without affecting the other, e.g. when the database no longer stores the entire domain model:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string NationalIdentificationNumber { get; set; }
}
public class PersonEntity
{
public string NationalIdentificationNumber { get; set; }
}
2. What if you want to store your data differently?
You don't always want to handle your data (i.e. your domain model) the same way as you store it (i.e. your entity). Granted, EF tries its best to ensure you don't have to worry about conversions by handling all handleable types for you, but there may be cases where you want the stored data to be different from the domain data.
For the sake of example, let's use a simple case of wanting to store a record's status as a FK to a table, but in your domain you wish to use an enum. If you don't like the example, swap it out for one you like better, the technical argument remains the same.
Using inheritance:
public class Record
{
public MyStatusEnum Status { get; set; }
}
public class RecordEntity : Record
{
public int StatusEntityId { get; set; }
public virtual RecordStatusEntity StatusEntity { get; set; }
}
We get into the same problem as before: you're trying to derive from a class but then selectively hide some of the base class' properties, which is still a strong code smell of using bad inheritance strategy.
To summarize
Yes, it initially seems a bit silly to create two classes with exactly the same properties. Developers have a natural urge to want to merge the two as gracefully as they can.
But don't fall into the trap of thinking that because two things look alike today, that they therefore represent the same thing or will always change in the same way at the same time.
There are two different purposes here (domain representation vs persistence storage), and these purposes need to be represented separately, because tying them together means that it will cost extra time and effort when they have to separate in the future.
Think of it this way: since you and a coworker always sit next to each other when working, and it's easier to build one bench than it is to build two desk chairs, it makes sense to build one bench for the two of you, right?
But that decision will become a costly decision when one of you moves to another location and the other doesn't, as you're now either going to have to build an additional desk chair (and thus have a bench + chair, which is one seat too many in total) or removing the bench and building two desk chairs.
In either case, the resulting task is inefficient, compared to when you had built two desk chairs to begin with. That decision would've meant that you and your coworker would not be needlessly tied together just because you happened to sit next to them at the time the decision was made.
I
-prefix convention; however, IMO, the argument is somewhat superfluous, and that convention is so well established in the C# community that people may find it confusing if it is not followed.