Order of mapping properties when loading from database - entity-framework

Let's say I have this code:
public bool Important { get; set; }
private bool _dependsOnImportant;
public bool DependsOnImportant
{
get;
set
{
if (value && !Important)
throw new InvalidOperationException();
_dependsOnImportant = value;
}
}
I want to protect setting DependsOnImportant to true, so that it is only possible when Important is true. This is POCO class. According to: Entity Framework 4.1 - Code first. Doesn't EF override my virtual members? my setter will be used by EF when loading object from database.
How can I be sure that Important has already been set with data from database when DependsOnImportant setter is called? Will EF be smart enough to detect that one setter is using another and load that data first? I don't think so. I think a new object is created with default constructor (setting all fields to default values) and then setters are invoked to populate the object.
So my questions are: Can we determine the order in which setters are called? But most importantly: Do we want to have that knowledge?

Related

EntityFramework6 "FOREIGN KEY constraint failed" on nullable foreign key

I have my entity defined like this:
public class Entity : BaseModel // Has the already ID defined
{
private int? companyId;
public Company? Company { get; set; }
public int? CompanyId {
get => this.companyId == 0 ? null : this.companyId; // I tried this for debugging purposes to force this value to "null" -> made no difference
set => this.companyId = value;
}
}
public class Company : BaseModel // Has the already ID defined
{
public IEnumerable<Entity> Entities { get; set; } = new List<Entity>();
}
Anyway, if I set the CompanyId to null, my DB throws an exception with the message: "FOREIGN KEY constraint failed". If the CompanyId is set to, e.g. 123, the relationship is resolved accordingly.
I mean, it makes sense, that EF cannot find null in my DB, but how do I want to set an optional value otherwise? I am using code first annotations only, hence my OnModelCreating of my context is completely empty.
How are you loading the entities in the first place? Are you loading an Entity by ID and trying to dis-associate it from a company, or have you loaded a company with it's entities and trying to remove one association?
Normally when working with relations where you have navigation properties, you want to de-associate them (or delete them) via the navigation properties, not the FK properties. For instance if loading a company and wanting to de-associate one of the entities you should eager-load the entities then remove the desired one from the collection:
var company = _context.Companies.Include(c => c.Entitites).Single(c => c.Id == companyId);
var entityToRemove = company.Entities.SingleOrDefault(e => e.Id == entityId);
if(entityToRemove != null)
company.Entities.Remove(entityToRemove);
_context.SaveChanges();
Provided that the relationship between Company and Entity is set up properly as an optional HasMany then provided these proxies are loaded, EF should work out to set the entityToRemove's FK to null.
If you want to do it from the Entity side:
var entityToRemove = _context.Entities.Include(e => e.Company).Single(e => e.Id == entityId);
entityToRemove.Company = null;
_context.SaveChanges();
That too should de-associate the entities. If these don't work then it's possible that your mapping is set up for a required relationship, though I am pulling this from memory so I might need to fire up an example to verify. :) You also should be checking for any code that might set that CompanyId to 0 when attempting to remove one, whether that might be happening due to some mapping or deserialization. Weird behaviour like that can occur when entities are passed around in a detached state or deserialized into controller methods. (which should be avoided)
Update: Code like this can be very dangerous and lead to unexpected problems like what you are encountering:
public virtual async Task<bool> Update(TModel entity)
{
Context.Update(entity);
await Context.SaveChangesAsync();
return true;
}
Update() is typically used for detached entities, and it will automatically treat all values in the entity as Modified. If model was already an entity tracked by the Context (and the context is set up for change tracking) then it is pretty much unnecessary. However, something in the calling chain or wherever has constructed the model (i.e. Entity) has set the nullable FK to 0 instead of #null. This could have been deserialized from a Form etc. in a view and sent to a Controller as an integer value based on a default for a removed selection. Ideally entity classes should not be used for this form of data transfer from view to controller or the like, instead using a POCO view model or DTO. To correct the behaviour as your code currently is, you could try the following:
public async Task<bool> UpdateEntity(Entity entity)
{
var dbEntity = Context.Set<Entity>().Include(x => x.Customer).Single(x => x.Id == entityId);
if (!Object.ReferenceEquals(entity, dbEntity))
{ // entity is a detached representation so copy values across to dbEntity.
// TODO: copy values from entity to dbEntity
if(!entity.CustomerId.HasValue || entity.CustomerId.Value == 0)
dbEntity.Customer = null;
}
await Context.SaveChangesAsync();
return true;
}
In this case we load the entity from the DbContext. If this method was called with an entity tracked by the DbContext, the dbEntity would be the same reference as entity. In this case with change tracking the Customer/CustomerId reference should have been removed. We don't need to set entity state or call Update. SaveChanges should persist the change. If instead the entity was a detached copy deserialized, (likely the case based on that 0 value) the reference would be different. In this case, the allowed values in the modified entity should be copied across to dbEntity, then we can inspect the CustomerId in that detached entity for #null or 0, and if so, remove the Customer reference from dbEntity before saving.
The caveats here are:
This won't work as a pure Generic implementation. To update an "Entity" class we need knowledge of these relationships like Customer so this data service, repository, or what-have-you implementation needs to be concrete and non-generic. It can extend a Generic base class for common functionality but we cannot rely on a purely Generic solution. (Generic methods work where implementation is identical across supported classes.)
This also means removing that attempt at trying to handle Zero in the Entity class. It should just be:
public class Entity : BaseModel
{
public Company? Company { get; set; }
[ForeignKey("Company")]
public int? CompanyId { get; set; }
// ...
}
Marking Foreign Keys explicitly is a good practice to avoid surprises when you eventually find yourself needing to break conventions that EF accommodates in simple scenarios.

