I just started using Code first approach for creating databases. I have following 3 tables :
public class TagDatabase
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TagID { get; set; }
public string TagName { get; set; }
public string Description { get; set; }
public int Count { get; set; }
[ForeignKey("TagTypes")]
public virtual int TagTypeID { get; set; }
public virtual ICollection<TagTypesDb> TagTypes { get; set; }
[ForeignKey("Users")]
public virtual int CreatedBy { get; set; }
public virtual UsersDb Users { get; set; }
}
public class TagTypesDb
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TagTypeID { get; set; }
public string TagTypeName { get; set; }
}
public class UsersDb
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserID { get; set; }
public string UserName { get; set; }
}
Here TagDatabse and User and TagType have 1 to 1 replationship. The fluent API code which i used for this is :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TagDatabase>()
.HasOptional(a => a.TagTypes)
.WithMany()
.HasForeignKey(u => u.TagTypeID);
modelBuilder.Entity<TagDatabase>()
.HasRequired(a => a.Users)
.WithMany()
.HasForeignKey(u => u.CreatedBy);
}
Now my issue is whenever i am trying to insert data in TagDatabase i got this exception :
TagDatabase_TagTypes: : Multiplicity conflicts with the referential constraint in Role 'TagDatabase_TagTypes_Target' in relationship 'TagDatabase_TagTypes'. Because all of the properties in the Dependent Role are non-nullable, multiplicity of the Principal Role must be '1'.
The TagTypeId property is allowed nulls .so i used HasOptional() in OnModelCreating method.
Cananybody please tell me how to solve this issue and what i am missing here ?
You should make the foreign key property nullable if the corresponding relationship is optional.
public class TagDatabase
{
//sniff...
[ForeignKey("TagTypes")]
public virtual int? TagTypeID { get; set; } //Since TagTypes is optional, this should be nullable
public virtual ICollection<TagTypesDb> TagTypes { get; set; }
//sniff...
}
Related
I have two classes like so:
[Table("GameLevels", Schema = "ref")]
public class GameLevel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public double PointsMin { get; set; }
public double PointsMax { get; set; }
}
[Table("GameProfiles", Schema = "usr")]
public class UserGameProfile
{
[Key]
[ForeignKey("ApplicationUser")]
public string Id { get; set; }
public int GamesPlayed { get; set; }
public double Points { get; set; }
public int WinCount { get; set; }
public int LossCount { get; set; }
public int DrawCount { get; set; }
public int ForfeitCount { get; set; }
public int GameLevelId { get; set; }
public virtual GameLevel Level { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
Entity framework builds this so that UserGameProfile has a foreign key pointing to GameLevel. I guess this is because of the GameLevelId property.
Is there any way I can get this to generate the tables and navigation property without the foreign key?
I tried:
modelBuilder.Entity<UserGameProfile>().HasOptional<GameLevel>(x => x.Level).WithMany();
But then database fails to build. With this error:
One or more validation errors were detected during model generation:
Project.Domain.Data.UserGameProfile_Level: : Multiplicity
conflicts with the referential constraint in Role
'UserGameProfile_Level_Target' in relationship
'UserGameProfile_Level'. Because all of the properties in the
Dependent Role are non-nullable, multiplicity of the Principal Role
must be '1'.
Basically what I want is a zero-or-one to zero-or-many relationship.
How do I keep levels independent but have the ability to add a level to a profile?
You can't drop the foreign key completely, otherwise, how do you expect the two entities (i.e, tables) to be linked? What you can do instead is have a nullable FK, which will effectively make the relationship zero-or-one to many.
In your GameLevel class, add a navigation property as a collection of UserGameProfile:
public class GameLevel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public double PointsMin { get; set; }
public double PointsMax { get; set; }
public virtual ICollection<UserGameProfile> UserGameProfiles { get; set; }
}
Then in the UserGameProfile class, make the property GameLevelId nullable:
public class UserGameProfile
{
// ...
// ...
public int? GameLevelId { get; set; }
[ForeignKey("GameLevelId")]
public virtual GameLevel GameLevel { get; set; }
}
This should work without even having to use Fluent API.
I have the following:
public class Event : IEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("date")]
public DateTimeOffset Date { get; set; }
[JsonProperty("distance")]
public int Distance { get; set; }
[JsonProperty("verticalAscend")]
public int VerticalAscend { get; set; }
[ForeignKey("User")]
[JsonProperty("userId")]
public Guid UserId { get; set; }
//attending
public DateTimeOffset DateCreated { get; set; }
public DateTimeOffset DateModified { get; set; }
[JsonProperty("user")]
public virtual User User { get; set; }
[JsonProperty("comments")]
public virtual ICollection<Comment> Comments { get; set; }
[JsonProperty("attending")]
public virtual ICollection<User> AttendingList { get; set; }
}
And:
public class User : IEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("profilePicUrl")]
public string ProfilePicUrl { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("surname")]
public string LastName { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
public DateTimeOffset DateCreated { get; set; }
public DateTimeOffset DateModified { get; set; }
public virtual ICollection<Event> AttendingEvents { get; set; }
[InverseProperty("User")]
public virtual ICollection<Event> Events { get; set; }
}
Relationships:
Event:
Many Users attending (AttendingList)
User:
Can attend many events (AttendingEvents)
Can create multiple events (Events)
There exists a many-many relationship between the Event.AttendingList and User.AttendingEvents.
There exists 0-many relationship between Event.User and User.Events, with ForeignKey as UserId.
I am trying to configure these with Fluent API, and using the InverseProperty to configure the other side of the relationship, mapping back to Event.User, but getting the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.UserEvents_dbo.Events_Event_Id' on table 'UserEvents' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I am unsure on how to solve this relationship on one table. What am I doing wrong?
In the DbContext configure your models as follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Event>().HasRequired(e => e.User)
.WithMany(u => u.Events)
.HasForeignKey(e => e.UserId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasMany<Event>(s => s.AttendingEvents)
.WithMany(c => c.AttendingList)
.Map(cs =>
{
cs.MapLeftKey("UserId");
cs.MapRightKey("EventId");
cs.ToTable("UserEvents");
});
}
There is a Cargo class/table which has identity CargoID
There is a ContainerIn class/table which containes CargoID
Every Cargo could have 1 or 0 corresponding container entries.
I am trying to create navigation properties such that.
Cargo.ContainerIn--->should give me associated ContainerIn entry
ContainerIn.Cargo--->should give me associated Cargo entry
Cargo Class:
public class Cargo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CargoID { get; set; }//SerialNo
[Required]
public DateTime DateOfPassage { get; set; }
public string CompanyUserName { get; set; }
public virtual ContainerIn ContainerIn { get; set; }
}
ContainerIn Subclass:
public class ContainerIn
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContainerInID { get; set; }
public int CargoID { get; set; }
public virtual Cargo Cargo { get; set; }
public int LoadStatus { get; set; }
}
I have also tried adding public int ContainerInID { get; set; } inCargo` class.
I am still getting :
`Unable to determine the principal end of an association between the types 'PisMark3.Models.Cargo.ContainerIn' and
'PisMark3.Models.Cargo.Cargo'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.`
EDIT:
I have added OnModelCreating in ApplicationDbContext class.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<PisMark3.Models.Cargo.Cargo>()
.HasOptional(s => s.ContainerIn)
.WithRequired(ad => ad.Cargo);
}
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
// Database.SetInitializer<ApplicationDbContext>(new DropCreateDatabaseIfModelChanges<ApplicationDbContext>());
}
....
Now I am getting:
you're pretty close. I think you want the following:
public class Cargo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CargoID { get; set; }//SerialNo
[Required]
public DateTime DateOfPassage { get; set; }
public string CompanyUserName { get; set; }
public int ContainerInId { get; set; } //need to define a foreign key. This is happening by naming convention in this case as with your `ContainerIn.CargoId` foreign key
public virtual ContainerIn ContainerIn { get; set; }
}
public class ContainerIn
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContainerInID { get; set; }
public int CargoID { get; set; }
public virtual Cargo Cargo { get; set; }
public int LoadStatus { get; set; }
}
Note this is a circular reference which should probably be avoided if possible however there are certainly some valid use cases. Just thought I'd give a shout out to that.
If you don't want to abide by naming conventions, you can use the ForeignKey data annotation as outlined here
unable to determine the principal end of an association between the
types 'PisMark3.Models.Cargo.ContainerIn' and
'PisMark3.Models.Cargo.Cargo'. The principal end of this association
must be explicitly configured using either the relationship fluent API
or data annotations
This is telling you to define your relationship, because it can not understand the relationship.
What you are looking for is
One-to-Zero-or-One relationship here is the link
This is your model,
public class Cargo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CargoID { get; set; }//SerialNo
[Required]
public DateTime DateOfPassage { get; set; }
public string CompanyUserName { get; set; }
public virtual ContainerIn CompanyUserNameContainIn { get; set; }
}
public class ContainerIn
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContainerInID { get; set; }
public int LoadStatus { get; set; }
public int CargoID { get; set; }
public virtual Cargo Cargo { get; set; }
}
This is your flent api code,
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Cargo>()
.HasOptional(s => s.CompanyUserNameContainIn)
.WithRequired(ad => ad.Cargo);
}
This tells entityframework that it has optional companyusername in cargo model and required model cargo in ContainerIn
You can read more in detail in the link I provided, it has nice example of student and address.
EDIT:
As you want to use identityDBContext you can modify your code as below
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public virtual DbSet<Cargo> Cargo { get; set; }
public virtual DbSet<ContainerIn> ContainerIn { get; set; }
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Cargo>()
.HasOptional(s => s.CompanyUserNameContainIn)
.WithRequired(ad => ad.Cargo);
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
}
}
I use Entity Framework 6.1 in ASP.NET MVC.
My model is :
public class Article
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public double Price { get; set; }
[InverseProperty("Article")]
public virtual ICollection<FormulaItem> FormulaItem { get; set; }
}
public class FormulaItem
{
[Key]
[Column(Order = 0)]
public int Id { get; set; }
[ForeignKey("IdMaster")]
public virtual Formula Formula { get; set; }
public int IdMaster { get; set; }
[ForeignKey("IdArticle")]
public virtual Article Article { get; set; }
public int IdArticle { get; set; }
public string Comment { get; set; }
public int Count { get; set; }
}
public class Formula
{
[Key]
[Column(Order = 0)]
public int Id { get; set; }
public FormulaMode Mode { get; set; }
// Wen add this line I get error
//[ForeignKey("IdArticle")]
//public virtual Article Article { get; set; }
//public int? IdArticle { get; set; }
public string Comment { get; set; }
public virtual IList<FormulaItem> Items { get; set; }
public Formula()
{
Items = new List<FormulaItem>();
}
}
This sample works fine, but when add the new poco:
// When I add this line in class formula I get error
[ForeignKey("IdArticle")]
public virtual Article Article { get; set; }
public int? IdArticle { get; set; }
to class Formula I get error:
Formula_Items_Source_Formula_Items_Target: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical
Consider using FluentAPI as it is more user-friendly:
modelBuilder.Entity<FormulaItem>()
.HasOptional(b => b.Article )
.WithMany(a => a.FormulaItem);
And in your case you must be missing another InverseProperty attribute:
[InverseProperty("FormulaItem")]
public virtual Article Article { get; set; }
I add:
modelBuilder.Configurations.Add(new FormulaConfig());
public class FormulaConfig : EntityTypeConfiguration<Formula>
{
public FormulaConfig()
{ // one-to-many
this.HasRequired(x => x.Article)
.WithOptional(x=>x.Formula)
.WillCascadeOnDelete();
}
}
but get error :
Article_Formula_Target: : Multiplicity is not valid in Role 'Article_Formula_Target' in relationship 'Article_Formula'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
Here's my code:
base class:
public class BaseEnt
{
[Key]
public int ID { get;set; }
public DateTime InsertDate { get; set; }
public int InsertUserID { get; set; }
public DateTime UpdateDate { get; set; }
public int UpdateUserID { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
public virtual User InsertUser { get; set; }
public virtual User UpdateUser { get; set; }
}
user entity:
public class User:BaseEnt
{
public string Username { get; set; }
public string Fullname { get; set; }
public virtual ICollection<User> InsertedUsers { get; set; }
public virtual ICollection<User> UpdatedUsers { get; set; }
}
model creating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasRequired(t => t.InsertUser)
.WithMany(t=>t.InsertedUsers)
.HasForeignKey(t => t.InsertUserID).WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasRequired(t => t.UpdateUser)
.WithMany(t=>t.UpdatedUsers)
.HasForeignKey(t => t.UpdateUserID).WillCascadeOnDelete(false);
}
Seed:
protected override void Seed(MCFDataContext context)
{
context.Users.Add(new Entities.User {ID=1,Fullname="Rusty Boi ",Username="jhaskdhaksdhk",InsertUserID=1,UpdateUserID=1,UpdateDate=DateTime.Now,InsertDate=DateTime.Now });
}
and here's the error i encountered in the seed part:
{"Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values."}
what you have here is some sort of "chicken and egg" problem. Possible solutions:
Make the foreign keys InsertUserID and UpdateUserID nullable
Do a design change: Maybe the user table shouldn't have InsertUserID, UpdateUserID. this could be solved by introducing another base class which doesn't have the InsertUserID and UpdateUserID properties or something similar