I'm using generated POCO classes and Entity Framework.
In order to make the code less complex I'm trying to remove all navigation properties from the code while still keeping the Foreign Key constraints in the database (navigation properties do more harm than good for us).
If I remove them manually from the POCO-classes I get the following error
The entity type UserEntity is not part of the model for the current context
If I try to remove them from the .edmx-file I get the following error:
Error 3 Error 3015: Problem in mapping fragments starting at lines 479, 562:Foreign key constraint 'fk_StorageContracts_User1' from table StorageContract (OwnerUserID) to table User (ID):: Insufficient mapping: Foreign key must be mapped to some AssociationSet or EntitySets participating in a foreign key association on the conceptual side.
Is there any way to remove navigation properties from POCO-classes without removing the corresponding FK?
I know this is old, but, since there is still no answer, I thought I'd give it a try:
I am still working in EF 4.0, but, following the example to which you referred, you have an xxxModel.tt. If you are willing to tweak that, you can find where it generates the Navigation Properties and change them to be simple auto-properties. I had a similar project where I generated them like this:
public List<NavDataX> NavDataXs
{
get; set;
}
Now, they are still there, but they are null until you explicitly set them. Doing it this way, I did not mess with the EDMX and did not encounter the two errors you mentioned.
Related
I am using EFCore 5.0.0.
When I AddAsync (person); I should be getting a temporary ID, and I use this ID to add the PersonId for School (shown in code below). FInally, I will SaveChangesAsync() where everything will be saved. However, the PersonId is set to 0. I want to get the temporary ID stored instead. How can I do this.
await _dbContext.AddAsync(person);
School school = mySchool;
school.PersonId = person.Id;
await _dbContext.AddAsync(school);
await _dbContext.SaveChangesAsync();
Note: There are many SO post that talks about the temporary ID, but none is related to this post.
Currently accepted answer is valid, but technically incorrect. Assigning navigation property is valid approach, but not mandatory. It's even perfectly valid to not have navigation property at all. As well as explicit FK property. But there is always at least shadow FK property which can be used to setup/maintain the relationship.
So the temporary key concept is part of the EF Core from the very beginning. However EF Core 3.0 introduced a breaking change - Temporary key values are no longer set onto entity instances. The link contains an explanation of the old and new behaviors, the reason and possible solutions:
Applications that assign primary key values onto foreign keys to form associations between entities may depend on the old behavior if the primary keys are store-generated and belong to entities in the Added state. This can be avoided by:
Not using store-generated keys.
Setting navigation properties to form relationships instead of setting foreign key values.
Obtain the actual temporary key values from the entity's tracking information. For example, context.Entry(blog).Property(e => e.Id).CurrentValue will return the temporary value even though blog.Id itself hasn't been set.
Bullet #1 makes no sense, Bullet #2 is what is suggested in the other answer. Bullet #3 is the direct answer/solution to your question.
And applying it to your example requires just changing
school.PersonId = person.Id;
to
school.PersonId = _contexy.Entry(person).Property(e => e.Id).CurrentValue;
Of course when you have navigation property and the related entity instance, it's better to use it and let EF Core do its magic. The temporary key is really useful when you don't have navigation property, or you don't have related entity instance and know the key, but don't want to do roundtrip to load it from database (and using fake stub entity instance can lead to unexpected side effects/behaviors). It works well with both explicit and shadow FK properties.
I've never seen linking entities in EF Core using the temporary id.
Typically what you would do is assign the entity and let EF sort out the ids and relationships.
i.e. in this instance, the School will be linked to the Person.
await _dbContext.AddAsync(person);
School school = mySchool;
school.Person = person;
await _dbContext.AddAsync(school);
await _dbContext.SaveChangesAsync();
I have a POCO class (OPERATION) that is used as an Entity Framework entity. This class has a navigation property (OP) and a foreign key into the same related entity (OP_ID).
In a method, I get an OPERATION and on this OPERATION the OP_ID and OP are both null. When I set the OP_ID to a valid value for this foreign key, the OP navigation property remains null. When I explicitly detect changes in the context, the OP navigation property is now assigned with the correct value.
Sample code
public bool UpdateOperation(operationID)
{
IQueryable<OPERATION> operations = from o in base.ctx.OPERATION
select o;
OPERATION operation = operations
.Where(o => o.OPERATION_ID == operationID)
.Include("OP")
.FirstOrDefault();
if (operation != null)
{
operation.OP_ID = opId;
}
// operation.OP is null here
operation.GetContext().ChangeTracker.DetectChanges();
// operation.OP is populated here
}
I have confirmed that the operation is, in fact, a dynamic proxy. For what it's worth, once I detect changes, operation.OP also becomes a dynamic proxy. However, even then, assigning a different value to operation.OP_ID still requires an explicit DetectChanges() call in order to update the value of operation.OP.
Update
In response to the comment from #ErikPhilips, the documentation here seems to imply that this should happen. Specifically:
The following examples show how to use the foreign key properties and navigation properties to associate the related objects. With foreign key associations, you can use either method to change, create, or modify relationships. With independent associations, you cannot use the foreign key property.
By assigning a new value to a foreign key property, as in the following example.
course.DepartmentID = newCourse.DepartmentID;
...
When you change the relationship of the objects attached to the context by using one of the methods described above, Entity Framework needs to keep foreign keys, references, and collections in sync. Entity Framework automatically manages this synchronization (also known as relationship fix-up) for the POCO entities with proxies.
If you are using POCO entities without proxies, you must make sure that the DetectChanges method is called to synchronize the related objects in the context.
Some additional context may be useful, as well. This is a legacy application that used to work directly with an ObjectContext instead of a DbContext, though even then using EF 6. We are now migrating to the DbContext API. This particular code, without any modifications, used to demonstrate the behavior I'm expecting. Specifically, when OP_ID is assigned, I can see in the debugger that the OP property is automatically populated to point to the correct OPERATION.
In the end, I was doing exactly what the documentation described. I was
assigning a new value to a foreign key property.
Yes, Entity Framework does manage this in fix-up. And yes, the documentation does state this.
It turns out, though, that the egg is ultimately on my face. I had checked the classes generated from my T4 template, and seen that all navigation properties were marked virtual. I had not checked thoroughly enough to note that the foreign key properties were not marked virtual, however. It appears that this is the default behavior of the EF-provided T4 template used when working model- or database-first. I've addressed this by changing this line in the CodeStringGenerator.Property() method in the T4 template
Accessibility.ForProperty(edmProperty)
to
AccessibilityAndVirtual(Accessibility.ForProperty(edmProperty))
In the end, as usual, following the documentation (here, the requirements for EF change tracking on POCOs) often results in dependent code behaving as it is documented. Shame on me.
I am working on a simple database and to be specific here is the model generated by database first approach (Visual Studio 2017 Community, Entity Framework 6.2):
Generated Database Model
I'd like the UserMessage table to be able to point to itself with a field named AnswerId, this is a nullable foreign key referencing its primary key. Again, to be specific, here is the part where I create the table:
UserMessage table script
My problem is that when Entity Framework generates the classes based on the existing database everything goes fine except for this particular table, in which EF suggests (I don't know why) that the UserMessage table has a multiplicity of 0..1 - * to itself while it should 1 - 0..1 (because a message may have a direct answer, but not more than 1, though that message, which is the answer, could also have an answer, so it's just like a linked list).
Here is the generated class: UserMessage generated class
To sum up the whole thing: I'd like to know why Entity Framework generates my class the way it does, and how could I make it generate it so that I only have a virtual property pointing to the answer (in case it has one), but not a collection.
Thank you for your answers!
I think what you're seeing is a correct interpretation by EntityFramework.
UserMessage1 represents a collection of all the UserMessages that have references to the parent as their answer. I understand that you probably won't use that collection for anything but it's not wrong that it's there. UserMessage2 seems to be the property you're looking for. Maybe you could rename those properties in the diagram so they're not confusing.
UserMessages1 = MessagesThatReferenceMe
UserMessages2 = the Message that I may or may not reference
I don't see how you can stop EF from generating this collection. I think if you delete the property in the diagram you will have to delete it every time you update the diagram.
Maybe try deleting the 2nd UserMessage navigation property in your model.
I choose Database First:
Here is an example table that is experiencing this issue. As you can see the EntityId column is the Primary Key:
The imported table in the model browser shows that it has the Primary Key:
But the code for the generated class does not have the EntityId column decorated with a Key attribute:
At run time I get this error:
Additional information: One or more validation errors were detected
during model generation: EntityType 'Entity' has no key defined.
Define the key for this EntityType.
Why do I have to manually decorate the EntityId column with the Key Attribtue? Shouldnt EntityFramework take care of all that considering it is Database first?
Typically speaking I have experience with EF4 through EF 6.1.3 and a teeny bit with Entity Core (was EF7 and then MS fun with naming). Typically if you are doing database first you do not get an adornment from your t4 template. I just looked at mine just now and I have no adornment for the key, just for constructor and the reference to the navigation of teOrder back to it.
I can save an Entity just fine and my code runs with this:
using (var context = new EntityTesting.TesterEntities())
{
var nPerson = new tePerson { FirstName = "Test", LastName = "Tester" };
context.tePerson.Add(nPerson);
context.SaveChanges();
}
What I would suggest is:
Go to your (name).edmx Entity File and on the design surface wipe out the object and then replace it on the surface. In countless times this has fixed issues with the generated objects. When you SAVE it should auto run the T4 templates (*.tt files). If not you can select them, right click and select 'Run Custom Tool'. This just generates the POCO objects or the Context objects.
If this is really a fault with the table it is typically with it NOT having a key. You are showing it does though. Is there anyway to mirror the exact same table logic and confirm the key has nothing out of the ordinary and is just a plain old key Primary Key?
Create a brand new table with similar structure but not an exact copy and a new Entity File and confirm you can create it.
Tables are pretty straight forward with EF, or as straight forward as EF can be. You create them in SQL, ensure you have a key, add it to a design surface, save, it generates the objects for you. If you have other things under the hood like custom procs hooked to it or other out of the ordinary nav items that would be one thing. The only other thing would be if it has a very old SQL Type as the key. I know that the 'Text' type and EF do not play nice together. There may be other types that behave the same way.
This issue was fixed by including the "metadata" part of the connection string.
At first my connection string looked like this:
data source=.;initial catalog=TestDatabase;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework;
Which produced the error.
Changing my connection string to this:
metadata=res://*/DbContexts.TestContext.csdl|res://*/DbContexts.TestContext.ssdl|res://*/DbContexts.TestContext.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=TestDatabase;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
Allowed operations on the Context to be performed with no error encountered
I currently have a Entity Framework 4.0 model in place with Table Per Type (TPT), but there are a few performance issues (lots of LOJ's/CASE statements), as well as an issue mapping between two particular domain areas (many-to-many).
I've decided to try out TPH.
I have an entity called "Location" which is abstract, and the base for all other entities.
I then have "Country", "City", "State", "Street", etc which all derive from Location.
"LocationType" is the dicriminator.
That part is working fine, but i'm having issues trying to define navigational properties for the derived types.
For instance, a "State" has a single "Country", so i should be able to do this:
var state = _ctx.Locations.OfType<State>().Include("Country").First();
var countryForState = state.Country;
But this would require a navigational property called "Country" on the "State" derived entity. How do i do this? When i generate the model from the database, i have a single table with all the FK's pointing to records in the same table:
(NOTE: I created those FK's manually in the DB).
But the FK's are placed as nav's on the "Location" entity, so how do i move these navigational properties down to the derived entities? I can't copy+paste the navs across, and i can't "create new navigational property", because it won't let me define the start/end role.
How do we do this?
It's also not clear with TPH if we can do it model-first, or we HAVE to start with a DB, fix up the model then re-generate the DB. I am yet to find a good example on the internet about how to define navs on children with TPH.
NOTE: I do not want to do code-first. My current solution has TPT with the EDMX, and pure POCO's, i am hoping to not affect the domain model/repositories (if possible), and just update the EF Model/database.
EDIT
Still no solution - however im trying to do model-first, and doing Add -> New Association, which does in fact allow me to add a nav to the derived entities. But when i try and "Generate database from Model", it still tries to create tables for "Location_Street", "Location_Country" etc. It's almost like TPH cannot be done model first.
EDIT
Here is my current model:
The validation error i am currently getting:
Error 1 Error 3002: Problem in mapping
fragments starting at line
359:Potential runtime violation of
table Locations's keys
(Locations.LocationId): Columns
(Locations.LocationId) are mapped to
EntitySet NeighbourhoodZipCode's
properties
(NeighbourhoodZipCode.Neighbourhood.LocationId)
on the conceptual side but they do not
form the EntitySet's key properties
(NeighbourhoodZipCode.Neighbourhood.LocationId,
NeighbourhoodZipCode.ZipCode.LocationId).
Just thought i'd keep editing this question with edit's regarding where i am currently at. I'm beginning to wonder if TPH with self-referencing FK's is even possible.
EDIT
So i figured out the above error, that was because i was missing the join-table for the Neighbourhood-ZipCode many to many.
Adding the join table (and mapping the navs to that) solved the above error.
But now im getting this error:
Error 3032: Problem in mapping
fragments starting at lines 373,
382:Condition members
'Locations.StateLocationId' have
duplicate condition values.
If i have a look at the CSDL, here is the association mapping for "CountyState" (a State has many counties, a County has 1 state):
<AssociationSetMapping Name="CountyState" TypeName="Locations.CountyState" StoreEntitySet="Locations">
<EndProperty Name="State">
<ScalarProperty Name="LocationId" ColumnName="StateLocationId" />
</EndProperty>
<EndProperty Name="County">
<ScalarProperty Name="LocationId" ColumnName="LocationId" />
</EndProperty>
<Condition ColumnName="StateLocationId" IsNull="false" />
</AssociationSetMapping>
It's that Condition ColumnName="StateLocationId" which is complaining, because ZipCodeState association also this condition.
But i don't get it. The discriminators for all entities are unique (i have triple checked), and i would have thought this was a valid scenario:
County has a single State, denoted by StateLocationId (Locations table)
ZipCode has a single State, denoted by StateLocationId (Locations table)
Is that not valid in TPH?
So i solved a few of my issues, but i hit a brick wall.
First of all, when you create self-referencing FK's in the database side, when you try and "Update Model from Database", Entity Framework will add these navigational properties to the main base type, as it has no explicit sense of TPH - you need to do this in the model side.
BUT, you can manually add the navigational properties to the child types.
WRT this error:
Error 3032: Problem in mapping fragments starting at lines 373, 382:Condition members 'Locations.StateLocationId' have duplicate condition values.
That was because i had an FK called "Location_State" which i was attempting to use for the "ZipCode_State" relationship, AND the "City_State" relationship - which does not work (still no idea why).
So to solve that, i had to add extra columns and extra FK's - one called "ZipCode_State", and another called "City_State" - obviously it has to be a 1-1 between navs and physical FK's.
Location.LocationType has no default value and is not nullable. A column value is required to store entity data.
That is my discriminator field. In the database side, it is not nullable.
I read threads about this issue, and they said you need to change the relationships from 0..* to 1..* - but my relationships already were 1..*.
If you look at my "Locations" actual database table above, all the FK's are nullable (they have to be). Therefore i started wondering if my relationships should be 0..*.
But they are nullable because of the TPH - not all "Locations" will have a "State". But if that Location is a "City", then it HAS to have a "State".
My feelings were further comforted by this SO question: ADO EF - Errors Mapping Associations between Derived Types in TPH
I was actually trying that workaround (before i even came across it), and the workaround does not work for me. I even tried changing all the relationships from 1..* to 0..*, and still no luck.
Wasting too much time here, I've gone back to TPT.
At the end of the day, with TPH i would have had a ridiculously large table, with lots and lots of redundant, nullable columns. JOIN-wise, it's more efficient. But at least with TPT i am not required to have nullable and self-referencing FK's.
If anyone has a solution to this problem, let me know. But until then, im sticking with TPT.