EF6 object relationships - c#

I have a database, which I cannot modify because it is from an external application. The database contains tables that are linked, but not via the key - foreign key structure. Simplified, the structure looks like this:
Table - Warehouse
ID (Key)
WarehouseCode
Description
Table - Location
ID (Key)
LocationCode
Description
WarehouseCode
As you can see, the two tables can be linked via the WarehouseCode, but I don't understand how I can do that in EF6.

Edit: Vol 2.
It seems that you will have to wait until EF7
• Support for specifying a foreign key associations that on the principal end specify columns(s) that comprise a unique constraint but are not the primary key,
It Seems there no way entity framework can relate ONLY one warehouse to a Location since there is no guarantee that there will be only one warehouse with that Warehouse Code.
References
1
2
So a workaround might be to establish a many to many relationship using an extra table only for this.
public class Warehouse
{
public Int32 Id { get; set; }
public String WarehouseCode { get; set; }
public String Description { get; set; }
[InverseProperty("LocationWarehouses")]
public virtual ICollection<Location> LocationsWithThisWarehouse{ get; set; }
}
public class Location
{
public Int32 Id { get; set; }
public String LocationCode { get; set; }
public String Description { get; set; }
public String WarehouseCode { get; set; }
[InverseProperty("LocationsWithThisWarehouse")]
public virtual Icollection<Warehouse> LocationWarehouses { get; set; }
}
public class YourContext: DbContext
{
public DbSet<Location> Locations { get; set; }
public DbSet<Warehouse> Warehouses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer < YourContext > (null);
modelBuilder.Entity<Warehouse>().ToTable("Warehouse", "SchemaName");
modelBuilder.Entity<Location>().ToTable("Location", "SchemaName");
modelBuilder.Entity<Warehouse>().HasMany(g => g.LocationsWithThisWarehouse).WithMany(t => t.LocationWarehouses).Map(m =>
{
m.MapLeftKey("WarehouseCodeOnWarehouse");
m.MapRightKey("WarehouseCodeOnLocation");
m.ToTable("WarehouseAtLocation");
});
}
}

Related

Foreign Key creation in EF Core code first

I have 2 models:
public class GaOrgOrders
{
public virtual Guid Id { get; set; }
[ForeignKey("GaOrganizations")]
public virtual Guid OrgId { get; set; }
[ForeignKey("GaApps")]
public virtual Guid AppId { get; set; }
[ForeignKey("GaOrgUserOrganizations")]
public virtual Guid OrgUserId { get; set; }
[ForeignKey("GaServicesTariffs")]
public virtual Guid ServiceTariffId { get; set; }
public virtual bool IsTemporary { get; set; }
public virtual Apps GaApps { get; set; }
public virtual Organizations GaOrganizations { get; set; }
public virtual OrgUserOrganizations GaOrgUserOrganizations { get; set; }
public virtual GaServicesTariffs GaServicesTariffs { get; set; }
}
and
public class GaOrganizations
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual bool IsDeleted { get; set; }
}
When I try to update database and add these tables I get an error:
Introducing FOREIGN KEY constraint 'FK_GaOrgOrders_GaOrganizations_OrgId' on table 'GaOrgOrders' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could not create constraint or index. See previous errors.
If I remove "ForeignKey" attribute from OrgId property - error disappears, but it creates in database a second field(GaOrganizationsId) and makes it a FK. Other Keys work fine. What's wrong with OrgId?
EF doesn't understand the relationship between the two entities so you might need to clarify using fluent API in your "OnModelCreating" method.
Using something like
modelBuilder.Entity<ClassB>().HasRequired(x => x.ClassA).WithOptional(x => x.ClassB);
where ClassA and ClassB are my entities having a 1 to 0 or 1 relationship between them.

Creating new table with foreign key with Entity Framework Core

