Entity Framework multiple mapping to same table DB first - c#

I have an issue with an Entity Framework from DB model.
My issue is down to the fact that one of my models has a multiple references to one table.
public partial class Customer
{
public int Id { get; set; }
public Nullable<int> PrimaryEngId { get; set; }
public Nullable<int> AssignedDevloperId { get; set; }
public virtual Engineer Engineer { get; set; }
public virtual Engineer Engineer1 { get; set; }
}
In my model the columns are mapped respectively, however when a colleague builds the model from the same database the two are reversed.
I believe the issue is that the first mapping to in was the primaryEngId
and the Db constraint is called FK_Customer_Engineer.
And the assigned developer id was added subsequently and the DB constraint is called FK_Customer_Devloper
So alphabetically Developer come before Engineer and Entity Framework now maps them the other way round.
My code references the Engineer in quite a lot of places which now won't work
Is there any way out of this?
Many thanks
Ian

You have to add missing ForeignKey attributes on foreign keys for those two navigation properties:
[ForeignKey("Primary")]
public int? PrimaryEngId { get; set; }
[ForeignKey("Assigned")]
public int? AssignedDevloperId { get; set; }
public virtual Engineer Primary { get; set; }
public virtual Engineer Assigned { get; set; }
NOTE: Also don't use generic names for navigation properties with EF. In the nutshell one of the best things EF gives you is that you can say:
#myCustomer.Assigned.Name
etc in the view, and you are totally screwing it up with names like Engineer and Engineer1.
NOTE2: Keep Nullable<int> to code generation. int? is a lot more readable.
NOTE3: Use VS refactoring to rename properties Engineer and Engineer1 to what they should be ( PrimaryEngineer and AssignedEningeer etc). After that add ForeignKey attributes to your model. That should be enough. However, any future changes that you are doing has to be done in the Code and not in db.
IF on the other hand you are constantly regenerating entities and context code from database, make sure that all your foreign keys has meaningful names, as EF will use them to generate name.(ie it is not named Engineer1 out of blue) Rename those foreign keys to reflect what logical relationship is. Ie you most likely have the following foreign keys in db:
FK_Customer_Engineer
FK_Customer_Engineer1
You need to rename them to
FK_Customer_PrimaryEngineer
FK_Customer_AssignedEngineer
Update: you can have different column name and property name like so:
[Column("PrimaryEngId")]
[ForeignKey("Primary")]
public int? PrimaryID { get; set; }

Related

How to make one table to many class in EDMX design

This my main class
public partial class MainAcc
{
public int Id { get; set; }
public string Type1 { get; set; }
public string Type2 { get; set; }
public string Type3 { get; set; }
}
and in EDMX design i make new entity base type MainAcc, i delete some column that i want to move in EDMX design, so it become like this after i save
public partial class MainAcc
{
public int Id { get; set; }
public string Type1 { get; set; }
}
public partial class ChildAcc : MainAcc
{
public string Type2 { get; set; }
public string Type3 { get; set; }
}
In design my ChildAcc entity use table map of MainAcc. and after all i got error like this:
Error 3032: Problem in mapping fragments starting at lines 2877, 2907:EntityTypes MyModel.MainAcc, MyModel.ChildAcc are being mapped to the same rows in table MainAcc. Mapping conditions can be used to distinguish the rows that these types are mapped to.
That error in text editor is in MainAcc.
Looks like you can't do this. I think these should be two different tables, but they can be related by some foreign key.
I tried Database First and Model First and the script I ended up adding showed that this would be two different tables related to each other.
Perhaps you expect to have one-to-one or one-to-many relationships, but that doesn't happen within a single table. If you go the Database First way, changing the model may result in an inconsistency with the current context. If the Model First method is adopted, two tables will be automatically generated after the model relationship is established.
I also tried to use table splitting for table mapping, but there are some limitations for dependent entity types, and derived types cannot be mapped to the same table.
Helpful Links:
Advanced table mapping
Model First
Configure One-to-Many Relationships in EF 6
This is just my opinion, hope this helps you. Please help me correct if my understanding is wrong.

Entity Framework - Explicitly mapped ID columns