How can I prevent the default value of DateTimeOffset from being inserted?

I'm using Entity Framework Core 3.1.7 and created an entity called Event, which I set up like this:
public class Event
{
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; }
}
The entity configuration looks like this:
builder.Property(e => e.FirstOccurred)
.IsRequired();
I use my dbContext to persist the entity like this:
await dbContext.Events.AddAsync(new Event());
In this scenario, I was incorrectly expecting that an exception would be thrown at the Database level because the value can't be null.
What actually happens is: the entity is happily persisted with FirstOccurred set to 0001-01-01T00:00:00+00:00
This makes sense, because the default value of DateTimeOffset is used.
Now my question: How could I improve my design to prevent this default value from being inserted?
Some ideas I had already:
Leave the above code as is, but make sure that wherever the entity is used, I'm setting the values correctly. Downside: no guarantee that this will be applied consistently in a team over time.
Make DateTimeOffset nullable, which in the above AddAsync() call would actually cause an SQL exception. Downside: At first glance, DateTimeOffset? FirstOccurred might be confusing because the actual DB constraints don't allow null
Remove set; for FirstOccurred and create a constructor that requires this property to be set, e.g. new Event(DateTimeOffset.Now)
I think you're on the right track with your last idea.
Remove set; for FirstOccurred and create a constructor that requires this property to be set, e.g. new Event(DateTimeOffset.Now)
It doesn't make sense to track an Event without the timestamp and it certainly doesn't make sense to use the default value for the timestamp.
Changing your model to require a value for the timestamp ensures that you are not writing default data to the record and prevents confusion from seeing a nullable model field when the corresponding table column is non-nullable.
public class Event {
public Event (DateTimeOffset firstOccurred) { FirstOcurred = firstOcurred; }
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; }
}
Just a note from the documentation, don't remove the set; accessor, just mark it private if you don't want the value to change after construction.
Once properties are being set via the constructor it can make sense to make some of them read-only. EF Core supports this, but there are some things to look out for:
Properties without setters are not mapped by convention. (Doing so tends to map properties that should not be mapped, such as computed properties.)
Using automatically generated key values requires a key property that is read-write, since the key value needs to be set by the key generator when inserting new entities.
An easy way to avoid these things is to use private setters.
Of course, you could also maintain the flexibility of the parameter-less constructor by overriding the default value for the property.
public class Event {
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; } = DateTimeOffset.Now;
}

Best practice for setting default values for model properties in Domain Driven Design?

