How to define in Entity Framework 6, which columns will be used to reference data in an ICollection<>?
For example, we have a simple User-Information relation, were i can get all information from a user which updated or created the information:
public class User
{
public int UserId
{
get;
get;
}
// Here is the problem, how to define that EF
// Has to use the CreateUser column
public virtual ICollection<User> User_CreateUser
{
get;
set;
}
// Here is the problem, how to define that EF
// Has to use the UpdateUser column
public virtual ICollection<User> User_UpdateUser
{
get;
set;
}
}
public class Information
{
public int InfoId
{
get;
set;
}
public int CreateUser
{
get;
set;
}
public int UpdateUser
{
get;
set;
}
[System.ComponentModel.DataAnnotations.Schema.ForeignKey("CreateUser")]
public virtual User User_CreateUser
{
get;
set;
}
[System.ComponentModel.DataAnnotations.Schema.ForeignKey("UpdateUser")]
public virtual User User_UpdateUser
{
get;
set;
}
}
But i don't know, how to map this. For the simple foreign-keys it was easy over annotations.
I use database first but generating the models on my own. (Which works pretty good besides the ICollection<>.
Thank you!
You've marked your second foreign key as User but it should be int.
public int UpdateUser { get; set; }
Try this:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Test
{
public class User
{
public int UserId { get; set; }
public virtual ICollection<User> User_CreateUser { get; set; }
public virtual ICollection<User> User_UpdateUser { get; set; }
}
public class Information
{
public int InfoId { get; set; }
public int CreateUser { get; set; }
public int UpdateUser { get; set; }
[ForeignKey("CreateUser")]
public virtual User User_CreateUser { get; set; }
[ForeignKey("UpdateUser")]
public virtual User User_UpdateUser { get; set; }
}
}
Related
I am developing a C# MVC application. I am using Code First approach to model my database.
My project had the following requirements:
Company Can Have Many Products
Product Can have many Advertisement
Types
Here are model classes (code first solution) to the above mentioned problem.
public class Company
{
public Company()
{
this.Employees = new HashSet<ApplicationUser>();
}
public int ID { get; set; }
[Required]
public string Name { get; set; }
public string Logo { get; set; }
[Display(Name="Company Description")]
public string CompanyDescription { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public virtual ICollection<ApplicationUser> Employees { get; set; }
public virtual ICollection<Client> Clients { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string ProductName { get; set; }
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<AdvertisementType> AdvertisementTypes { get; set; }
}
public class AdvertisementType
{
public int AdvertisementTypeID { get; set; }
public int ProductID { get; set; }
[Display(Name = "Advertisement Name")]
public string AdvertisementTypeName { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public virtual Product Product { get; set; }
}
When I try to update the database, after creating the migrations i get the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.AdvertisementTypes_dbo.Products_ProductID' on table 'AdvertisementTypes' 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.
I have been trying solve this problem but unable to find any solution. I don't find any problem with the model classes, nor i think there are any issues with the relationship between the models.
Any suggestions or help will be useful.
EDIT
Here is screenshot of Tables and their relations
in your dbContext you need to turn cascade delete to false if you want to avoid that.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
You can initialize your make a List() in Product like you did with Company class...
public class Product
{
public int ProductID { get; set; }
public Product()
{
this.AdvertisementTypes = new List<AdvertisementType>();
}
I'm trying to stay in POCO, but I'm at an impasse:
Public class Alpha()
{
public int ID { get; set; }
[ForeignKey("")]
public int BetaId { get; set; }
public virtual BetaA BetaA{ get; set; }
public virtual BetaB BetaB{ get; set; }
}
Public class BetaA()
{
public int ID { get; set; }
}
Public class BetaB()
{
public int ID { get; set; }
}
How do I get Alpha.BetaId to be both ForeignKey to BetaA.ID and BetaB.ID in POCO?
If anyone else stumble around this
Solution I used
Public class Alpha()
{
public int ID { get; set; }
public int BetaId { get; set; }
[ForeignKey("BetaId")]
public virtual BetaA BetaA{ get; set; }
[ForeignKey("BetaId")]
public virtual BetaB BetaB{ get; set; }
}
Public class BetaA()
{
public int ID { get; set; }
}
Public class BetaB()
{
public int ID { get; set; }
}
have BetaA and BetaB share a primary key then reference BetaB via BetaA
You can also decorate the nav properties with the foreign key attribute and specify the int property as the FK for both, though I have not tested this scenario myself (I'm on a mobile device). Give it a shot and let me know!
I'm experiencing an unexpected error when setting up a migration after adding keys and foreign keys to my data model. I'm using VS2013 Express, with .NET framework 4.5.
When creating a data model for Entity Framework, because the relationship keys between classes aren't what is expected by convention, I'm using data annotations as outlined in the MS Data Developer Center. Here's the class code:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BacklogTracker.Models
{
public class WorkOrder
{
[Key]
public string woNum { get; set; }
public string woClosingStatus { get; set; }
[ForeignKey("ID")]
public virtual ICollection<Note> woNotes { get; set; }
[ForeignKey("machSN")]
public virtual Machine woMachine { get; set; }
[ForeignKey("ID")]
public virtual ICollection<Segment> woSegments { get; set; }
}
public class Machine
{
[Key]
public string machSN { get; set; }
public string machLocation { get; set; }
public string machModel { get; set; }
}
public class Segment
{
[Key]
public int ID { get; set; }
public uint segNum { get; set; }
public string segRepair { get; set; }
[ForeignKey("ID")]
public virtual ICollection<Note> segNotes { get; set; }
}
public class Note
{
[Key]
public int ID { get; set; }
public DateTime notetimestamp { get; set; }
public string notestring { get; set; }
}
}
However, when I try to perform a migration after updating the model by performing enable-migrations in the package manager console, I get the following error:
The ForeignKeyAttribute on property 'woMachine' on type
'BacklogTracker.Models.WorkOrder' is not valid. The foreign key name
'machSN' was not found on the dependent type
'BacklogTracker.Models.WorkOrder'. The Name value should be a comma
separated list of foreign key property names.
Why is my foreign key name 'machSN' not being found?
I think you have some errors in your model. Default Code First convention for ForeignKey relationship expected to have declared a foreign key property in the dependend end (WorkOrder) that match with primary key property of the principal end (Machine). It is not necessary that they have the same name, check this link. So, declare a property named machSN in your WorkOrder class:
public class WorkOrder
{
[Key]
public string woNum { get; set; }
public string woClosingStatus { get; set; }
public virtual ICollection<Note> woNotes { get; set; }
public string machSN { get; set; }
[ForeignKey("machSN")]
public virtual Machine woMachine { get; set; }
public virtual ICollection<Segment> woSegments { get; set; }
}
You can find other errors in the woNotes and woSegments navigation properties. In this side of a one-to-many relationship you don't declare a FK, is in the other side, in Note and Segment classes, for example:
public class Note
{
[Key]
public int ID { get; set; }
public DateTime notetimestamp { get; set; }
public string notestring { get; set; }
[ForeignKey("Order)]
public string woNum { get; set; }
public virtual WorkOrder Order{get;set;}
}
Delete also in the Segment class the ForeignKey attribute over segNotes navigation property for the same reasons explained before.
public class Segment
{
[Key]
public int ID { get; set; }
public uint segNum { get; set; }
public string segRepair { get; set; }
public virtual ICollection<Note> segNotes { get; set; }
}
When my models were generated, many relationships were mapped automagically. However, some of the relationships are "incorrect" (or at least, not what I want), or missing.
I don't doubt that this is because of poor database design, but based on my role in this project there is not much I can do to fix that. However, is there something I can do in my application code to fix the mapping?
Here is one example:
I would like to map the StoreProductId property to the StoreProducts table.
ProductAttributePriceAdjustment
public partial class ProductAttributePriceAdjustment
{
public int AdjustmentId { get; set; }
public int StoreProductId { get; set; }
public int StoreId { get; set; }
public string ProductId { get; set; }
public Nullable<int> ProductSizeId { get; set; }
public Nullable<decimal> Adjustment { get; set; }
public int PointsAdjustment { get; set; }
public Nullable<int> ProductColorID { get; set; }
public StoreProduct StoreProduct { get; set; }
}
StoreProduct
public partial class StoreProduct
{
public int StoreProductID { get; set; }
public int StoreID { get; set; }
public string ProductID { get; set; }
public bool Featured { get; set; }
public bool Clearance { get; set; }
}
In my view, when I try calling something like:
#adjustment.StoreProduct.ProductID
I get this error:
Object reference not set to an instance of an object.
Update 1
I followed Frans' advice and updated my model to this:
public partial class ProductAttributePriceAdjustment
{
public int AdjustmentId { get; set; }
public int StoreProductId { get; set; }
public int StoreId { get; set; }
public string ProductId { get; set; }
public Nullable<int> ProductSizeId { get; set; }
public Nullable<decimal> Adjustment { get; set; }
public int PointsAdjustment { get; set; }
public Nullable<int> ProductColorID { get; set; }
public virtual StoreProduct StoreProduct { get; set; }
}
but am still getting the same error.
You cannot create a 1:1 mapping in entity framework like this. It's not supported.
Entity Framework only supports 1:1 mappings in which both tables have a shared primary key (ie they have the same primary key, and one of them is a foreign key to the other). In your situation, you are actually creating a 1 to many, because there is no guarantee that StoreProductId is unique.
yesterday I created database in Management Studio and now I want to create it in program using EF Code First.
Here is link to my database: http://s11.postimg.org/6sv6cucgj/1462037_646961388683482_1557326399_n.jpg
And what I did:
public class GameModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreationTime { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string TotalTime { get; set; }
public DateTime RouteStartTime { get; set; }
public DateTime RouteEndTime { get; set; }
public int MaxPlayersPerTeam { get; set; }
public int CityId { get; set; }
public int CreatorId { get; set; }
[InverseProperty("Id")]
[ForeignKey("CreatorId")]
//public int TeamId { get; set; }
//[ForeignKey("TeamId")]
public virtual UserModel Creator { get; set; }
public virtual CityModel City { get; set; }
//public virtual TeamModel WinnerTeam { get; set; }
}
public class RegionModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CityModel> Cities { get; set; }
}
public class CityModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RegionId { get; set; }
public virtual RegionModel Region { get; set; }
public virtual ICollection<UserModel> Users { get; set; }
public virtual ICollection<GameModel> Games { get; set; }
}
public class UserModel
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime RegistrationDate { get; set; }
public string FacebookId { get; set; }
public int CityId { get; set; }
public virtual CityModel City { get; set; }
public virtual IEnumerable<GameModel> Games { get; set; }
}
For now I wanted to create 4 tables but I have some problems... I want to make CreatorId in GameModel, but it doesn't work... When i wrote UserId instead of CreatorId it was working ( without [InverseProperty("Id")] and [ForeignKey("CreatorId")]).
This is what i get:
The view 'The property 'Id' cannot be configured as a navigation property. The property must be a valid entity type and the property should have a non-abstract getter and setter. For collection properties the type must implement ICollection where T is a valid entity type.' or its master was not found or no view engine supports the searched locations.
edit:
I changed it like this:
public int CityId { get; set; }
public int CreatorId { get; set; }
[ForeignKey("CityId")]
public virtual CityModel City { get; set; }
[ForeignKey("CreatorId")]
public virtual UserModel Creator { get; set; }
And there is another problem.
The view 'Introducing FOREIGN KEY constraint 'FK_dbo.UserModels_dbo.CityModels_CityId' on table 'UserModels' 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.' or its master was not found or no view engine supports the searched locations.
And I have no idea how to solve it.
The InversePropertyAttribute specifies, which navigation property should be used for that relation.
A navigation property must be of an entity type (the types declared in your model, GameModel for example) or some type implementing ICollection<T>, where T has to be an entity type. UserModel.Id is an int, which clearly doesn't satisfy that condition.
So, the inverse property of GameModel.Creator could be UserModel.Games if you changed the type to ICollection<GameModel>, or had to be left unspecified. If you don't specify an inverse property, EF will try to work everything out on its own (in this case it would properly recognize GameModel.Creator as a navigation property, but UserModel.Games would most likely throw an exception, as it is neither an entity type, nor does it implement ICollection<T> with T being an entity type, nor is it a primitive type from a database point of view). However, EF's work-everything-out-by-itself-magic doesn't cope too well with multiple relations between the same entity types, which is when the InversePropertyAttribute is needed.
A quick example that demonstrates the problem:
class SomePrettyImportantStuff {
[Key]
public int ID { get; set; }
public int OtherId1 { get; set; }
public int OtherId2 { get; set; }
[ForeignKey("OtherId1")]
public virtual OtherImportantStuff Nav1 { get; set; }
[ForeignKey("OtherId2")]
public virtual OtherImportantStuff Nav2 { get; set; }
}
class OtherImportantStuff {
[Key]
public int ID { get; set; }
public virtual ICollection<SomePrettyImportantStuff> SoldStuff { get; set; }
public virtual ICollection<SomePrettyImportantStuff> BoughtStuff { get; set; }
}
Here, EF knows that it has to generate 2 FKs from SomePrettyImportantStuff to OtherImportantStuff with the names Id1 and Id2, but it has no way to tell which of the IDs refers to the entity where it was sold from and which is the one it was bought from.
Edit: How to fix the cyclic reference problem
To fix that problem, your context class should override OnModelCreating and configure the foreign keys which shouldn't cascade on delete accordingly, like this:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<CityModel>().HasMany(c => c.Users).WithRequired(u => u.City)
.HasForeignKey(u => u.CityId).WillCascadeOnDelete(value: false);
// Add other non-cascading FK declarations here
base.OnModelCreating(builder);
}