Define foreign key in Entity Framework only with Id property - c#

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("...");

Related

Entity framework: Foreign entity without requiring foreign entity to actually exist entity to exist

I have an audit-tracking like system, that contains the following two entities:
The JobCreate entity:
public class JobCreate
{
[Key] public string JobId { get; set; }
public List<AffectedEntity> AffectedEntities { get; set; }
}
And the AffectedEntity entity:
public abstract class AffectedEntity
{
[Required]
public string JobId { get; set; }
public int Id { get; set; }
[CanBeNull] public JobCreate Job { get; set; }
}
So far this is just a normal foreign key relation:
modelBuilder.Entity<JobCreate>()
.HasMany(j => j.AffectedEntities)
.WithOne(a => a.Job)
.HasForeignKey(a => a.JobId)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
Entity Framework generates a foreign key for this relationship. My problem with this is that this audit system is event driven, which means it receives the events that creates the AffectedEntity and the event that creates the JobCreate entries out of order. In other words, the JobCreate entity might not yet exist when the AffectedEntity is created. However as far as the domain goes, this is actually fine. So how do I model that in Entity Framework? I want to be able to "navigate" along that connection from JobCreate to AffectedEntity, however the other direction is not necessary.
the JobCreate entity might not yet exist when the AffectedEntity is created. However as far as the domain goes, this is actually fine. So how do I model that in Entity Framework?
Just have the relationship in the EF model, but omit it or set the FK to not be enforced in the back-end. EG in SQL Server you would set the Foreign Key Constraint to NOCHECK.
Just beware that EF may assume that the FK is enforced when it creates queries. EG if you query db.AffectedEntities.Inclue("JobCreate") it may use an INNER JOIN and not return any AffectedEntities without a JobCreate.
And if you need to deal with AffectedEntities with a null JobID, you'd have to change the data type to int?.

Entity Framework, Create 0-1 relation with existed entity

I have an existing entity User. Now I am trying to create a new entity Contact with 0-1 relation with User.
class Contact
{
public int Id {get; set;}
public string Name {get; set;}
public int? UserId {get; set;}
public virtual User TheUser{get; set;}
}
All suggestion involve about something like this:
modelBuilder.Entity<User>()
.HasOptional(t => t.TheUser)
.WithOptionalDependent(u => u.TheConatct);
But this means we have to add TheConatct property to the existed User entity. Actually I do not want to make any modification to the existed entity. All what I need to define a foreign key form Contact to User entity and can access the User entity from Contact via TheUser property.
Update:
If I use ForeignKey attributes to annotate the property:
class Contact
{
public int Id {get; set;}
public string Name {get; set;}
public int? UserId {get; set;}
[ForeignKey("UserId")]
public virtual User TheUser{get; set;}
}
Then, the result of ObjectContext.CreateDatabase() will also include create statements for already existed tables (depending on the entities that have relations with User).
Of course we are talking about "Entity Framework 6 Code First", Also, I have the same problem with 1-1 relation.
The idea, I cannot alter the existing entity User to add additional property for the new entity Contact
I wonder if there is a way to overcome this issue
Just use another overload:
modelBuilder.Entity<User>()
.HasOptional(t => t.TheUser)
.WithOptionalDependent();
I don't know which version of Entity Framework you are using and I assume you are using Code First, but you may have to consider using a one to many relationship instead of a 0-1.
I don't believe there is support for 0-1 in the way you want it, but you can simulate by having a one to many (even though your "many" will only ever by 1)

multiple added entities may have the same primary key on database seed

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.

What's the difference when I create Code First relation setting only a collection

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.

EF4.1 (code first) - How to specify a composite relationship

In Linq to SQL I could specify a relationship that didn't have to depend on the foreign keys and pks existing in the database, useful for creating composite relationships like this:
public class Equipment_CableNormalised
{
...
[Association(ThisKey = "EquipmentId,PortNumber", OtherKey = "EquipmentId,PortNumber", IsForeignKey = false)]
public List<EquipmentPort> EquipmentPorts
{
get; set;
}
}
This then generated the sql similar to " .. join EquipmentPorts EP on EP.EquipmentId = blah and EP.PortNumber = Blah".
Can I do the same sort of thing in EF4.1 (using annotations or fluent api)? I know you can specify composite keys and use the [Keys] and [ForeignKeys] attributes, but this relationship doesn't map to keys...
How does the sample relation from your code works? I expect that EquipementId must be either PK or unique key (not supported in both L2S and EF) on one side because otherwise the relation could not exist (both one-to-one and one-to-many demands unique principal). Once it is PK on one side the port number is redundant.
Code first allows only mapping to keys. If you have existing database you can cheat it in your model and map new relations in the same way as you would map existing but you still have to follow simple rule - properties in principal are primary keys, properties in dependent entity are mapped as foreign keys.
If you want EF to generate DB for you, you will always have all relations in the database.
Use HasKey http://www.ienablemuch.com/2011/06/mapping-class-to-database-view-with.html
Either use HasKey, put this on OnModelCreating
 modelBuilder.Entity<SalesOnEachCountry>().HasKey(x => new { x.CountryId, x.OrYear });   
Or use Key Column Order
public class SalesOnEachCountry
{       
    [Key, Column(Order=0)] public int CountryId { get; set; }
    public string CountryName { get; set; }
    [Key, Column(Order=1)] public int OrYear { get; set; }
     
    public long SalesCount { get; set; }     
    public decimal TotalSales { get; set; }
}
Regarding your question about foreign key, I haven't yet tried the pure code(OnModelCreating) approach, perhaps you can just put two ForeignKey attribute on child class itself, might need to put Column Order too.
This could be the answer composite key as foreign key
That answer confirms my hunch that you could put two ForeignKey attributes on child class itself.

Categories