I have a DbContext which I via the developer command prompt and creating a migrations schema turn in to my database. But if you look at the product object I have a dictionary object named Parts. That property does not get added to the Product table when the database is updated in the command prompt. I don't even know if it is possible what I am trying to do.
I want to add a table in the database named Parts and then add a foreign key to the Product table which connects the Parts dictionary object in the Product table, and the the new Parts table. Is this possible with Entity Framework Core?
public class ShoppingDbContext : IdentityDbContext<User>
{
public ShoppingDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Price { get; set; }
public int CategoryId { get; set; }
Dictionary<string, Part> Parts { get; set; }
}
EF Core can't currently map a dictionary property directly. If you want to create an association between Products and Parts, then define each of them as an entity. You can then create navigation properties between them--a reference from Part to the Product which it belongs, and a collection of Parts on Product. For example:
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Price { get; set; }
public int CategoryId { get; set; }
public ICollection<Part> Parts { get; set; }
}
public class Part
{
public int PartId { get; set; }
public int ProductId { get; set; }
public Product Product { get; set;}
}
Part also defines a property ProductId that acts as the FK to the Product entity. You don't need to add that property--EF will simulate it for you if you don't want it, but usually it is easier to deal with entities if the FK is mapped to a property.
Relationships are tracked through object references instead of foreign key properties. This type of association is called an independent association.
More Details Here:
https://msdn.microsoft.com/en-us/data/jj713564.aspx
Sample code:
public partial class Product
{
public Product()
{
this.Parts = new HashSet<Part>();
}
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Price { get; set; }
public int CategoryId { get; set; }
public virtual ICollection<Part> Parts { get; set; }
}
Basically like what Arthur said, EF Core does not support it yet.
However, another way is to create a composite table should you want to or if it's viable for your use.
Here's a simple example:
// -------------- Defining BrandsOfCategories Entity --------------- //
modelBuilder.Entity<BrandCategory>()
.HasKey(input => new { input.BrandId, input.CatId })
.HasName("BrandsOfCategories_CompositeKey");
modelBuilder.Entity<BrandCategory>()
.Property(input => input.DeletedAt)
.IsRequired(false);
// -------------- Defining BrandsOfCategories Entity --------------- //
public class BrandCategory
{
public int CatId { get; set; }
public int BrandId { get; set; }
public DateTime? DeletedAt { get; set; }
public Category Category { get; set; }
public Brands Brand { get; set; }
}
The DeletedAt is optional of course. This handles M-M Relationships.
I had the same issue, I resolved it by removing the keyword virtual on the navigation properties and with in the ApplicatinDbContext

Introducing FOREIGN KEY constraint key on table table may cause cycles or multiple cascade paths. Specify ON DELETE ... Error

i was trying to run the Update-Database command in Nugget Package Manager console but wasnt successful as i kept getting the error
Introducing FOREIGN KEY constraint 'FK_dbo.TeamToLeaders_dbo.Teams_TeamId' on table 'TeamToLeaders' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could not create constraint. See previous errors..
I want to set up relationship in which there is a class called Team.cs that contains the below properties
public class Team
{
public int TeamId { get; set; }
public string TeamName { get; set; }
public Decimal MonthlyTarget { get; set; }
public ICollection<SalesAgent> Agents { get; set; }
}
which means a team has many Agents and there is another class called SalesAgent.cs which contain info about agents
public class SalesAgent
{
[Key]
public int AgentId { get; set; }
public string AgentFirstName { get; set; }
public string AgentLastName { get; set; }
public string HomeAddress { get; set; }
public bool IsActive { get; set; }
public string AgentPhone { get; set; }
public Decimal MonthlyTarget { get; set; }
public int TeamId { get; set; }
public virtual Team Team { get; set; }
}
Now i want a class which i would be able add the relationship between a team and an agent i.e in essence i want to be able to assign a team leader to each team so i set up the class below
public class TeamToLeader
{
[Key]
public int TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int TeamId { get; set; }
public int AgentId { get; set; }
public virtual Team Team { get; set; }
[ForeignKey("AgentId")]
public virtual SalesAgent Agent { get; set; }
}
Upon running "Update-Database Command" I get an error that The ForeignKeyAttribute on property 'AgentId' on type 'SalesForce.Models.TeamToLeader' is not valid. The navigation property 'SalesAgent' was not found on the dependent type 'SalesForce.Models.TeamToLeader'. The Name value should be a valid navigation property name.
So i changed the model to
public class TeamToLeader
{
[Key]
public int TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int TeamId { get; set; }
[ForeignKey("SalesAgent")]
public int AgentId { get; set; }
public virtual Team Team { get; set; }
public virtual SalesAgent Agent { get; set; }
}
and that resulted in this error
Introducing FOREIGN KEY constraint 'FK_dbo.TeamToLeaders_dbo.Teams_TeamId' on table 'TeamToLeaders' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
Help please.
You should diasble OneToManyCascadeDeleteConvention to force EF not to use cascade delete. In DbContext add:
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
...
Or you can make foreign keys nullable:
public class TeamToLeader
{
[Key]
public int? TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int? TeamId { get; set; }
[ForeignKey("SalesAgent")]
public int AgentId { get; set; }
public virtual Team Team { get; set; }
public virtual SalesAgent Agent { get; set; }
}
Depends which behavior you prefer.
You can also use fluent API:
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TeamToLeader>().HasRequired(i => i.Agent).WithMany().WillCascadeOnDelete(false);
}
...
Note that your model Team has many SalesAgent and many TeamToLeader.
There should be TeamToLeaders collection in your Team and SalesAgent model :
...
public virtual ICollection<TeamToLeader> TeamToLeaders { get; set; }
...
I'm not sure if you need Team to many SalesAgent relation anymore.
As this link, and this link saids...
It is theoretically correct but SQL server (not Entity framework) doesn't like it because your model allows single employee to be a member of both First and Second team. If the Team is deleted this will cause multiple delete paths to the same Employee entity.
SQL server doesn't allow multiple delete paths to the same entity.
This link said that it can be solved by disabling OneToManyCascadeDeleteConvention and ManyToManyCascadeDeleteConvention, but those deleting operations SHOULD BE done by codes manually.

EF 6 how to set two foreign keys to same table