Consider the relationship between the following entities:
class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
class Author
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
Okay, nobody in their right minds would have a one-to-one relationship in this context; that's not the issue at play here...
You'll notice that for each navigation property (Author and Post) there are explicit Id columns defined (AuthorId and PostId) respectively.
Personally I don't like this approach (though I can see some potential benefit). I'd prefer EF to manage the Id columns internally for me, and just let me expose a relationship between Post and Author.
What I want to know is, is there any official recommendation for or against explicit Id columns?
NOTE: I do know of one place where explicit Id mapping is valuable, and that is when you're implementing a many-to-many join table. You can use the Ids to create a unique constraint which prevents record duplication for the same many-to-many relationship.
What I want to know is, is there any official recommendation for or against explicit Id columns?
Yes:
It is recommended to include properties in the model that map to
foreign keys in the database. With foreign key properties included,
you can create or change a relationship by modifying the foreign key
value on a dependent object. This kind of association is called a
foreign key association. Using foreign keys is even more essential
when working with N-Tier applications.
Entity Framework Relationships and Navigation Properties
This is not an "official recommendation". But here is how I see it.
You want the navigational properties to be virtual for lazy loading, and you will need the two Id columns for the mapping into the database.
But your code never uses those foreign keys. Here is an example that links a Post with an Author.
var p = new Post {Title="Foo"};
p.Author = _db.Authors.First(a => a.Id == 5);
_db.Posts.Add(p);
_db.SaveChanges();
You also need to map those fields up into your domain layer to keep track of relations.

Modeling to use same FK name of a unchanged database

I am using EF6 but...
I can not change the database.
So, if I'm not wrong, I need to create a model that suits the database.
I have to models in relationship one to many:
[Table("ReceCli")]
public class ReceCli
{
[Key]
public int Indice { get; set; }
[Required, StringLength(12)]
[Display(Name = "NÂș Documento")]
public string NDOC { get; set; }
[Display(Name = "Banco do boleto")]
[Column("CodBancoBoleto")]
public int CodBancoBoleto { get; set; }
public Banco Banco { get; set; }
}
and
[Table("Bancos")]
public class Banco
{
[Key]
public int CodBanco { get; set; }
[Column("Banco")]
[Required, StringLength(50)]
[Display(Name = "Banco")]
public string Nome { get; set; }
}
In the database this relations are expressing like:
ALTER TABLE [dbo].[ReceCli] WITH NOCHECK ADD CONSTRAINT [ReceCli_CodBancoBoleto] FOREIGN KEY([CodBancoBoleto])
REFERENCES [dbo].[Bancos] ([CodBanco])
GO
ALTER TABLE [dbo].[ReceCli] CHECK CONSTRAINT [ReceCli_CodBancoBoleto]
When executing return an error:
Invalid column name 'Banco_CodBanco'.
I can not change the database.
How can I change the model to EF use ReceCli_CodBancoBoleto name of column instead of Banco_CodBanco ?
You can do model an existing db by hand but you can also tell EF to generate the model from an existing database.
As for your example, a couple of things:
The relationship you have modeled is not one to many but one to one.
Public Banco Banco {get; set;}
Change To:
Public ICollection<Banco> Bancos {get;set;}
There are several ways you can model relationships with EF. Here's a sample of Modeling 1 to many relationships in EF.
The Column attribute is used to match to names in the DB. Make sure your EF CF properties that don't match the database have a Column Attribute. For Your RecCli it should look something like:
[Column("CodBanco")]
public int CodBancoBoleto { get; set; }
or
public int CodBanco { get; set; }
However, you are mapping a 1 to many relationship so having the CodBancoBoleto is not needed. Just use the navigation property of Public ICollection<Banco> Bancos {get;set;}. This should suffice except you might have to put a ForeignKey attribute for it telling it to use CodBanco as the key for the navigation.
[ForeignKey("CodBanco")]
Public ICollection<Banco> Bancos {get;set;}
You might have to do this for all your keys as the default code first convention for keys end with Id. I say might as your Banco Class's key is named properly CodBanco and marked with the Key. So you might be fine.
A final note is that you appear to be trying to use the constraints name for the mapping. You don't use the constraint name, rather the actual column names, aka the references part of the constraint.

