I have this:
public class Foo
{
public int Id { get; set; }
public Bar Bar { get; set; }
}
public class Bar
{
public int Something { get; set; }
public int SomethingElse { get; set; }
}
and my database is like this:
CREATE TABLE [Foo](
[Id] INT,
[Bar_Something] INT NOT NULL,
[Bar_SomethingElse] INT NOT NULL,
)
When I get the DB context with
public class DB: DbContext
{
public DbSet<Foo> Foo { get; set; }
}
Foo.Id is mapped correctly but Bar cannot be mapped with this error System.InvalidOperationException : The entity type 'Bar' requires a primary key to be defined.
I don't want to create Bar table and give its id as FK to Foo.
How can I map the columns Bar_Something and Bar_SomethingElse to Foo.Bar.Something and Foo.Bar.SomethingElse?
EF Core 2.0 and later support Owned entity types. By default, those are mapped using Table splitting.
In EF Core 2.1, you probably only need to add the [Owned] attribute to Bar, ie :
[Owned]
public class Bar
{
public int Something { get; set; }
public int SomethingElse { get; set; }
}
The owned type's properties will be mapped to fields in the same table named Property_OwnedProperty. In this case it will be Bar_Something and Bar_SomethingElse
Looks like someone designed the table with those requirements in mind.
In EF Core 2.0 you need to specify the owned type in the context configuration :
modelBuilder.Entity<Foo>().OwnsOne(p => p.Bar);
What you seem to be looking for is Table Splitting - the second entity of Bar will still need an ID field though it would be the same field that is used for the Foo object, meaning it will join them on a 1-1 basis perfectly.
This allows you to map the ID field of the table to multiple objects, then becoming both the principal and foreign key for the join.
you can read more about it an a quick example over Here as a pretty simple blog post demo.
This can also be done using the [Owned] attribute - the difference between using owned and simply mapping two objects to the same table is that an Owned object will only ever be a navigational property - so you wouldnt be able to just look for Bar you would always have to look for Foo and include Bar.
Depending on how you want them to behave (independent, or dependant) you have the two options for table splitting.
a primary key to be defined in your Bar class.
public class Bar
{
[Key]
public int Something { get; set; }
public int SomethingElse { get; set; }
}
Related
I'm trying to implement a relationship(one-to-many) for my entities which use default TPH inheritance
public abstract class base
{
public int Id { get; set; }
...
}
public class X : base
{
public ApplicationUser User { get; set; }
...
}
public class Y : base
{
public ApplicationUser User { get; set; }
...
}
public class ApplicationUser
{
public string Name { get; set;}
...
public ICollection<X> classX { get; set; }
public ICollection<Y> classY { get; set; }
}
Everything works, but the problem is that Entity Framework creates two columns in the base table - User_Id and User_Id1. How can I map it so that there is only one column for the foreign key (User_Id) and depending on the content of the record in the Discriminator column (created by EF) the foreign key would be assigned to the appropriate entity?
How can I map it so that there is only one column for the foreign key (User_Id) and depending on the content of the record in the Discriminator column (created by EF) the foreign key would be assigned to the appropriate entity?
You can't. Thinking about it a bit I don't see any obvious reason why that feature couldn't be implemented. It just hasn't.
If you want X and Y to share an attribute, derive them both from and intermediate XYEntity type, and give ApplicationUser a single Navigation Property of type ICollection<XYEntity>.
In my DTOs, I send Ids instead of entire objects to assign/relate one object to another. The problem is my mapping code will need access to the database to handle this because my entity classes don't have BarId property.
public class FooDTO
{
public int FooId { get; set; }
public int BarId { get; set; }
}
public class Foo
{
public int FooId { get; set; }
public Bar Bar { get; set; }
}
This can probably be solved by adding additional BarId property to my entity class, that way I don't couple data access with my mapper.
But the question arises: if bar with specified id doesn't exist can this be handled in some reasonable way to return the custom error message?
public class Foo
{
public int FooId { get; set; }
public int? BarId { get; set; }
public Bar Bar { get; set; }
}
Is it fine to access database in my mapping code and handle these assignments manually or is it better to leave it to the ORM by explicitly adding a foreign key property (BarId) in my entity class?
Also see: https://learn.microsoft.com/en-us/ef/core/modeling/relationships#no-foreign-key-property
While it is recommended to have a foreign key property defined in the dependent entity class, it is not required.
It seems like adding foreign key property is recommended so I guess I will choose this route.
no, not fine. if you are accessing the db this is most definitely not mapping code.
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.
I am new to ASP.Net MVC and I am trying to get a better understanding of ASP.Net MVC. I did a couple tutorials and made a few models in those tutorials. One question that kept popping up in my head was: When would I use public int Id { get; set; } and when would I be using public int MyClassNameId { get; set; } instead as identifier for my model class? Would it matter if I would use a custom property name instead of the default Id name for my identifier for a model class?
For example, why would I use public int ArtistId { get; set; } over public int Id { get; set; }?:
public class Artist
{
public int ArtistId { get; set; }
public string Name { get; set; }
}
Is it so that it matches a property name in another class in which it will be used as Foreign Key?
Entity Framework CodeFirst recognize the key, by default, by name. Valid names are Id or <YourClassName>Id.
Your property should be named Id or AccountTypesId
Another way is to use the ModelBuilder to specify the key.
Sample
public class MyDbContext : DbContext
{
public DbSet<Artists> Artists{ get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Artists>.HasKey(x => x.ArtistId);
base.OnModelCreating(modelBuilder);
}
}
More about it you can find here
If you use custom property names then compiler will not understand it's meaning that it is an id and must be used as primary key in database table.
when you name it id compiler understands it's meaning .
This depends on whether or not using Entity Framework to set up your databases. If you are Entity Framework looks for specific property names to identity as Primary Keys.
For example, let's say you have a model called Book.
public class Book
{
public string Title {get; set;}
//all other properties here
}
When Entity Framework tries to set up your database, it looks for a property that it can identify as a primary key corresponding to the specific model. In this case EF would look for "BookID" as a primary key. So if you wished to have an accessible primary key property you would set it up like this.
public class Book
{
public int BookID {get;set;}
public string Title {get; set;}
//all other properties here
}
If you wished to set up a primary key that was not called "BookID", you could use a data annotation:
public class Book
{
[Key]
public int BookIdentifier{get;set;}
public string Title {get; set;}
//all other properties here
}
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