assuming I have the following model structures for an asp.net mvc 5 app using entity framework 6
class Athlete {
int AthleteID {get; set;}
List<YearsAsAthlete> YearsAsAthlete {get;set;}
}
class YearsAsAthlete {
int YearsAsAthleteID {get;set;}
int AthleteID {get;set;}
[ForeignKey("AthleteID")]
Athlete Athlete {get;set;}
List<ContractRevenue> ContractRevenue {get;set;}
List<AdvertisementRevenue> AdvertisementRevenue {get;set;}
}
class ContractRevenue {
int ContractRevenueID {get;set;}
int YearsAsAthleteID {get;set;}
[ForeignKey("YearsAsAthleteID")]
YearsAsAthlete YearsAsAthlete {get;set;}
List<RevenueAmounts> RevenueAmounts {get;set;}
}
class AdvertisementRevenue {get;set;}
int AdvertisementRevenueID {get;set;}
int YearsAsAthleteID {get;set;}
[ForeignKey("YearsAsAthleteID")]
YearsAsAthlete YearsAsAthlete {get;set;}
List<RevenueAmounts> RevenueAmounts {get;set;}
}
class RevenueAmounts {
int RevenueAmountsID {get;set;}
int AmountPaid {get;set;}
date DateOfPayment {get;set;}
}
These models work fine when I have them like this, they have relationships and everything is delicious like a hot fudge sundae. When I run this, the database creates these tables and the RevenueAmounts table get 2 auto-generated foreign key columns for ContracRevenue and AdvertisementRevenue.
However, I don't want these as they're named strangely (ContractRevenue_ContractRevenueID) and I need some way to access the foreginkey id property in my post controller method for adding new values that correlate with the right type of revenue.
When I change the RevenueAmounts model to the following:
class RevenueAmounts {
int RevenueAmountsID {get;set;}
int AmountPaid {get;set;}
date DateOfPayment {get;set;}
// ***NOTE adding foreign keys here
int ContractRevenueID {get;set;}
[ForeginKey("ContractRevenueID")]
ContractRevenue ContractRevenue {get;set;}
int AdvertisementRevenueID {get;set;}
[ForeignKey("AdvertisementRevenueID")]
AdvertisementRevenue AdvertisementRevenue {get;set;}
}
I start getting an exception:
[SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_dbo.AdvertisementRevenue_dbo.YearsAsAthlete_YearsAsAthleteID' on table 'AdvertisementRevenue' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
** EDIT **
I've turned off the cascading delete functionality using the fluent API however now I'm getting a different exception:
Unable to determine the principal end of the 'GIP.DAL.ContractRevenue_RevenueAmounts' relationship. Multiple added entities may have the same primary key.
By the way, the exception is being thrown when I'm trying to seed a bunch of info into the database and then doing context.SaveChanges() at the bottom (only doing it once at the end)
In your edit, you mention the 'Multiple added entities may have the same primary key.' error. Without knowing all of the details of what you are doing here, it sounds like you are creating a relationship with an entity - of which there are two in the context with the same ID. These are probably new entities which have not yet been saved which is where they get an automatically generated ID from the database. If the relationship is based on the ID then there is some ambiguity because Entity Framework is unable to determine which of the new entities the relationship is actually pointing to - they both have the ID that the relationship is pointing to.
There are two potential fixes.
Generate a temporary, unique identifier for entities as they are created in the context. Entity Framework will discard this as the entity is saved but up until that point, it can use it to tell one new entity apart from the other. I have used negative integers for this purpose in the past.
Do not create the relationships using IDs but rather on entity references. If Entity Framework has a direct reference to the entity, then it does not need to go through the process of identifying the entity based on non-unique identifiers and should not have this problem.
This is happening because Entity Framework cannot determine which object within the relationship is the parent when Cascade delete is enabled.
Are you using Code First? When the Migration is generated you will see an option for cascade delete in the table declaration. This should be resolved if you set that to false.
However the bigger issue is that you are creating object relationships that doesn't implement an Aggregate Root which most often would avoid this issue.
I had the same problem but in my case there was another solution.
In a loop i was adding parents
for each parent I was adding children. There is relation between parent and child. Of course I used the FK as a virtual reference column but still the problem appeared.
foreach (var parent in parents)
{
var id = parent.Id;
var parentEntity = new Parent();
this.Context.Set<Parent>().Add(parentEntity);
parentEntity.CreatedOn = DateTime.UtcNow;
...
foreach (var child in parents[id].Children)
{
var childEntity = new Child();
//this.Context.Set<Child>().Add(childEntity);
parent.Children.Add(childEntity);
childEntity.CreatedOn = DateTime.UtcNow;
...
}
}
I had to comment out the line that was adding the child entity to EF context and it fixed the issue.
Related
Im utilsing a code first approach for the first time (Ive previously always used database first) and am trying to understand some basic concepts. If I create a foreign key relationship between two entities, how does entity framework know which properties (columns) to use in the two sides of the relationship ? My question is probably better explained with a simple code example. I have two entities, patient and treatment. A patient can have multiple treatments so there will be a one to many relationship between the patient and the treatment, with a foreign key relationship existing between the two entities. Here are my entity classes. Please note these are greatly simplified for the sake of explanation.
public class Patient
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PatientTreatment> PatientTreatment { get; set; }
}
public class PatientTreatment
{
public int Id { get; set; }
public string TreatmentDescription { get; set; }
public int PatientId { get; set; }
public virtual Patient Patient { get; set; }
}
So for the patient entity the primary key would be Id and for the PatientTreatment entity, its primary key would also be Id
For the foreign key relationship, according to what Ive googled so far, the code above will create that relationship for me, is this correct ? If so, how would entity framework know that the PatientId in PatientTreatment is linked to Id in the Patient entity ? This is how its supposed to be in the database (SQL Server), but I cant see how entity framework would know this. Im really new to the code first approach so Im just trying to understand how this would work. Could anyone explain this to me ?
Ive also read that setting the relationship as above doesnt create indexes (PatientId in PatientTreatment) so these have to be created in code as well
EF works with conventions, as Caius mentioned.
In your case:
EF knows that there are two entity object - Patient and PatientTreatment, because dbSet and optional configuration exist for those classes.
Patient contains so called navigation property leading to PatientTreatment's - a collection, but it could be most of the things implementing IEnumerable - EF assumes that You want to create relationship here.
Patient have an Id field - EF by naming convention without any configuration will assume that this is an entity key. Same goes for PatientTreatment
PatientTreatment has a navigation property to a single Patient - this, again, by convention tells EF that you want the relationship between this two entities to be one-to-many - collection on one side, single reference on the other side.
Ofc one to many could also be possible by convention even without navigation property in PatientTreatment - just to be clear.
Is it possible to flatten a two-table relationships into a single entity in Entity Framework?
Specifically, (simplified for example) given the following two tables that define a 1-1 relationship
create table Foo
(
Id int not null identity (1, 1)
constraint PK_Foo_Id primary key (Id),
Name nvarchar(64) not null,
BarId int not null
constraint FK_Bar_Foo foreign key (BarId) references Bar (Id)
)
create table Bar
(
Id int not null identity (1, 1)
constraint PK_Bar_Id primary key (Id),
Value nvarchar(max) not null
)
I can easily map this to entities like this
public class Foo
{
public int Id { get; set;}
public string Name { get; set;}
public Bar Bar { get; set;}
}
public class Bar
{
public int Id { get; set;}
public string Value { get; set;}
}
But what I would like to map to a single flattened entity
public class FlatFoo
{
public int Id { get; set;}
public string Name { get; set;}
public string Value { get; set;}
}
Notice that only one field from table Bar is mapped to FlatFoo
Notes
The actual tables are larger.
Since the text value in Bar can get large it would fill index pages quickly, so there are two tables for quicker index searches against Foo.Id and Foo.Name.
I have looked into Split Entities, but it required both tables have the same primary key.
I have looked at Complex Types but it works in the opposite manner taking a flat table and splitting into composite entities.
I am looking to use the Fluent API to perform the mapping.
Can you provide any help in flattening the mapping between two tables and a single entity?
Update
Yes, views will work to get a flat entity, but then I am not mapping from tables to entity. Likewise, from the other side, I know it is possible to map to non-public composition and expose the property that way. But, I am more interested in learning if EF fluent API is flexible enough to handle the mapping directly than I am in solving a particular issue.
Unfortunately, there is considerable push-back here (at work) to any suggestion of adding anything other than tables to a database (something as basic as views included). It is typically pointed out that doing so adds additional point of maintenance, increases training for support, adds complexity for basic CRUD and other excuses for not learning the tools available. It is silly at best, but it is something I have to deal with. :(
So, as a point of learning for me, is it possible to do this seemingly basic task of directly mapping fields from two arbitrary tables into one entity using EF, fluent API preferred?
Entity Framework doesn't provide a way to map one entity to two tables and then cherry pick from the columns in the way you describe unless the tables share a common key. So as mentioned in the comments, the simplest solution is to create a View and map the entity to that.
public class FlatFooMap : EntityTypeConfiguration<FlatFoo>
{
public FlatFooMap ()
{
ToTable("vwFlatFoo");
HasKey(t => t.Id);
}
}
I am struggling with Entity framework. The thing I want to acheive is to have an entity with only Id which will define a foreign key without actual reference to the related entity. How can it be achieved?
Class.cs:
public class Class {
Guid Id {get; set} // this is my primary key
Guid ProfessorId {get; set; } // this has to be id of professor which is a foreign key
}
Professor.cs
public class Class {
Guid Id {get; set} // this is a primaty key of professor and it should constraint ProfessorId
}
I'm using fluent api. I cannot add any virtual properties to above classes (basically i cannot modify them), so how the mapping should be configured?
I understand that you cannot modify entities but you need to add at least one navigation property. It is necessary for Entity Framework in order that it understand the relationship.
In your code ProfessorId is only a scalar property.
The only other way is to execute sql command directly from your context
dbContext.Database.ExecuteSqlCommand("...");
I have a class with the following properties
public class Booking
{
public long BookingId {get;set;}
public string RoomNumber {get;set;}
[ForeignKey("BookingCustomer")]
public long? BookingCustomerId {get;set;}
public virtual Customer BookingCustomer {get;set;}
}
public class Customer
{
public long CustomerId {get;set;}
public string FirstName {get;set;}
}
if in a method I reference properties of the customer class am getting object null reference exception while BookingCustomerId is populated.i.e.,
hotel.BookingCustomerId=2
For instance,
string customerFirstName = hotel.BookingCustomer.FirstName;
if I peek at the hotel.BookingCustomer i get null
How do I go about this Lazy Loading?
If the related entity is coming back as null this means the relationship as understood by entity framework can't find any related entities.
It appears you are using data annotations to flag properties with properties such as foreign keys. You may also need to flag primary keys with the [key] attribute.
You will also need to add a related booking entity to your customer data.
Alternatively you can you the fluent api to do the following in your context.
// Configure the primary key for the Booking
modelBuilder.Entity<Booking>()
.HasKey(t => t.BookingID);
modelBuilder.Entity<Booking>()
.HasRequired(t => t.customer)
.WithRequiredPrincipal(t => t.booking);
More on the fluent a picture here:
https://msdn.microsoft.com/en-us/data/jj591620.aspx#RequiredToRequired
Lazy loading implies that the related objects are retreived when the getter of that object is used for the first time.
At just that time a query to the database is executed to retreive for that object ,for example Hotel.BookingCustomer.
Try to see if the query is indeed executed e.g. with Sql Server profiler.
Examine the query to makes sure everything is correct
If you can't see the query triggered, try to it without the virtual keyword (eager loading) and see if it's working then.
I am using Entity Framework 5 with Code First. I update my model(entities) while I'm writing the business logic which leads to some problems. Since now when I wanted to create 1 : N relationship I was using this approach:
Entity-1
{
public int Entity-1ID { get; set; }
public virtual ICollection<Entity-N> Entity-Ns { get; set; }
}
and
Entity-N
{
public int Entity-NID { get; set; }
public int Entity-1ID { get; set; }
public virtual Entity-1 Entity-1 { get; set; }
}
bur recently I faced the problem with the need of null Foreign Keys (I needed to add more relations to some entities) and since I am already using GenericRepository where all types are value types (no nullables) and also have some code written based on that I decided that it's too late to change all this.
Since I already have Foreign Key for some entity and I need to relate that entity with a new one I have faced a problem.
My solution is - when I have a new entity I only add collection from the existing entity to the new one (or vice-verca). What bothers me is - what kind of problems this may cause if I use it.
Right now I have entity Page where
Page
{
public virtual ICollection<SomeEntity> SomeEntities { get; set; }
}
and as expected in SomeEntity I don't have a FK for Page but when I look at the Microsoft SQL Management Studio I see that the SomeEntities table has a column called Page_PageID.
Can someone explain me what exactly happens when I make a relation like this. Why even though I have this column Page_PageID which practically acts as FK I can't use it from my code and why if I explicitly define public int PageID { get; set; } in SomeEntitiy and I try to change int to int? I get all kinds of errors for trying to use null but with this auto-created column Page_PageID there's no problem to have records with null values for it.
Your question:Can someone explain me what exactly happens when I make a relation like this?
Page
{
public virtual ICollection<SomeEntity> SomeEntities { get; set; }
}
In Code First,if there is a collection property between two entities,Entity Framework will create a one-to-many relationship.That means the entity has collection property is the principal end of the relationship,the other entity is the dependent end.And,in the table of the dependent entity,Entity Framework will generate a foreign key to the principal table.
So,The collection property "SomeEntities" of entity Page will cause the Entity Framework to generate a foreign key in the table of entity "SomeEntity".
There are three scenarios that Code Frist will treat as a one-to-manay relationship between to entities.
1.There is a reference navigation property in one entity.
2.There is a collection navigation property in one entity(Your Page entity belong to this).
3.There is a reference navigation property in one entity,and a collection navigation property in the other entity.
Your question:Why even though I have this column Page_PageID which practically acts as FK I can't use it from my code .
If you want to use the foreign key,you should define a foreign key property in entity "SomeEntity".By default,Entity Framework generate a foreign key with the below patterns:
[Target Type Key Name],[Target Type Name] + [Target Type Key Name],or [Navigation Property Name] + [Target Type Key Name].that is why your foreign key named "Page_PageID".You can use the annotation "ForeignKey".
Your question: why if I explicitly define public int PageID { get; set; } in SomeEntitiy and I try to change int to int? I get all kinds of errors for trying to use null but with this auto-created column Page_PageID there's no problem to have records with null values for it..
By default,if the primary key is value types,the foreign key to it will be not null.And that means the relationship is required.
I can not see the key of your Page entity.If it's type is int,the auto-created column Page_PageId(foreign key to Page) will be not null too.Did you show your whole Page entity?
If you did not define a foreign key(like public int PageId) in SomeEntity,code first will generate a foreign key named with pattern [Target Type Name]_[Target Type Key Name],that is Page_PageId.And in this case(there is no foreign key property in the dependent entity),the foreign key generated by Entity Framework in silence will be null.
But if you explicitly define a foreign key property,like public int PageId,the nullability of the foreign key in the database will be determined by the type of the foreign key property(value types is not null).
So,like your question,when you with the auto-created FK column Page_PageID,the FK is null,you can insert leave it null.When you explicitly defined a foreign key property,public int PageID { get; set; },the FK PageId will be not null,because PageId is integer.
Some more,you can control the nullability of FK int the database by specifying the PageId as int?.Again,By convention, Code First is using the nullability of the foreign key
property in your class to determine if the relationship is required or optional.