I have a table UserForms that has two foreign keys to a Countries table, but on creating my controller and create view (for the UserForms model) the two fields linking to the foreign keys do not appear. What should I do to sort this problem? Below are the two models:
public class UserForms
{
public int Id { get; set; }
public string FullNames { get; set; }
public Countries IndividualsCountry { get; set; }
public Countries BusinessCountry { get; set; }
}
public class Countries
{
public Countries()
{
this.STRBusinessCountry = new HashSet<UserForms>();
this.STRIndividualsCountry = new HashSet<UserForms>();
}
public int Id { get; set; }
public string NameOfCountry { get; set; }
[InverseProperty("IndividualsCountry")]
public virtual ICollection<UserForm> STRIndividualsCountry { get; set; }
[InverseProperty("BusinessCountry")]
public virtual ICollection<UserForm> STRBusinessCountry { get; set; }
}
The comment left by #T.Glatzer is correct. You should expose foreign key properties on your dependent entities:
public class UserForms
{
public int Id { get; set; }
public string FullNames { get; set; }
public int IndividualsCountryId { get; set; }
[ForeignKey("IndividualsCountryId")]
public virtual Countries IndividualsCountry { get; set; }
public int BusinessCountryId { get; set; }
[ForeignKey("BusinessCountryId")]
public virtual Countries BusinessCountry { get; set; }
}
Here I used int, but if either of these navigation properties are optional, you would just substitute int? or System.Nullable<int> instead (which will create an int NULL column in the database rather than an int NOT NULL).
Although EF does not require you to expose navigation properties, it is generally a good practice to. Trust me. It will help you avoid unexpected exceptions later on. In fact, some EF exception messages actually recommend exposing foreign key properties on the entity classes to help EF better figure out how to map relationships. Here is an example of one such exception. Note "Additional Information" section:
{"The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_dbo.DependentTable_dbo.PrincipalTable_Id". The conflict
occurred in database "DatabaseName", table "dbo.PrincipalTable", column
'Id'. The statement has been terminated."}
Additional information: An error occurred while saving entities that
do not expose foreign key properties for their relationships. The
EntityEntries property will return null because a single entity cannot
be identified as the source of the exception. Handling of exceptions
while saving can be made easier by exposing foreign key properties in
your entity types. See the InnerException for details.
#danludwig thanks for expounding #T.Glatzer answer this has worked for me! thank you. my final code that is now working is
public class UserForms
{
public int Id { get; set; }
public string FullNames { get; set; }
[ForeignKey("IndividualsCountry")]
public int? IndividualsCountryId { get; set; }
[ForeignKey("BusinessCountry")]
public int? BusinessCountryId { get; set; }
public virtual Countries IndividualsCountry { get; set; }
public virtual Countries BusinessCountry { get; set; }
}
public class Countries
{
public Countries()
{
this.STRBusinessCountry = new HashSet<UserForms>();
this.STRIndividualsCountry = new HashSet<UserForms>();
}
public int Id { get; set; }
public string NameOfCountry { get; set; }
[InverseProperty("IndividualsCountry")]
public virtual ICollection<UserForms> STRIndividualsCountry { get; set; }
[InverseProperty("BusinessCountry")]
public virtual ICollection<UserForms> STRBusinessCountry { get; set; }
}

Creating many to many junction table in Entity Framework

I am trying to add a many to many relationship between two of my entities. I need a junction table with an additional field, I'm aware that means EF cannot do this automatically and that I need to create an Entity for my junction table.
I have the following models
public class Supplier
{
public int Id { get; set;}
public virtual ICollection<SupplierUsers> UserPermissions { get; set; }
}
And
public class User
{
public string Id { get; set;}
public virtual ICollection<SupplierUsers> UserPermissions { get; set; }
}
I need for a user to have a permission stored in the junction table. So I have created the following entity
public class SupplierUsers
{
public string UserId { get; set; }
public int SupplierId { get; set; }
public SupplierUserPermission Permission { get; set; }
public virtual Supplier Supplier { get; set; }
public virtual User User { get; set; }
}
In my OnModelCreating I've also added the following (this is probably where I'm going wrong)
modelBuilder.Entity<SupplierUsers>()
.HasKey(x => new { x.UserId, x.SupplierId });
This works to an extent, I can successfully add a user/supplier/permission to this table.
But I cannot add the same user / supplier multiple times to this table (probably due to the PK?).
How can I alter my code so that I can add the same user or supplier multiple times in this table?
Here's what the table structure looks like at the moment:
Thank you.
If i understand you correctly you want to add multiple equal pairs of UserId and SupplierId to SupplierUsers, right?
Add a SupplierUsersId field to your SupplierUsers entity and make it primary key.
public class SupplierUsers
{
public int SupplierUsersId { get;set; }
public string UserId { get; set; }
public int SupplierId { get; set; }
public SupplierUserPermission Permission { get; set; }
public virtual Supplier Supplier { get; set; }
public virtual User User { get; set; }
}
Remove the configuration from OnModelCreating()

Categories