What's the best way to set default properties for new entities in DDD? Also, what's the best way to set default states for complex properties (eg. collections)?
My feeling is that default values should be in the models themselves as they are a form of business rule ("by default, we want X's to be Y & Z"), and the domain represents the business. With this approach, maybe a static "GetNew()" method on the model itself would work:
public class Person {
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public bool IsAlive { get; set; }
public List Limbs { get; set; }
public static Person GetNew() {
return new Person() {
IsAlive = true,
Limbs = new List() { RightArm, LeftArm, RightLeg, LeftLeg }
}
}
}
Unfortunately in our case, we need the collection property to be set to all members of another list, and as this model is decoupled from its Repository/DbContext it doesn't have any way of loading them all.
Crappy solution would be to pass as parameter :
public static Person GetNew(List<Limb> allLimbs) {
return new Person() {
IsAlive = true,
Limbs = allLimbs
}
}
Alternatively is there some better way of setting default values for simple & complex model properties?
This is an instance of the factory pattern in DDD. It can either be a dedicated class, such as PersonFactory, or a static method, as in your example. I prefer the static method because I see no need to create a whole new class.
As far as initializing the collection, the GetNew method with the collection as a parameter is something I would go with. It states an important constraint - to create a new person entity you need that collection. The collection instance would be provided by an application service hosting the specific use case where it is needed. More generally, default values could be stored in the database, in which case the application service would call out to a repository to obtain the required values.
Take a look at the Static Builder in Joshua Bloch's Effective Java (Second Edition). In there, you have a static builder class and you chain calls to set properties before construction so it solves the problem of either having a constructor that takes a ton of arguments or having to put setters on every property (in which case, you effectively have a Struct).

Why is my Entity Framework Code First proxy collection null and why can't I set it?

I am using DBContext and have two classes whose properties are all virtual. I can see in the debugger that I am getting a proxy object when I query the context. However, a collection property is still null when I try to add to it. I thought that the proxy would ensure that collection is initialized.
Because my Poco object can be used outside of its data context, I added a check for the collection being null in the constructor and create it if necessary:
public class DanceStyle
{
public DanceStyle()
{
if (DanceEvents == null)
{
DanceEvents = new Collection<DanceEvent>();
}
}
...
public virtual ICollection<DanceEvent> DanceEvents { get; set; }
}
That works outside the data context but if I retrieve an object using a query, although the test is true, when I try to set it, I get following exception: 'The property 'DanceEvents' on type 'DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94' cannot be set because the collection is already set to an EntityCollection.'
I can see it is null and I cannot add to it, but neither can I set it to a collection because the proxy says it is already set. Therefore I cannot use it. I'm confused.
Here is the DanceEvent class:
public class DanceEvent
{
public DanceEvent()
{
if (DanceStyles == null)
{
DanceStyles = new Collection<DanceStyle>();
}
}
...
public virtual ICollection<DanceStyle> DanceStyles { get; set; }
}
I have omitted the other value-type properties from the code above. I have no other mappings for those classes in the context class.
As you correctly observed in the answer to your own question, removing the "virtual" keyword from the collection properties works around the problem, by preventing the Entity Framework from creating a change tracking proxy. However, this is not a solution for many people, because change tracking proxies can be really convenient and can help prevent issues when you forget to detect changes at the right places in your code.
A better approach would be to modify your POCO classes, so that they instantiate the collection properties in their get accessor, rather than in the constructor. Here's your POCO class, modified to allow change tracking proxy creation:
public class DanceEvent
{
private ICollection<DanceStyle> _danceStyles;
public virtual ICollection<DanceStyle> DanceStyles
{
get { return _danceStyles ?? (_danceStyles = new Collection<DanceStyle>()); }
protected set { _danceStyles = value; }
}
}
In the above code the collection property is no longer automatic, but rather has a backing field. It's better if you leave the setter protected, preventing any code (other than the proxy) from subsequently modifying these properties. You will notice that the constructor was no longer necessary and was removed.
I found the solution to this problem here: Code First adding to collections? How to use Code First with repositories?
I removed 'virtual' from all properties except collections and lazy loaded objects, that is, all native types.
But I still don't understand how you can end up with the situation where you have a null collection that you cannot use and have no way to set it to a valid collection.
I also found this answer from Rowan Miller on an MSDN forum
Hi,
If you make all your properties virtual then EF will generate proxy classes at runtime that derives from your POCO classed, these proxies allow EF to find out about changes in real time rather than having to capture the original values of your object and then scan for changes when you save (this is obviously has performance and memory usage benefits but the difference will be negligible unless you have a large number of entities loaded into memory). These are known as 'change tracking proxies', if you make your navigation properties virtual then a proxy is still generated but it is much simpler and just includes some logic to perform lazy loading when you access a navigation property.
Because your original code was generating change tracking proxies, EF was replacing your collection property with a special collection type to help it find out about changes. Because you try and set the collection back to a simple list in the constructor you are getting the exception.
Unless you are seeing performance issues I would follow Terrence's suggestion and just remove 'virtual' from your non-navigation properties.
~Rowan
So it appears that I only have the problem with a full 'change tracking proxy' if all my properties are virtual. But given that, why can I still not use the virtual property on the change tracking proxy? This code blows up on line three because ds2.DanceEvents is null and cannot be set in the constructor:
DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single();
DanceEvent evt = CreateDanceEvent();
ds2.DanceEvents.Add(evt);
I'm still confused, even though my code is now working because of the fix above.
Old question...
Poco class:
public partial class MyPOCO
{
public MyPOCO()
{
this.MyPocoSub = new HashSet<MyPocoSub>();
}
//VIRTUAL
public virtual ICollection<MyPocoSub> MyPocoSub { get; set; }
}
and proxy code:
public override ICollection<MyPocoSubSet> MyPocoSubSets
{
get
{
ICollection<MyPocoSubSet> myPocoSubSets = base.MyPocoSubSets;
if (!this.ef_proxy_interceptorForMyPocoSubSets(this, myPocoSubSets))
{
return base.MyPocoSubSets;
}
return myPocoSubSets;
}
set
{
if (value != this.RelationshipManager.GetRelatedEnd("WindowsFormsApplication.Models.MyPocoSubSet_MyPOCO", "MyPocoSubSet_MyPOCO_Source"))
{
// EXCEPTION
throw new InvalidOperationException("The property 'MyPocoSubSets' on type 'MyPOCO_A78FCE6C6A890855C68B368B750864E3136B589F9023C7B1D90BF7C83FD291AC' cannot be set because the collection is already set to an EntityCollection.");
}
base.MyPocoSubSets = value;
}
}
As you can see that exception raised in proxy class in ExtityFramework 5. This means that behavior still exist.

