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.
Related
I have two classes, Person and Event. Multiple people can sign up for an event and is coded as a foreign key list. My question is what is the correct way of adding an additional variable such as a paid bool or something? I imagine I will have to make a separate class with a foreign key link on both Person and Event but I wanted to make sure there wasn't some fancy way of adding an additional property.
If Person have many events then i would have structure my person class like below
public class Person
{
public int Id { get; set; }
// each person have many events
public List<EventRelation> Events { get; set; }
}
public class EventRelation
{
public int Id { get; set; }
public Events Event { get; set; }
// Person Id forgen Key
public int Person_Id { get; set; }
// Events Id forgen Key
public int Event_Id { get; set; }
}
public class Events
{
public int Id { get; set; }
public string EventType { get; set; }
}
Yes, you are doing it the right way. Since it's an N to N relationship, it needs a joining entity as you specified. The joining entity would have two primary (foreign key) properties including PersonId and EventId and other optional properties (in your case Paid).
Update:
Regarding looking for a fancy way, the answer is No. When designing a database, you are limited to the data model you are using which is relational data model here. EF and other kinds of ORMs are just tools for communications between the app and database.
However, if you define one foreign key in Person class with the type of Collection<Event> and another in Event class with the type of Collection<Person>, entity framework will automatically create the joining table with the two foreign keys. But it's not what you desire, because you need to add more properties to the joining table.
you can always add new properties into your class, execute migration script and then update database script in order to synchronise Entities with Database.
class Person
{
public int Id {get; set;}
}
suppose you add DateOfBirth
class Person
{
public int Id {get; set;}
public DateTime DateOfBirth {get; set;}
}
then
dotnet add migration 'BirthDate'
dotnet ef database update
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.
I need to implement Entity-Attribute-Value functionality on multiple data tables using Entity Framework. Let's say I have an attribute value EF class that looks like this:
public class EntityAttributeValue
{
// Not important to my question.
public virtual Entity ParentEntity { get; set; }
public virtual EntityAttribute ParentEntityAttribute { get; set; }
// Field in question.
public Guid ParentSurrogateKey { get; set; }
public string Value { get; set; }
...
}
Then I have multiple entities that have supplementary EAV values associated with them:
public class Entity1
{
// Key. EntityAttributeBalue.ParentSurrogateKey maps to this.
[Key]
public Guid SurrogateKey { get; set; }
// Standard properties.
public string Property1 { get; set; }
public string Property2 { get; set; }
// Collection of EAV values associated with this entity/table.
[ForeignKey("ParentSurrogateKey")]
public virtual IList<EntityAttributeValue> EntityAttributeValues { get; set; }
}
public class Entity2
{
// Key. EntityAttributeBalue.ParentSurrogateKey maps to this.
[Key]
public Guid SurrogateKey { get; set; }
// Standard properties.
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
// Collection of EAV values associated with this entity/table.
[ForeignKey("ParentSurrogateKey")]
public virtual IList<EntityAttributeValue> EntityAttributeValues { get; set; }
}
My problem is that both Entity1 and Entity2 have EntityAttributeValue objects associated with them. Code first migrations tries to create a foreign key from EntityAttributeValue back to Entity1 and another one back to Entity2 on ParentSurrogateKey. The surrogate key for any single given EntityAttributeValue is only associated with either one Entity1 or one Entity2 (or, expanding out, one EntityN...), not both/all.
I have a many to many relationship here, but one side not only maps to multiple rows, but multiple entities/tables over a shared GUID column.
How should I be approaching this? Should I just remove the EntityAttributeValue foreign keys back to Entity1 and Entity2 from the automatic migration (which would be a long term pain)? Should I be manually retrieving the list of EntityAttributeValues for a given EAV entity instead of relying on EF to do it for me?
Well, the answer turned out to be obvious and simple. I needed to define a many-to-many relationship with FluentAPI. In OnModelCreating, I just added:
modelBuilder.Entity<Entity1>()
.HasMany(m => m.EntityAttributeValues)
.WithMany();
modelBuilder.Entity<Entity2>()
.HasMany(m => m.EntityAttributeValues)
.WithMany();
I thought I had tried this, but I guess I hadn't. Because the many-to-many relationship creates an intermediate table for each entity and the foreign keys are on that intermediate table (and there is only a row in the intermediate table when a given EntityAttributeValue applies to a given Entity), no foreign key issues.
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.
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