I have the class PERTActivity_T which inherits from MetadataTyper class, so as other tables.
Opposite to its siblings PERTActivity_T already has the properties:
[ForeignKey("BSContext")]
public int? BSContextId { get; set; }
public BSContext BSContext { get; set; }
But now I need all tables that inherit from MetadataTyper to have those properties as well. So I removed them from PERTActivity_T and included them in MetadataTyper but when I generate a migration it removes the relationship from BSContextId to BSContext and adds an extra field called BSContext_Id (with an underscore) and adds the foreign key to it.
This happens only for PERTActivity_T the other tables that inherit from MetadataTyper migrate fine.
At first I tried to edit the migration manually to remove all changes to PERTActivity_T since it is already created the way I need it, it all seamed to work fine but when I use a mapper to edit PERTActivity_T or any table related to it EF throws an error saying BSContext_Id is unknown.
How can I make EF understand that PERTActivity_T doesn't need any additional changes?
Turns out that my problem was that BSContext already contained a navigation property to PERTActivity_T and then I duplicated it.
All I had to do was remove the duplicated navigation property and the migration generated fine.
Related
Using Microsoft.EntityFrameworkCore version 5.0.7 and Npgsql.EntityFrameworkCore.PostgreSQL version 5.0.7, I'm currently stuck trying to remove a relationship and have that change stored. Assume two models:
public class Banana {
public int Id { get; set; }
public Consumer? Consumer { get; set; }
}
public class Consumer {
public int Id { get; set; }
}
I'm able to assign a consumer just fine using
myBanana.Consumer = dbContext.Consumers.First(row => row.Id == 1);
dbContext.Update(myBanana);
dbContext.SaveChanges();
and that works just fine - the database is updated accordingly. However, once that is stored, trying to remove the reference again using
myBanana.Consumer = null;
dbContext.Update(myBanana);
dbContext.SaveChanges();
fails. After saving, the old value is still in the database, not null as I would expect. Interestingly, other changes to the banana are saved just fine.
I'm not sure if I'm hitting a weird issue with Nullables, or if I'm just missing something, so I'd appreciate some hints.
If you want to continue using auto-generated foreign key properties, you have to make sure that the navigations are loaded. If they are lazy-loaded by default and you don't manually load them, the property will already be null before you try to assign null, so EF can't observe a change.
Loading the navigation with .Include(banana => banana.Consumer) works, and so does loading it via dbContext.Entry(myBanana).Reference(banana => banana.Consumer).Load(). After the relevant navigation items are loaded, myBanana.Consumer = null from the example in the question works as expected.
If you have a non-tracking entity to work with (for example because it was generated by Model Binding), you can either get a tracking entity, or change the value of the auto-generated foreign key shadow property directly:
dbContext.Entry(myBanana).Property("ConsumerId").CurrentValue = null;
which also works. This may be a little bit less polished since you depend on a string as the field name to be correct, but it can be a valid option depending on the circumstances.
I've inherited a database and I need to insert data using EF6. I get the error:
DbUpdateException: Unable to determine the principal end of the 'POSModel.FK_KitMemberTaxRaw_KitMemberSaleReturnRaw_KitMemberSaleReturnRowId' relationship. Multiple added entities may have the same primary key.
I deserialize XML to the POCO objects using DataContractSerializer.
I'm using the object references from the xml document's structure to define the relationships. The POCO objects are generated using a t4 script provided from the NuGet package (which does not work with either deserializer well at all!)
I've decorated KitMemberTaxRaw like so:
[ForeignKey("KitMemberSaleReturnRaw")]
public virtual KitMemberSaleReturnRaw KitMemberSaleReturnRaw { get; set; }
[ForeignKey("KitMemberKitMemberSaleReturnRaw")]
public virtual KitMemberKitMemberSaleReturnRaw KitMemberKitMemberSaleReturnRaw { get; set; }
The KitMemberTaxRaw table may be joined to either table KitMemberKitMemberSaleReturnRaw or KitMemberSaleReturnRaw (but not both).
How does EF determine 'the principal end of the relationship'?
The issue turned out to be EF6 was not able to automatically understand a table with links to a parent and an optional grand parent. The navigation properties generated by the Microsoft provided template were correct but insufficient.
To cure the issue I manually created temporary primary keys for the relationship it did not understand.
Note: The DataContractSerializer class I used created the POCO objects creates an array for the instantiated navigation properties. I had to change the template to generate IList<> properties instead of ICollection<>. At run time there were errors because the array could not be dynamically resized.
The entity model I'm working on is structured with inheritence as per:
public abstract class Line {}
public class WooLine : Line{
public bool wooProperty{ get; set; }
}
public class BooLine : Line
These are both stored in the database in the table Line. And in the database the column wooProperty is NOT NULL and default value (0).
These are maintained in a web app written with Knockout & Breeze. When working with BooLine trying to create a new entity, it throws an exception that I can't insert NULL into column wooProperty.
I set up a profile to trace the query, and it appears that since it's mapped to the Line table, during the Insert EntityFramework reads up all the properties and tries to actually insert NULL into the wooProperty, since it's not present in the Boo model. I'm moderately upset that EF is actively trying to insert NULL to a property I'm not working with...
Anyway. I can't move the wooProperty to the Line model - it belongs in the WooLine model. I'm hoping to solve it by either modifying the metadata in Breeze or forcing the wooProperty onto the saveChanges data. But I can't get breeze to recognize the property in the metadata. I've tried to run
metadataStore.registerEntityTypeCtor(
'BooLine', function () {
this.wooProperty = false;
});
Which almost works - but Breeze maps it as __unmapped value and as such isn't recognized after being recieved by EntityFramework.
I also started playing around with overriding the EFContextProvider and overriding BeforeSaveEntity. Entity is ReadOnly of type BooLine, and I can clearly see WooProperty in the UnmappedProperties, but I have no idea where to go from there... Any ideas?
TLDR in a way; Want to 'trick' entity framework into thinking an unmapped value is mapped when creating an entity.
To summarize my comments I would recommend one of the following:
make your model use TPT inheritance so there is no wooProperty column in the Line table, but in the inherited WooLine table
change your wooProperty column to be nullable and mark the wooProperty property in your entity class as [Required] and let EF take care of reading only "valid WooLines" - this should work if there is also a valid discriminator column for EF to use
I need a view-model in my ASP.NET MVC 5 project, but when I added one to the models folder, a new entity was added to the database and I was forced to add a migration and update my database. I do not want this to happen, as it is a view-model I am adding and not a model I need to persist back to the database. I want to scaffold some of the controller and views so I have added a primary key to the class. I did not add the newly created view-model to my DbContext class.
ViewModel:
public class RolesViewModel
{
public int RolesViewModelId { get; set; }
public string Role { get; set; }
}
Is there a way to create a view-model that doesn't automatically get added to the DbContext class, and therefore cause the data model to change?
Many thanks,
Jason.
Whether you call it a view model, an entity, etc. it's just semantics. Everything is just a class, and the context it's used in determines what you refer to it as. In the case of entities, that's adding a reference either explicitly or implicitly in your DbContext, and that's the only way you'll end up with something added to your database. I emphasized the "or implicitly* part because if any class that is referenced in the your DbContext, or any class connected to any class referenced there, also references your "view model", it will end up in your database. Entity Framework will automatically follow your class hierarchies and create tables for all relationships, even if you do not reference a particular class in those hierarchies directly in your DbContext.
In your case Scaffolding will add the following code below in your appContext Class
public DbSet<RolesViewModel> RolesViewModel { get; set; }
You can still use scaffolding if you wish, however remember to remove that entry and no table will be created by code first. It will keep your database clean.
I have a File entity and a User entity. The File entity has a 1:1 relationship with the User entity through a property called LastChangeUser (this records the user who last changed the file). There's also a field within the File entity named LastChangeUserId, which is the actual FK relationship. The relationship is one-way: the User entity has no navigation property leading back to the File entity.
class File
{
public int Id { get; set; }
public int? LastChangeUserId { get; set; }
public virtual User LastChangeUser { get; set; }
}
class User
{
public int Id { get; set; }
}
When a File is changed, I need to set the LastChangeUser for the File. I only have the ID of the user to hand, not the complete User object. So, I'm doing this:
file.LastChangeUser = null;
file.LastChangeUserId = userId;
This seems to work on creating the file, when the File object is newly-created (a POCO which is then added to the entity collection).
However, it does not work when updating the file, when the File object is an existing object retrieved (as a proxy) from the DB.
In the latter case, I end up with a NULL in the DB for the LastChangeUserId field. (After a call to SaveChanges, the object has null in both the LastChangeUser and LastChangeUserId fields).
Maybe I'm just doing the wrong thing here? What's the right way? Do I really need to go get the User object in order to set the LastChangeUser property?
The reason #kevin_fitz solution works here is due to the way changetracking and validation in EF work. The default behavior for change tracking in EF is a method called snapshot tracking which essentially clones an initial state of every entity when its first loaded. When you go to save changes in EF the original snapshot for each entity is compared to the current state object (the one which you are modifying) and any differences are persisted to the database.
Along side this EF also performs pre-submit validation on entities (which can FYI be disabled).
In your case you have made two changes to the model which will be detected by the snapshot tracker on save (and they actually conflict). The tracker however will try and process both of these through the validation rules which will pickup that this is a required relationship and cant be set to null. This is why you are seeing this error and why removing the null update fixes your problem.
On a sidenote, you actually only need to update either the object or the key on a navigation property to trigger that database relationship to be updated. For more details on how navigation properties work in EF codefirst checkout my article here: http://blog.staticvoid.co.nz/2012/07/entity-framework-navigation-property.html