EF Code first parent child mapping - entity-framework

This may be a duplicate qn, but i couldnt get a proper answer to this scenario. I have the following table structure:
public class File
{
public int FileId { get; set; } //PK
public int VersionID { get; set; }
public virtual ICollection<FileLocal> FileLLocalCollection { get; set; }
}
public class FileLocal
{
public int FileId { get; set; } //PK, FK
public int LangID { get; set; } //PK,FK
public string FileName { get; set; }
}
I have not included the third table here(Its basically LangID (PK) & LangCode )
How do i specify this mapping in fluent Api so that i can load "FileLLocalCollection" with every File objects?

The first part of your mapping can be done this way:
modelBuilder.Entity<File>()
.HasMany(f => f.FileLocalCollection)
.WithRequired()
.HasForeignKey(fl => fl.FileId);
modelBuilder.Entity<FileLocal>()
.HasKey(fl => new {fl.FileId, fl.LangId});
And the second part depends on the way how your Lang is defined. For example if you add navigation property from FileLocal to Lang you can map it this way:
modelBuilder.Entity<FileLocal>()
.HasRequired(fl => fl.Lang)
.WithMany()
.HasForeignKey(fl => fl.LangId);

Related

MVC EF code first creating model class

I'm new to MVC and EF code first. I'm in struggle to model a real-estate company DB model using EF code-first approach and I did some exercises as well as reading some online tutorials.
First thing I have a customers table that would be in relation with one or more properties he/she has registered as it's owner to sell or to rent, I was wondering if it is possible to have some sub classes inside a model class for registered properties as below:
public Property
{
public int PropertyID { get; set; }
public bool IsforSale { get; set; }
public bool IsforRent { get; set; }
public class Apartment{
public int ApartmentID { get; set; }
public int AptSqureMeter { get; set; }
. . .
. . .
}
public class Villa{
public int VillaID { get; set; }
public int VillaSqureMeter { get; set; }
. . .
. . .
}
and also other sub-classes for other types of properties
}
If the answer is Yes, then how should I declare the relations using data annotation or Fluent API, and then please help me how to update both Customers table and Property table with the customer information and property info at the same time?
thanks for your answer in advance.
As #Esteban already provided you with a pretty detailed answer on how to design your POCOs and manage the relationship between them, I will only focus on that part of your question:
how should I declare the relations using data annotation or Fluent API
First of all, you should know that certain model configurations can only be done using the fluent API, here's a non exhaustive list:
The precision of a DateTime property
The precision and scale of numeric properties
A String or Binary property as fixed-length
A String property as non-unicode
The on-delete behavior of relationships
Advanced mapping strategies
That said, I'm not telling you to use Fluent API instead of Data Annotation :-)
As you seem to work on an MVC application, you should keep in mind that Data Annotation attributes will be understood and processed by both by Entity Framework and by MVC for validation purposes. But MVC won't understand the Fluent API configuration!
Both your Villa and Apartment classes have similar properties, if they are the same but as it's type, you could create an enum for that.
public enum PropertyType {
Apartment = 1,
Villa
}
public class Property {
public int PropertyID { get; set; }
public bool IsforSale { get; set; }
public bool IsforRent { get; set; }
public PropertyType PropertyType { get; set; }
public int SquareMeter { get; set; }
}
This way of modelating objects is refered as plain old clr object or POCO for short.
Assume this model:
public class User {
public int UserId { get; set; }
public string Username { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role {
public int RoleId { get; set; }
public string Name { get; set; }
public virtual List<User> Users { get; set; }
}
Creating relations with fluent api:
Mapping many to many
On your OnModelCreating method (you'll get this virtual method when deriving from DbContext):
protected override void OnModelCreating(DbModelBuilder builder) {
// Map models/table
builder.Entity<User>().ToTable("Users");
builder.Entity<Role>().ToTable("Roles");
// Map properties/columns
builder.Entity<User>().Property(q => q.UserId).HasColumnName("UserId");
builder.Entity<User>().Property(q => q.Username).HasColumnName("Username");
builder.Entity<Role>().Property(q => q.RoleId).HasColumnName("RoleId");
builder.Entity<Role>().Property(q => q.Name).HasColumnName("Name");
// Map primary keys
builder.Entity<User>().HasKey(q => q.UserId);
builder.Entity<Role>().HasKey(q => q.RoleId);
// Map foreign keys/navigation properties
// in this case is a many to many relationship
modelBuilder.Entity<User>()
.HasMany(q => q.Roles)
.WithMany(q => q.Users)
.Map(
q => {
q.ToTable("UserRoles");
q.MapLeftKey("UserId");
q.MapRightKey("RoleId");
});
Mapping different types of relationships with fluent api:
One to zero or one:
Given this model:
public class MenuItem {
public int MenuItemId { get; set; }
public string Name { get; set; }
public int? ParentMenuItemId { get; set; }
public MenuItem ParentMenuItem { get; set; }
}
And you want to express this relationship, you could do this inside your OnModelCreating method:
builder.Entity<MenuItem>()
.HasOptional(q => q.ParentMenuItem)
.WithMany()
.HasForeignKey(q => q.ParentMenuItemId);
One to many
Given this model:
public class Country {
public int CountryId { get; set; }
public string Name { get; set; }
public virtual List<Province> Provinces { get; set; }
}
public class Province {
public int ProvinceId { get; set; }
public string Name { get; set; }
public int CountryId { get; set; }
public Country Country { get; set; }
}
You now might want to express this almost obvious relationship. You could to as follows:
builder.Entity<Province>()
.HasRequired(q => q.Country)
.WithMany(q => q.Provinces)
.HasForeignKey(q => q.CountryId);
Here are two useful links from MSDN for further info:
Configuring Relationships with the Fluent API.
Code First Relationships Fluent API.
EDIT:
I forgot to mention how to create a many to many relationship with additional properties, in this case EF will NOT handle the creation of the join table.
Given this model:
public class User {
public int UserId { get; set; }
public string Username { get; set; }
public virtual List<Role> Roles { get; set; }
pubilc virtual List<UserEmail> UserEmails { get; set; }
}
pubilc class Email {
public int EmailId { get; set; }
public string Address { get; set; }
public List<UserEmail> UserEmails { get; set; }
}
public class UserEmail {
public int UserId { get; set; }
public int EmailId { get; set; }
public bool IsPrimary { get; set; }
public User User { get; set; }
public Email Email { get; set; }
}
Now that we've added a new property into our join table ef will not handle this new table.
We can achieve this using the fluent api in this case:
builder.Entity<UserEmail>()
.HasKey( q => new {
q.UserId, q.EmailId
});
builder.Entity<UserEmail>()
.HasRequired(q => q.User)
.WithMany(q => q.UserEmails)
.HasForeignKey(q => q.EmailId);
builder.Entity<UserEmail>()
.HasRequired(q => q.Email)
.WithMany(q => q.UserEmails)
.HasForeignKey(q => q.UserId);

Entity framework Invalid Column name, EF adds number 1 to primary key

I have these two entities:
public partial class Suscriptores
{
public Suscriptores()
{
this.Publicacion = new HashSet<Publicacion>();
}
[Key]
public int IdSuscriptor { get; set; }
public string LogoSuscriptor { get; set; }
public string Identificacion { get; set; }
public string Nombre { get; set; }
public string Direccion { get; set; }
public string Telefono { get; set; }
public string Email { get; set; }
public string Fax { get; set; }
public string Home { get; set; }
public virtual ICollection<Publicacion> Publicacion { get; set; }
}
public partial class Publicacion
{
public Publicacion()
{
this.Edictos = new HashSet<Edictos>();
}
[Key]
public decimal IdPublicacion { get; set; }
public System.DateTime FechaPublicacion { get; set; }
public string IdUsuario { get; set; }
public System.DateTime FechaPublicacionHasta { get; set; }
public System.DateTime FechaArchivoHasta { get; set; }
public int IdSuscriptor { get; set; }
public decimal IdTipoPublicacion { get; set; }
[ForeignKey("IdSuscriptor")]
public virtual Suscriptores Suscriptores { get; set; }
}
When I try to run this query:
public ActionResult DetailsVSTO(string Identificacion)
{
var SusQ = from s in db.Suscriptores
where s.Identificacion == Identificacion
select s;
return Json(SusQ.First(), JsonRequestBehavior.AllowGet);
}
It throw this message:
System.Data.SqlClient.SqlException: Invalid column name 'Suscriptores_IdSuscriptor1'
Trying to solve this problem, I added this fluent API code at DBContext:
modelBuilder.Entity<Suscriptores>()
.HasMany(x => x.Publicacion)
.WithRequired(x => x.Suscriptores)
.Map(a => a.MapKey("IdSuscriptor"));
But the problem persists. How can I solve this?
Try add a many-to-one mapping as well. Please use pure Fluent API, and you should remove the [ForeignKey] annotations.
modelBuilder.Entity<Publicacion>()
.HasRequired(x => x.Suscriptores)
.WithMany(x => x.Publicacion);
I received this error in relation to a non-foreign key column and wasted far too much time trying to figure out the error. It was in my code, not in EF or the database. I had simply thought that I had typed
this.Property(t => t.Revision).HasColumnName("Revision");
this.Property(t => t.DistributionClass).HasColumnName("DistributionClass");
But what I had typed was
this.Property(t => t.Revision).HasColumnName("Revision");
this.Property(t => t.Revision).HasColumnName("DistributionClass");
I suppose I was looking at the line above and put t.Revision instead of t.DistributionClass. And no matter how long I looked at it I could not see my own mistake. With any luck this will save some other poor soul some time.
I had this issue in my Item table on a property (column) I had just added, and how frustrating!
Turns out I had a List property in the data model for Order, and because I did not Ignore it in that configuration it cause this same issue in the Item table. This would not have happened except that both tables had a property of the same name, so I had to do this... which I should have done anyways.
public OrderConfiguration() {
Ignore(p => p.Items);
}
Hello Guys In my case I had a legacy code with
two classes with different names of the same foreign key.
Adding the Annotation doing reference to the correct column and the name of attribute with the same name in other classes.then the annotation ForeignKey doing match between the both columns.
[Table("LFile")]
public partial class LFile
{
[Key]
public int FileId { get; set; }
[StringLength(500)]
public string FileName { get; set; }
public int? LRRFileDetailId { get; set; }
public byte[] FileData { get; set; }
public FileType Type { get; set; }
[Column("LUpload_Id")] //Foreign Key of the class
public int? LUploadId { get; set; }
[ForeignKey("LUploadId")] //Foreign key inherited
public virtual LParserProcess LParserProcess { get; set; }
}
I was getting the same error SqlException message where the number 1 was being appended to my field names.
I was able to solve it once I realized that I had incorrectly assumed the [ForeignKey] annotations refer to the field name as it is in the database. Instead, they should match the property name as defined in the model.
So for example:
[Column("Organisation_Account_Manager")]
[Display(Name = "Organisation_Account_Manager_ID")]
[DisplayFormat(NullDisplayText = "Unknown")]
public int? Organisation_Account_Manager_ID { get; set; }
[Display(Name = "Account Manager")]
[ForeignKey("Organisation_Account_Manager_ID")]
public Contact Account_Manager { get; set; }
In this example it will work, because [ForeignKey("Organisation_Account_Manager_ID")] is an exact match to public int? Organisation_Account_Manager_ID. Previously my [ForeignKey] annotation was using Organisation_Account_Manager, which is the field name in the database -- but this was incorrect.

Is there a data annotation equivalent to Entity Framework's fluent API's WillCascadeOnDelete()? [duplicate]

I'm currently using EF Code First 4.3 with migrations enabled, but automatic migrations disabled.
My question is simple, is there a data annotations equivalent of the model configuration .WillCascadeOnDelete(false)
I would like to decorate my class so that the foreign key relationships do NOT trigger a cascading delete.
Code sample:
public class Container
{
public int ContainerID { get; set; }
public string Name { get; set; }
public virtual ICollection<Output> Outputs { get; set; }
}
public class Output
{
public int ContainerID { get; set; }
public virtual Container Container { get; set; }
public int OutputTypeID { get; set; }
public virtual OutputType OutputType { get; set; }
public int Quantity { get; set; }
}
public class OutputType
{
public int OutputTypeID { get; set; }
public string Name { get; set; }
}
I Would like to do something like this:
public class Output
{
[CascadeOnDelete(false)]
public int ContainerID { get; set; }
public virtual Container Container { get; set; }
[CascadeOnDelete(false)]
public int OutputTypeID { get; set; }
public virtual OutputType OutputType { get; set; }
public int Quantity { get; set; }
}
This way i would be able to scaffold the migration correctly. which scaffolds the foreign key relationships to be cascade deleted at the moment.
Any ideas, other than using Model Configuration?
No there is no such equivalent. You must use fluent API to remove cascade delete selectively or you must remove OneToManyCascadeDelete convention to remove it globally.
Create a mapping class (the fluent syntax) and use the code below:
// add relationships "Post" and "User" to a "Comment" entity
this.HasRequired(t => t.Post)
.WithMany(t => t.Comments)
.HasForeignKey(d => d.PostID)
.WillCascadeOnDelete(false); // <---
this.HasOptional(t => t.User)
.WithMany(t => t.Comments)
.HasForeignKey(d => d.UserID)
.WillCascadeOnDelete(false); // <---
Here's a nice post on how to set up fluent mappings if you need more info.
Just make the FK property nullable can prevent cascade delete from happening:
public int? OutputTypeID { get; set; }

How do I code an optional one-to-one relationship in EF 4.1 code first with lazy loading and the same primary key on both tables?

I'm working with an application and data structure built upon ASP/ADO.NET and I'm converting part of it to ASP.NET MVC. In the data structure, there exists a "optional one-to-one" relationship, where both tables use the same primary key, and name. Basically this table can be considered an "optional extension" of the primary table. Here are samples of the model:
public class ZoneMedia
{
public int ZoneMediaID { get; set; }
public string MediaName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public virtual ZoneMediaText MediaText { get; set; }
}
public class ZoneMediaText
{
public int ZoneMediaID { get; set; }
public string Text { get; set; }
public int Color { get; set; }
}
Obviously, EF 4.1 code first has an issue mapping this automatically. So I realize I must specify the mapping explicitly. I tried this:
modelBuilder.Entity<ZoneMedia>()
.HasOptional(zm => zm.ZoneMediaText);
modelBuilder.Entity<ZoneMediaText>()
.HasRequired(zmt => zmt.ZoneMedia)
.WithRequiredDependent(zm => zm.ZoneMediaText)
.Map(m => m.MapKey("ZoneMediaID"));
But it is still giving me an exception about the name of the primary key.
Schema specified is not valid. Errors:
(199,6) : error 0019: Each property name in a type must be unique. Property name 'ZoneMediaID' was already defined.
I'm a little stumped. I need to adapt to this non-conventional structure I realize in EF 4.1 it would be much easier to just add a unique PK to the optional relation and hold the foreign key relationship in the primary table, but I can't change the database layout. Any advice would be appreciated.
I hope i understood well.
This works for me:
public class ZoneMedia
{
public int ZoneMediaID { get; set; }
public string MediaName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public virtual ZoneMediaText MediaText { get; set; }
}
public class ZoneMediaText
{
public int ZoneMediaID { get; set; }
public string Text { get; set; }
public int Color { get; set; }
public virtual ZoneMedia ZoneMedia { get; set; }
}
public class TestEFDbContext : DbContext
{
public DbSet<ZoneMedia> ZoneMedia { get; set; }
public DbSet<ZoneMediaText> ZoneMediaText { get; set; }
protected override void OnModelCreating (DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ZoneMedia>()
.HasOptional(zm => zm.MediaText);
modelBuilder.Entity<ZoneMediaText>()
.HasKey(zmt => zmt.ZoneMediaID);
modelBuilder.Entity<ZoneMediaText>()
.HasRequired(zmt => zmt.ZoneMedia)
.WithRequiredDependent(zm => zm.MediaText);
base.OnModelCreating(modelBuilder);
}
}
class Program
{
static void Main (string[] args)
{
var dbcontext = new TestEFDbContext();
var medias = dbcontext.ZoneMedia.ToList();
}
}
This Correctly create a FK_ZoneMediaTexts_ZoneMedias_ZoneMediaID in ZomeMediaTexts table, and the Foreign Key is the Primary Key.
EDIT: maybe it's worth pointing out that I'm using EF 4.3.0

Entity Framework - Error: define the key for entity type

Hallo everybody,
i have the following simple models.
public class A
{
public B B { get; set; }
public C C { get; set; }
}
public class B
{
public int Id { get; set; }
}
public class C
{
public int Id { get; set; }
}
I get an error while i am trying to get the data:
System.Data.Edm.EdmEntityType: :
EntityType 'A' has no key
defined. Define the key for this
EntityType.
Previous it was done via "RelatedTo". Has anybody a solution for this problem with the help of an example?
Thanks in advance!
Each entity in EF must have a primary key. It looks like A is junction table for many to many so you have multiple choices.
Remove A totaly and let EF handle many-to-many:
public class B
{
public int Id { get; set; }
public virtual ICollection<C> Cs { get; set; }
}
public class C
{
public int Id { get; set; }
public virtual ICollection<B> Bs { get; set; }
}
If you want A as entity you must either define additional key:
public class A
{
public int Id { get; set; }
public B B { get; set; }
public C C { get; set; }
}
or you must include FK properties for B and C and mark them both as composite primary key (should be in db as well):
public class A
{
public int bId { get; set; }
public int cId { get; set; }
public B B { get; set; }
public C C { get; set; }
}
Edit:
Mapping for the last solution
modelBuilder.Entity<A>.HasKey(a => new { a.bId, a.cId });
modelBuilder.Entity<A>.HasRequired(a => a.B)
.WithMany()
.HasForeignKey(a => a.bId);
modelBuilder.Entity<A>.HasRequired(a => a.C)
.WithMany()
.HasForeignKey(a => a.cId);
Anyway if your A looks exactly as you described without any other properties you are definitely doing it wrong. Mapping A is only needed when it contains anything else then navigation properties / FKs for modelling many-to-many relation.