Entity framework core one-to-many relationship - entity-framework

I have two models DestinationEntity and SourceEntity, what I want is in view I need to get destination details first, after that I need to get source details in that I want to select multiple destinations and save both have separate CRUD entry, how can I achieve this
source can have multiple destination and destination can have single source
here is my entities
public class DestinationEntity
{
public int ID {get; set;}
public string DName {get; set;}
public string DPath {get; set;}
public SourceEntity source {get; set;}
}
public class SourceEntity
{
public int ID {get; set;}
public string SName {get; set;}
public string SPath {get; set;}
public ICollection<DestinationEntity> Destinations {get; set;}
}

Create and Delete are pretty obvious, Update is the tricky one. Basically, there are three approaches. Since each Destination MUST have one and only one Source.
One, remove all Destinations and then re-add them to the Source. This removes "orphaned" Destinations and is the simplest, brute force approach.
Two, have the UI communicate which Destinations are added and which are removed. OR, detect it yourself by examining the FKs (null = new) in the input and compare existing with saved values, removing the "deleted" Destinations. Trickier.
Three, use the JSONPatchDocument format which gives a roadmap on how to update the saved entities.

Related

How to handle custom fields in Entity Framework 6.0?

Consider this scenario:
Table1
Field1
Field2
Field3
We are writing an ERP application and it is common for implementations have additional fields based on user needs say User1 and User2 to Table1. Information about these user fields is stored in metadata tables. Custom fields will differ from one implementation to other.
I am wondering if EF can support this requirement.
Ideally I would like to have model classes generated like
partial class Table1
public Field1 {get; set;}
public Field2 {get; set;}
public Field3 {get; set;}
///The user fields collection should store user defined fields
public List<string, object) UserFields {get; set;}
When I say context.Table1.Get("foo"), system should return appropriate data
Is this possible in EF?
regards,
Abhishek
I could not find relevant examples. I can modify template files so that all entities can have additional property public List<string, object) UserFields {get; set;}. But I have no idea how EF can recognize additional user fields and hydrate entities accordingly. Any help will be greatly appreciated.

Why is Entity Framework looking for the wrong foreign key column?