How to define references when one poco/table has a composite primary key

Considering the documentation here, you can define foreign key relationships in your pocos like the given example:
public class Customer
{
[References(typeof(CustomerAddress))]
public int PrimaryAddressId { get; set; }
[Reference]
public CustomerAddress PrimaryAddress { get; set; }
}
However, let's say that my CustomerAddress poco class actually has to be defined like this because someone decided to design the table like this a long, long time ago.
public class CustomerAddress
{
[PrimaryKey]
public int Id_1 { get; set; }
[PrimaryKey]
public string Id_2 { get; set; }
}
How can I properly define my [Reference] for the PrimaryAddress property in the Customer class with the composite key defined in CustomerAddress?
You can't using APIs that rely on it (but you can still use SELECT)
Please see OrmLite limitations.
I had same problem with a legacy database I can't modify (because another project cohabit with mine).
So I deleted primary key then created a new Id field (autoincrement) on the database (PK) and finally created a unique constraint (NOT NULLABLE) on both fields.
So now, I can use OrmLite to select properly without breaking compatibility with the other project.

Code first model not auto incrementing key on insert

I think I have an error in my model, but I'm not sure what it is.
First the error
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_dbo.ProjectDocAccess_dbo.ProjectDoc_ProjectDocAccessID". The conflict
occurred in database "dbname",
table "dbo.ProjectDoc", column 'ProjectDocID'.
The ProjectDocAccess model (trimmed down)
public class ProjectDocAccess
{
public int ProjectDocAccessID { get; set; }
public int ProjectDocID { get; set; }
public virtual ProjectDoc ProjectDoc { get; set; }
}
The ProjectDoc model (trimmed down)
public class ProjectDoc
{
public int ProjectDocID { get; set; }
public int ProjectID { get; set; }
public ProjectDocAccess ProjectDocAccess { get; set; }
public Project Project { get; set; }
}
The fluent API mapping
modelBuilder.Entity<ProjectDocAccess>()
.HasRequired(p => p.ProjectDoc).WithOptional()
.WillCascadeOnDelete(true);
When I attempt to insert a new record in the ProjectDocAccess table, it forces me to insert a value in the ProjectDocAccessID field. In all of my other models, this auto increments. I'm not sure what I am doing wrong. Help?
UPDATE
Based on the answer I selected. This is what I did to fix it.
Removed the fluent API mapping altogether.
Updated the ProjectDocAccess model as follows
public class ProjectDocAccess
{
[Key, ForeignKey("ProjectDoc")]
public int ProjectDocAccessID { get; set; }
public virtual ProjectDoc ProjectDoc { get; set; }
}
First, when issues like this you should take a look at your migration files generated (or if you don't have them just enable it) - it shows in a 'higher level' form of tables/mappings generated for you.
Problem is that you have 'one to one' relationship and your ProjectDocAccess to ProjectDoc is mapped with pk -> pk. Code first automatically does that for you in these cases as that's the only supported way of making the one to one.
So your ProjectDocAccessID is at all times mapped == the same as your ProjectDocID.
The ProjectDocID is auto-generated - but you need to put the access-id to match it yourself. (I'm guessing that could be automated but strictly in db terms it's not).
So, it can't auto-generate things. It has to be copied from the principal
table, where the 'optional' is.
With your current entities it is what it is - but you could rearrange things maybe (though one-to-one are tough to make and could lead to issues if you don't get it right).
There is an option to create one to one using FK-s only (which would then allow you to place an 'independent' primary key on your 'access' table) - but requires manually injecting SQL CONSTRAINT - e.g. see this page - http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx
If you want the ProjectDocAccessID to be generated by the database (an Identity column) then you should decorate that property with the DatabaseGenerated attribute, like this:
[DatabaseGenerated(DatabaseGenerationOption.Identity)]
public int ProjectDocAccessID { get; set; }
As Andrei pointed out, you may also need to decorate it with the [Key] attribute as well.
Source: http://msdn.microsoft.com/en-US/data/jj591583

Categories