NB I'm not asking how to find/filter entities in the DB. I'm not asking about the difference between Find and Where. I'm not asking about the implication of generics.
According to the docs, the DbContext provides methods Find and Find<T>. Usually, I go through the DbSet but noticing that there's Add() on both the context directly and the DB set, I wanted to see what will happen if I try to filter directly on the context too, instead of the DB set.
I haven't found a single example of such call, though. A lot of information on going through the DB set and the differences mentioned in the disclaimer at the top. But I see no sample showing usage of Find() nor Find<T>() directly on the context.
Is it possible at all and if so, how? Intellisense is cryptic...
It does the same thing, but without using the DbSet. It works by specifying the type (entity) you want to fetch. You just have to specify the type, which must match your DbSet.
Is it possible at all and if so, how?
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options) { }
public DbSet<City> Cities{ get; set; }
public DbSet<State> States { get; set; }
}
And use it like so:
MyContext context = CreateContext() // Or inject it...
// Will return the City which have id == 5.
object myUntypedCity = context.Find(typeof(City), 5);
// Will return the city which have id == [your guid].
City myTypedCity = context.Find<City>(Guid.NewGuid());
As a sidenote; Add() on the Context works the same way. It will detect which type of entity it is and put it on the correct DbSet. I do not know what will happen if you have several DbSet using the same type.
why we have a non-generic Find() on the context given that quite a few
DBs are using integers for IDs, hence ensuring ambiguous ID values
between different entities/tables
In some scenarios you might not expose the DbSet or you only know the type of the entity. This can be very handy when making some very generic code where you don't have a typed entity nor know exactly what type of id (primary key) it has. It may be a Guid or an int. I myself have never found a practical use for Find on the DbContext.
When you use Find on a DbSet, the Type of entity is known, so it just needs to Find by the provided ID.
When you use Find on the DbContext, you either have to tell EF which Type (Entity) to find against from its known mappings, or you can use the Generic version which defines the Type of entity to search against.
Why would you use these methods instead of DbSet.Find? One example would be where you don't want to expose an aspect of your domain as a DbSet. For example if you have a DbSet of Customers, Businesses, etc. that all contain references to Address entities it would be uncommon to ever need to load an Address outside of that scope so you would probably opt not to expose a DbSet<Address> in your domain to potentially be abused through convenience. In the case where your DbContext actually does need to locate a particular Address, you could use DbContext.Find<Address>(addressId) to load it.
Honestly I don't use DbContext.Find(), I don't even use DbSet.Find(), I much, much prefer to work with IQueryable and projection. But hey, it's an option. :)
Related
In Domain-Driven Design, how can I hydrate the Id property when I retrieve an entity from a repository? When I create an entity for the first time (before it is persisted), I can generate a unique ID in the entity's constructor. But when I retrieve an entity from the repository, it already has an ID. How do I set the Id property in this case? Passing the ID to the entity's constructor doesn't feel right to me, but maybe it is the correct approach?
I am not using an object-relational mapping (ORM) tool.
public interface IPersonRepository
{
Person GetById(long id);
}
public abstract class Entity
{
public long Id { get; private set; }
protected Entity()
{
Id = // Generate a unique Id with some algorithm.
}
}
public sealed class Person : Entity
{
//...
}
When I CREATE the Entity for the first time (before its persistence), I can generate a unique id in Entity's constructor...
which may not be a good idea. Non deterministic data (like time, or copies of remote mutable state) should be inputs to your domain model. In practice, you will often get away with it; but that alone doesn't make it a good idea.
The usual answer is that the repository will fetch the persisted representation of the information (a DTO, for example), and hand that off to a factory whose purpose is the construction of the entity.
So the identity of the entity becomes just another piece of information passed from the repository to the factory.
Now, "factory" here is just another life cycle pattern; and it can take many different forms, including the form of constructor. In which case, the identifier would normally just be passed into the entity as an argument.
Identifiers in particular can be a bit weird, because they don't normally express business semantics. It's typical of the identifier pattern that they are opaque things that really only support equality comparison. Your entities almost never look at their own identifier to figure out what to do next.
But if your entity needs a reference to its own identifier, for whatever reason, you'll normally create that reference when you initialize the object, and leave it unchanged from that point forward (in other words, the entities identifier property is an immutable reference to an immutable value).
1) Aggregate or Entity?
I think there is some confusion in your question in terms of DDD. In general you shouldn't load entities. You should load Aggregate, through Aggregate root (which is entity), all other entities for this aggregate should be loaded automatically.
From Evans DDD:
only AGGREGATE roots can be obtained directly with database queries. All other objects must be found by traversal of associations.
Martin Fowler:
Aggregates are the basic element of transfer of data storage - you request to load or save whole aggregates.
Aggregate Root
2) How to set a Id. It's a good idea to use immutable properties. public long Id { get; private set; }, lets think we are doing things correctly when we use immutable id. Now lets go ahead and found possible ways for setting Id properly.
set id from the class method. Looks confusing to set id for existing entity (aggregate root). I don't suggest to use this option.
set id from constructor. Why not? You set the Id during the creation of the entity (aggregate root). From Evans DDD:
A public constructor must follow the same rules as a FACTORY: It must be an atomic operation that satisfies all invariants of the created object.
factory. From Evans DDD:
Complex assemblies, especially of AGGREGATES, call for FACTORIES
set id during deserialisation. Is clear and simple way. I would chose this one. I would store Id and other data together (it's common practise). GetById(long id); returns Person which already had Id setted during deserialisation.
Given this kind of context:
public class FooContext : DbContext
{
public FooContext(DbContextOptions<FooContext> opts) : base(opts)
{ }
public DbSet<Bar> Bars { get; set; }
}
I can get to a Bar in two ways:
fooContext.Bars.Add(new Bar()); // Approach 1
or
fooContext.Set<Bar>().Add(new Bar()); // Approach 2
What is the difference between the two approaches?
I've tried to answer my own question by:
Inspecting the intellisense for both (only tells me that Set<T>() also creates a DbSet<T>)
Googling for "EF Core Set vs property" but that doesn't seem to be the 'right' query
Google for DbSet<T> specifically on the docs urls but no relevant results here either it seems
Reading the intro of the DbSet<T> docs which just suggests that you can get a set through either of the two methods (not if there is or isn't a difference)
Read the Set<T>() docs which has no relevant info
But I could not find any good explanation about which of the two is used for which purpose. What is the difference? Or perhaps more importantly: where and how should I be able to find this in the docs?
They do exactly the same thing. The real question is when will you use one over the other.
You use DbSet when you know the type of entity you want to play with. You simple write the DbContext name then the entity type name and you can create, read, update or delete entries for this entity with the entity methods available. You know what you want and you know where to do it.
You use Set when you don't know the entity type you want to play with. Lets say, you wanted to build a class that does your repository functions for creating, reading, updating and deleting entries for an entity. You want this class to be reusable so that you can just pass a DbContext on it and it will use the same create, read, update and delete methods. You don't know for sure what DbContext it will be used on or what DbSet the DbContext will have. Here's when you use generics so that your class can be used by any DbContext for any DbSet.
Here's an example of a class you can use for creating any entity on any DbSet in any DbContext
public class Repository<TDbContext> where TDbContext : DbContext
{
private TDbContext _context { get; }
public Repository(TDbContext context)
{
_context = context;
}
public TEntity Create<TEntity>(TEntity entity) where TEntity : class
{
if(entity != null)
{
var dataSet = _context.Set<TEntity>();
if(entity is IEnumerable)
{
dataSet.AddRange(entity);
}
else
{
dataSet.Add(entity);
}
_context.SaveChanges();
}
return entity;
}
}
And this is how to use it.
var dbContext01 = new DbContext01();
var dbContext02 = new DbContext02();
var repository01 = new Repository<DbContext01>(dbContext01);
var repository02 = new Repository<DbContext02>(dbContext02);
repository01.Create(new EntityOnDbContext01 {
Property01A = "String",
Property01B = "String"
});
repository02.Create(new EntityOnDbContext02 {
Property02A = 12345,
Property02B = 12345
});
Here's a link if you want to know more about generics. Its super awesome.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/
Unfortunately currently you won't find explanation in the official documentation, mainly because all these are functionally equivalent.
First, the generic methods of DbConext like Add<TEntity>, Remove<TEntity>, Attach<TEntity> etc. a fully equivalent of the corresponding DbSet<TEntity> methods (actually currently they are the implementation of the later, i.e. DbSet methods simply call the corresponding DbContext generic method). Which one you use is just a matter of taste.
Second, DbSet<TEntity> property and Set<TEntity> method are functionally equivalent, but do have some non functional differences.
The DbSet properties are populated once at the context creation, while Set method always performs a lookup, so DbSet property access should be faster than Set method (although not significant).
The important difference is actually the EF Core Including & Excluding Types convention:
By convention, types that are exposed in DbSet properties on your context are included in your model. In addition, types that are mentioned in the OnModelCreating method are also included.
So while you can keep your DbContext without exposed DbSet properties and work just with Set method, if you do so you have to tell explicitly EF Core which are your entity types by adding in OnModelCreating a call to modelBuilder.Entity<TEntity>(); for each entity type (this is what the documentation does mean by types that are mentioned in the OnModelCreating method).
They are the same and actually returns the same DbSet instance.
var options = //...;
using (var ctx = new FooContext(options))
{
// true
bool isSame = ReferenceEquals(ctx.Bars, ctx.Set<Bar>());
}
One use case for not including a DbSet property in your DbContext is when you want to hide an entity type from a consumer. (e.g. an entity that acts as join table for many-to-many relationship). You can then mark the entity as internal class so consumers also can't also access it using Set<>.
Also, if you don't expose a DbSet property, you need to explicitly configure the entity or you'll get the following exception:
//throws System.InvalidOperationException: 'The entity type 'Foo' was not found. Ensure that the entity type has been added to the model.'
ctx.Set<Foo>().Add(new Foo());
We need to add a new functionality in our app, such that for SELECT queries performed by our DbContext on its DbSets, for example when calling MyDbContext.Users, or MyDbContext.Students etc (User and Student inherit from a BaseEntity class, which includes a propery IsActive), we will be able to intercept them, check if the entity is of this base type BaseEntity, and modify the queries in some way, for example add a where clause to check if IsActive is true. I've tried to look into the IDbCommandInterceptor interface but its methods, including the one that is probably relevent to me, ReaderExecuting, intercept all kinds of SELECT statements performed on the database, not just the ones for the MyDbContext's dbsets.
How can I do this in the correct way?
Thanks, ashilon
Interceptors, in this instance, are probably not what you want. They intercept all queries for all DbContexts and you can only modify the SQL directly. This is too dangerous when you want to add a where clause because joins could mess everything up.
If you have a repository or some base class all queries go through, do it there. If you are always doing context.Students.Where(...) then there is a sneaky thing you can do, but not entirely sure if this'll work but I don't see why it wouldn't.
On your DbContext class, I would change all the DbSet property namesd, like Students, to StudentsDbSet. I would then add this property to replace it:
public IQueryable<Student> Students
{
get { return StudentdsDbSet.Where(a => a.IsActive); }
}
All old code would now refer to this property that forces IsActive only records. Then if you needed non-active students, you could do StudentsDbSet.Where(...) and you'd be good to go.
In the data architecture I have to contend with, there are no deletes. Instead, all records have a nullable datetime2 that signals the record has been "disabled". This means that in the cases of direct selections on the entities, I'll always have to add in a check to see if the entity was disabled or not.
So far what I've come up with is just a simple extension method called .Enabled() that gets only the enabled rows. It seems effective so far, but it's also annoying that I have to type that in every case.
Surely someone else has skinned this cat before. Is there a better way to do this with Entity Framework I'm just not privy to?
I suppose you could do something like this
public class MyContext : DbContext
{
public IDbSet<Thing> Things { get; set; }
public IQueryable<Thing> EnabledThings
{
get
{
return Things.Where(t => t.Enabled);
}
}
}
or the same as an extension method (but on the context not the DbSet/queriable).
Personally in practice I actually do exactly as you have in your example and use Things.Enabled().Whatever
I don't know of anything native to Entity Framework. But normally when I run into this, I create an "repository" layer that I run most database transactions through. Then I create methods like GetAll() which returns all items with appropriate where statement in place to hide "deleted" items.
I'm not well versed in domain driven design and I've recently started created a domain model for a project. I still haven't decided on an ORM (though I will likely go with NHibernate) and I am currently trying to ensure that my Value Objects should be just that.
I have a few VOs that have almost no behavior other than to encapsulate "like" terms, for instance:
public class Referral {
public Case Case { get; set; } // this is the a reference to the aggregate root
public ReferralType ReferralType { get; set; } // this is an enum
public string ReferralTypeOther { get; set; }
} // etc, etc.
This particular class has a reference to "Case" which is two levels up, so if say I were going to access a Referral I could go: case.social.referral (Case, Social and Referral are all classes, there is a single Social inside a Case and there is a single Referral inside a Social). Now that I am looking at it as I type it, I don't think I need a Case in the Referral since it will be accessible through the Social entity, correct?
Now, there is no doubt in my mind this is something that should be a VO, and the method I plan to use to persist this to the database is to either have NHibernate assign it a surrogate identifier (which I am still not too clear on, if anyone could please elaborate on that too it would help me out, since I don't know if the surrogate identifier requires that I have an Id in my VO already or if it can operate without one) and/or a protected Id property that would not be exposed outside the Referral class (for the sole purpose of persisting to the DB).
Now on to my title question: Should a VO have a collection, (in my case a List) inside it? I can only think of this as a one-to-many relationship in the database but since there is no identity it didn't seem adequate to make the class an entity. Below is the code:
public class LivingSituation {
private IList<AdultAtHome> AdultsAtHome { get; set; }
public ResidingWith CurrentlyResidingWith { get; set } // this is an enum
} // etc, etc.
This class currently doesn't have an Id and the AdultsAtHome class just has intrinsic types (string, int). So I am not sure if this should be an entity or if it can remain as a VO and I just need to configure my ORM to use a 1:m relationship for this using their own tables and a private/protected Id field so that the ORM can persist to the DB.
Also, should I go with normalized tables for each of my classes, or not? I think I would only need to use a table per class when there is a possibility of having multiple instances of the class assigned to an entity or value object and/or there is the possibility of having collections 1:m relationships with some of those objects. I have no problem with using a single table for certain value objects that have intrinsic types but with nested types I think it would be advantageous to use normalized tables. Any suggestions on this as well?
Sorry for being so verbose with the multiple questions:
1) Do I need a surrogate identifier (with say NHibernate) for my value objects?
2) If #1 is yes, then does this need to be private/protected so that my value object "remains" a value object in concept?
3) Can a value object have other value objects (in say, a List) or would that constitute an entity? (I think the answer to this is no, but I'd prefer to be sure before I proceed further.)
4) Do I need a reference to the aggregate root from a value object that is a few levels down from the aggregate root? (I don't think I do, this is likely an oversight on my part when writing the model, anyone agree?)
5) Is it OK to use normalized tables for certain things (like nested types and/or types with collections as properties which would need their own tables anyway for the 1:m relationship) while having the ORM do the mapping for the simpler value objects to the same table that belongs to my entity?
Thanks again.
Take a look at the answers to related questions here and here
1) Yes - If you're storing VOs in their own table
2) If you can use a private/protected ID property, then great. Alternatively, you might use explicit interfaces to 'hide' the ID property.
But, reading into your question, are you suggesting that developers who see an ID property will automatically assume the object is an entity? If so, they need (re)training.
3) Yes it can, but with the following restrictions:
It should be quite rare
It should only reference other VOs
Also, consider this: VOs shouldn't stick around. Would it be easy/efficient to re-create the entire VO every time it's needed? If not, make it an Entity.
4) Depends on how you want to implement your Aggregate Locking. If you want to use Ayende's solution, the answer is yes. Otherwise, you would need a mechanism to traverse the object graph back to the Aggregate Root.
5) Yes. Don't forget that DDD is Persistence Ignorant (in an ideal world!).
However...
I believe Referral should be an Entity. Imagine these conversations:
Conversation 1:
Tom: "Hey Joe! Can you give me David Jone's referral?"
Joe: "Which one?"
Tom: "Sorry, I mean Referral No.123"
Conversation 2:
Tom: "Hey Joe! Can you give me David Jone's referral?"
Joe: "Which one?"
Tom: "I don't care - just give me any"
Conversation 1 suggests that Referral is an Entity, whereas conversation 2 suggests it's a VO.
One more thing: Does Referral.ReferralType change during it's lifetime (there's another hint that it should be an Entity)? If it doesn't change, consider using polyporphism and let NH handle it.
Hope that helps!