Entity Framework 4 , Custom Properties . Add some traitement

I have a class Address Generated by entity Framework.
I Have an propertie AddressID in this class.
I Would like to be able to add some treatement for this prop in the set process.
EX :
public partial class Address
{
public bool _AddressID;
public bool AddressID{get return AddressID;}
set{
if(value == -1) _AddressID = null;
}
}
Thanks
Of course you can't redefine your AddressID in order to put your custom logic in its setter, as you'll get compiler error:
The type Address already contains a definition for 'AddressID'
But no worries, if you take a look at the EF generated code for your EntityObject (let's assume its name is Address) you'll see that every scalar property of generated Address class has its own version of OnPropertyChanging and OnPropertyChanged method. For example, OnAddressIDChanging and OnAddressIDChanged in this case.
As you can see below, there is no default implementation for these two methods, only a declaration. This perfectly provides you the opportunity to execute custom logic
as the property is about to change (PropertyChanging) as well as just after the property
value has changed (PropertyChanged).
// From the designer code for Address class:
partial void OnAddressIDChanging(global::System.Int32 value);
partial void OnAddressIDChanged();
This is how your Entity Model designer code already is look like (hypotetically):
public global::System.Int32 AddressID {
get {
return _AddressID;
}
set {
if (_AddressID != value) {
// OnPropertyChanging method get called here:
OnAddressIDChanging(value);
ReportPropertyChanging("AddressID");
_AddressID = StructuralObject.SetValidValue(value);
ReportPropertyChanged("AddressID");
// OnPropertyChanged get called here:
OnAddressIDChanged();
}
}
}
So all you need to do in order to hook up your custom code is:
public partial class Address {
partial void OnAddressIDChanged() {
if(AddressID == -1) {
AddressID = 0;
}
}
}
By the way, about other posted answers - with all due respect to them - if you want this solution for a production application then you cannot use "Code First" since it merely is a CTP as for now and will be part of the next release for EF, so it cannot be an option.
About customizing default code generation, while this is indeed possible since in VS 2010, Entity Framework itself also uses T4 for designer code generation and we can take advantage of it by changing the T4, But it is an option only if you want to fundamentally change how the entity classes are generated in general and you cannot use it for customizing a setter logic for a specific entity.
Code First in EF4 is an option - it allows you to fully control all of the code. However, another option is to customize the EF4 T4 templates that ship with EF4. If you have certain patterns in your code that you consistently use, this would be a good approach. You can read more about how to customize the templates here: Customizing Entity Classes in VS2010