I've seen various questions on related topics, which seem like they would address my issue, but nothing I try seems to help.
I have an EF (6.1.3) model of an existing DB, which has been working fine. I've just added an additional column to a table, which represents a new relationship. Perhaps relevantly, the relationship is the second one between the two tables - the original Location is now joined by ActualDirectSite, both of them relating the Uniform and Location tables.
The moment I added the two new properties, ActualDirectSiteID and ActualDirectSite, my SELECT queries started failing with the error "Invalid column name 'Location_ID'". It's true that that column doesn't exist, but I don't see why EF is looking for it - it was happy before, but something has made it think the column name should be different. The failing name makes me think it's the original Location which is somehow no longer working.
Here's the Entity in question:
public partial class Uniform
{
public int ID { get; set; }
[Column("LocationID")]
public int? LocationID { get; set; }
[ForeignKey("LocationID")]
public virtual Location Location { get; set; }
public int? ActualDirectSiteID { get; set; }
[ForeignKey("ActualDirectSiteID")]
public virtual Location ActualDirectSite { get; set; }
}
And my (shortened) table def:
CREATE TABLE [dbo].[Uniforms](
[ID] [int] IDENTITY(1,1) NOT NULL,
[LocationID] [int] NULL,
[ActualDirectSiteID] [int] NULL)
The obvious solution to relying on convention causing incorrect assumptions about column names is to specify them explicitly, and so I've tried using Column annotations, and also to make sure that the ID and navigation properties know about each other using ForeignKey, but no dice. Any ideas?
EDIT: added missing LocationID field (already present in full code)
EDIT2: to be clear, before I added ActualDirectSiteID to the Entity it all worked fine, with no annotations required. I've just had another look at the generated SQL, and it seems like the Location_ID reference corresponds to the ActualDirectSite property:
//[Extent1] is "Uniform"
... , [Extent1].[LocationID] AS [LocationID], [Extent1].[ActualDirectSiteID] AS [ActualDirectSiteID], [Extent1].[Location_ID] AS [Location_ID], //...[Extent4] begins
EDIT3: I didn't include any of my Location entity, here it is:
[Table("Location")]
public partial class Location
{
public int ID { get; set; }
public virtual ICollection<Uniform> Uniforms { get; set; }
}
As noted in the comments: with multiple navigation properties to the same table, EF will get confused as to which navigation property refers to which inverse navigation property and ignore the FK mapping of those. A similar issue I stumbled across some time ago can be found in this SO question.
There are only two ways (I know of) to fix this issue:
Ignore at least all but one of the navigation properties with [NotMapped] or .Ignore() or
Add a inverse navigation property to (at least) all but one navigation properties to this table and adjust the mapping accordingly.
Actually, this behavior smells like a bug on EF side (from a DB point of view, I don't see the problem there), but the workaround is simple enough.
By convention every foreign key declaration include 2 properties.
If you create link to Location entity, then you must add property with name - LocationId type int. That is why you got an error
ForeignKey annotation is used to specify the name of used int id property for link (if you plan to use different name)
You can declare foreign key only like here:
public Location Location {get; set;}
public int LocationId {get; set;}
Or like here:
[ForeignKey("CustomIdProperty")]
public Location Location {get; set;}
public int CustomIdProperty {get; set;}
(Pardon me for possible typos - writting from phone)

Entity Framework Migration API

Hi i have made a data sync project on top of entity framework.
the framework is schema independent to some extent.
i want to make it more tolerant to changes in schema even the currently considered breaking changes.
to achieve this i will have to get inside the ef migration engine and will have to generate a command like
add-transformation
which will be detecting the changes and creating a transformation.
I have looked into the source code of ef 6 but couldnt find an appropriate place to start.
any help would be appreciated.
Edit 1 :- answer to questions received in the comments
Code First Approach
Extent:
Changes in data will be handled by the migration so no need to incorporate the changes.
What I need to is a way to execute a command like add-transformation which would create a new transformation like a new migration. So typically lets say i have a database model (domain model) like
class A
{
public int a {get; set;}
public int b {get; set;}
}
then i change the class to the structure
class A
{
public int a {get; set;}
public int b {get; set;}
public int c {get; set;}
}
and then i run add-tranformation ClassChangesA
the code i require should
1. Detect changes
2. Generate a class like the migration class. Ex.
class Transformation_112334_ClassChangesA
{
public A Up(OldA model){
//Property C added
}
public OldA Down(A model){
//Property C removed
}
}
I believe that the command you are looking for is add-migration migration_name
then you can update your database using the command update-database, this is how to work with code first migrations in entity framework.

Entity Framework: Map join tables

I have the following model:
I want to expose a member on "intallations" that give me a list of "modules" based on the join table "installation_modules", how can I do that ?
I want to be able to write
installations.Modules.Something()
Without having to use the join table in my code.
I also want to map "installation_type" directly on the installation, is it possible ? If yes how ?
Entity Framework will automatically manage the many-many relationship for you, but usually the installation_modules table should be having just two columns, installation_id and module_id which will be the composite primary key instead of a separate primary key. So Installation model/class will have public virtual ICollection<Module> Modules {get; set;} and the Module class will have public virtual ICollection<Installation> Installations {get; set;}navigational properties for easy accessing of entities.
I also want to map "installation_type" directly on the installation, is it possible ? If yes how ?
Yes, it is possible. You can have a navigational property for InstallationType in your Installation entity for this.
public class Installation
{
//....other properties
[Column("installation_type_id")]
public int InstallationTypeId {get; set;}
[ForeignKey("InstallationTypeId")]
public virtual InstallationType InstallationType{get; set;}
}

Fixing Model/Column mapping in Code First/EF6

First of all, I shot myself in the foot. I'm building a test application (this is work related, not school btw.) I have a model with a foreign key property
Home_TeamId
That mapped to a column called
Home_TeamId in my database. Everything was happy until I refactored everything to use ID instead of Id. I didn't notice the Migration added a column called Home_TeamID1 and is storing the data there instead of Home_TeamId (where I want it.)
So what I would like to do is:
Drop the column Home_TeamID1 (No problem, I can do that.)
Rename Home_TeamId to Home_TeamID. (No problem, I can do that.)
Tell EF to write the data to the original column.
I've read how to use database mappings in the DbContext, but that isn't what I'm trying to do either (i.e., this is a one-time thing, not something I need to do every time the app runs.) (BTW, there is no .edmx file either.)
So that's the question -- how do I tell EF to write the Home_TeamID field in the domain model to the Home_TeamID column in the table?
I should add that I've done another migration since then so it's not (necessarily) so easy as to just target back one revision.
Edit 1:
EF was writing the same Team ID to both the Home_TeamID and Home_TeamID1 columns, although it had made the ..ID1 file the foreign key.
I've looked everywhere on my project for the text "ID1" (both as text and as binary Unicode) and the only places it shows up are in the *_migration.cs files.
In the meantime, I've tried Steps 1 and 2 above. And now (as expected) I get:
InnerException: System.Data.SqlClient.SqlException HResult=-2146232060
Message=Invalid column name 'Home_TeamID1'.
Invalid column name 'Visitors_TeamID1'.
Edit 2:
I tried this:
Create a brand new (blank database)
Excluded all the .cs files in the Migrations from the project
add-migration InitialRecreate
Looked in the resulting .cs file and removed any reference to ID1. (In fact, there were two...where did they come from??)
Looked in the project and found 0 references to ID1.
Update-database
Ran the project
Invalid column name 'Home_TeamID1'.
So obviously the problem isn't the database itself.
It was a case of the software outsmarting the human. In my "higher-level" GameSummary class, I had:
public int GameSummaryID { get; set; }
public int Home_TeamID { get; set; }
public virtual Team Home { get; set; }
public int Visitors_TeamID { get; set; }
public virtual Team Visitors { get; set; }
And in the Team class I had:
public int TeamID { get; set; }
So EF was creating two columns, one for Home_TeamID (the field Home_TeamID in the GameSummary class) and one for Home_TeamID (the foreign key for the navigation property that pointed to the Team object). The solution:
public int GameSummaryID { get; set; }
public int HomeTeamID { get; set; }
public virtual Team Home { get; set; }
public int VisitorsTeamID { get; set; }
public virtual Team Visitors { get; set; }