Entity Framework - Multiple Many-To-Many Relationships - asp.net

I am trying to describe the mapping to EF to create multiple many-to-many relationships between the following two entities (simplified):
Product:
public class Product
{
[Key]
public int ProductID { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
public virtual ICollection<Transaction> RefundTransactions { get; set; }
public virtual ICollection<Transaction> VoidTransactions { get; set; }
}
Transaction:
public class Transaction
{
[Key]
public int TransactionID { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<Product> RefundProducts { get; set; }
public virtual ICollection<Product> VoidProducts { get; set; }
}
OnModelCreating:
modelBuilder.Entity<Transaction>()
.HasMany(m => m.Products)
.WithMany(t => t.Transactions)
.Map(m =>
{
m.ToTable("Transaction_Product_Mapping");
m.MapLeftKey("TransactionID");
m.MapRightKey("ProductID");
}
);
modelBuilder.Entity<Transaction>()
.HasMany(transaction => transaction.VoidProducts)
.WithMany(t => t.VoidTransactions)
.Map(m =>
{
m.ToTable("Transaction_Void_Product_Mapping");
m.MapLeftKey("TransactionID");
m.MapRightKey("VoidProductID");
}
);
modelBuilder.Entity<Transaction>()
.HasMany(m => m.RefundProducts)
.WithMany(t => t.Transactions)
.Map(m =>
{
m.ToTable("Transaction_Refund_Product_Mapping");
m.MapLeftKey("TransactionID");
m.MapRightKey("RefundProductID");
}
);
Exception:
Type Transaction_Products is not defined in namespace Nautix_EPOS.Models
Now, I think this may be because I am defining the mapping 3 times separately. And possibly overwriting the first with the second, and the second with the last.
Question:
How can I tell EF about the multiple many-to-many mappings between the same two tables?

I figured it out, it's because I used the same t.Transactions in the first and third declaration. I should have use t.RefundTransactions:
modelBuilder.Entity<Transaction>()
.HasMany(m => m.Products)
.WithMany(t => t.Transactions)
.Map(m =>
{
m.ToTable("Transaction_Product_Mapping");
m.MapLeftKey("TransactionID");
m.MapRightKey("ProductID");
}
);
modelBuilder.Entity<Transaction>()
.HasMany(transaction => transaction.VoidProducts)
.WithMany(t => t.VoidTransactions)
.Map(m =>
{
m.ToTable("Transaction_Void_Product_Mapping");
m.MapLeftKey("TransactionID");
m.MapRightKey("VoidProductID");
}
);
modelBuilder.Entity<Transaction>()
.HasMany(m => m.RefundProducts)
.WithMany(t => t.RefundTransactions)
.Map(m =>
{
m.ToTable("Transaction_Refund_Product_Mapping");
m.MapLeftKey("TransactionID");
m.MapRightKey("RefundProductID");
}
);

Related

Introducing FOREIGN KEY constraint 'FK_Product_User_UserId' on table 'Product' may cause cycles or multiple cascade paths

i cannot create database with ef core
error : Introducing FOREIGN KEY constraint 'FK_Product_User_UserId' on table 'Product' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
this is my product class
{
public Product()
{
}
public Guid UserId { get; set; }
public Guid CategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string PhotoPath { get; set; }
public decimal Price { get; set; }
public Category Category { get; set; }
public User User { get; set; }
}
and
this is my user class
{
public User()
{
Payments = new HashSet<Payment>();
Categories = new HashSet<Category>();
Products = new HashSet<Product>();
}
public string Username { get; set; }
public Guid Password { get; set; }
public ICollection<Payment> Payments { get; set; }
public ICollection<Category> Categories { get; set; }
public ICollection<Product> Products { get; set; }
}
its mapping class
{
public ProductMap()
{
}
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.UserId).IsRequired();
builder.Property(x => x.CreatedDate).IsRequired();
builder.Property(x => x.Description).HasMaxLength(500);
builder.Property(x => x.IsActive).IsRequired();
builder.Property(x => x.Name).HasMaxLength(500).IsRequired();
builder.Property(x => x.PhotoPath).HasMaxLength(4000);
builder.Property(x => x.Price).HasColumnType("decimal(10,3)").IsRequired();
builder.HasOne(x => x.Category).WithMany(x => x.Products).HasForeignKey(x => x.CategoryId);
builder.HasOne(x => x.User).WithMany(x => x.Products).HasForeignKey(x => x.UserId);
}
}
and i cannot create database cause error like this posts title.
what can i do?
Thanks.
You are saying that each user has many Categories and each Category has many Products so you have to remove this line from the User class since it causing cycle path
public ICollection<Product> Products { get; set; }
And also fix the last line of the Configure method:
builder.HasOne(x => x.User).WithMany(x => x.Categories).HasForeignKey(x => x.UserId);

Validation failed for one or more entities. UserManager.AddToRole()

I'm developing a Web Api using ASP.NET MVC5 and I have successfully implemented a Custom IdentityUser.Like So
public partial class Authentication : IdentityUser
{
public Authentication()
{
this.LoginSessions = new List<LoginSession>();
}
[Required]
public string PersonId { get; set; }
public virtual Lecturer Lecturer { get; set; }
public virtual Student Student { get; set; }
public virtual ICollection<LoginSession> LoginSessions { get; set; }
}
with the following Maps
public class IdentityUserLoginConfiguration: EntityTypeConfiguration<IdentityUserLogin>
{
public IdentityUserLoginConfiguration()
{
HasKey(t => t.UserId);
}
}
public class IdentityRoleConfiguration : EntityTypeConfiguration<IdentityRole>
{
public IdentityRoleConfiguration()
{
HasKey(t => t.Id);
}
}
public class AuthenticationMap : EntityTypeConfiguration<Authentication>
{
public AuthenticationMap()
{
// Primary Key
this.HasKey(t => t.PersonId);
// Properties
this.Property(t => t.PersonId)
.IsRequired()
.HasMaxLength(14);
// Table & Column Mappings
this.ToTable("Authentication");
this.Property(t => t.PersonId).HasColumnName("PersonID");
// Relationships
this.HasOptional(t => t.Lecturer)
.WithOptionalPrincipal(t => t.Authentication);
this.HasOptional(t => t.Student)
.WithOptionalPrincipal(t => t.Authentication);
}
}
But now my problem is that when I'm adding a user to a Role Using the
UserManager.AddToRole()
method I get a DBValidationError
This is really worrying me and would like some help as soon as possible

EF Code First - Invalid column name

Im getting an error "Invalid column name 'FeeLevel_LevelId' which makes absolutely no sense considering all properties are simple types and there is no FeeLevel nor a LevelId object in this object.
So my context is:
public partial class FeesDbContext : DisconnectedEntityContext
{
public DbSet<Currency> Currencies { get; set; }
public DbSet<FeeLevel> FeeLevels { get; set; }
public DbSet<FeeLevelDetail> FeeLevelDetails { get; set; }
public DbSet<FeeType> FeeTypes { get; set; }
public DbSet<MemberFeeDiscountLevel> MemberFeeDiscountLevels { get; set; }
public FeesDbContext()
: base("FeesDb") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new FeeLevelMap());
modelBuilder.Configurations.Add(new FeeLevelDetailMap());
modelBuilder.Configurations.Add(new FeeTypeMap());
modelBuilder.Configurations.Add(new CurrencyMap());
modelBuilder.Configurations.Add(new MemberFeeDiscountLevelMap());
}
public static void ApplyChanges<TEntity>(TEntity root)
where TEntity : class, IObjectWithState
{
using (var context = new FeesDbContext())
{
context.Set<TEntity>().Add(root);
foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = ConvertState(stateInfo.State);
}
context.SaveChanges();
}
}
}
The POCO is:
public partial class MemberFeeDiscountLevel : AbstractState
{
public long MemberFeeDiscountLevelId { get; set; }
public System.Guid MemberId { get; set; }
public short MemberAsType { get; set; }
public long FeeDiscountLevelId { get; set; }
public System.DateTime FeeDiscountLevelAppliedDate { get; set; }
public Nullable<System.DateTime> FeeDiscountLevelExpiresDate { get; set; }
public Nullable<long> FallbackFeeDiscountLevelId { get; set; }
public System.Guid UserId { get; set; }
public System.DateTime LastModified { get; set; }
public MemberFeeDiscountLevel(ObjectState state) : base(state) { }
public MemberFeeDiscountLevel()
{
}
}
It's mapping is:
public class MemberFeeDiscountLevelMap : EntityTypeConfiguration<MemberFeeDiscountLevel>
{
public MemberFeeDiscountLevelMap()
{
// Primary Key
this.HasKey(t => t.MemberFeeDiscountLevelId);
this.Ignore(t => t.State);
// Properties
// Table & Column Mappings
this.ToTable("MemberFeeDiscountLevel");
this.Property(t => t.MemberFeeDiscountLevelId).HasColumnName("MemberFeeDiscountLevelId");
this.Property(t => t.MemberId).HasColumnName("MemberId");
this.Property(t => t.MemberAsType).HasColumnName("MemberAsType");
this.Property(t => t.FeeDiscountLevelId).HasColumnName("FeeDiscountLevelId");
this.Property(t => t.FeeDiscountLevelAppliedDate).HasColumnName("FeeDiscountLevelAppliedDate");
this.Property(t => t.FeeDiscountLevelExpiresDate).HasColumnName("FeeDiscountLevelExpiresDate");
this.Property(t => t.FallbackFeeDiscountLevelId).HasColumnName("FallbackFeeDiscountLevelId");
this.Property(t => t.UserId).HasColumnName("UserId");
this.Property(t => t.LastModified).HasColumnName("LastModified");
}
}
The database table is:
and it has not relationships. Yet EF is generating the following SQL:
exec sp_executesql N'INSERT [dbo].[MemberFeeDiscountLevel]([MemberId], [MemberAsType], [FeeDiscountLevelId], [FeeDiscountLevelAppliedDate], [FeeDiscountLevelExpiresDate], [FallbackFeeDiscountLevelId], [UserId], [LastModified], [FeeLevel_LevelId])
VALUES (#0, #1, #2, #3, #4, #5, #6, #7, #8, NULL)
SELECT [MemberFeeDiscountLevelId]
FROM [dbo].[MemberFeeDiscountLevel]
WHERE ##ROWCOUNT > 0 AND [MemberFeeDiscountLevelId] = scope_identity()',N'#0 uniqueidentifier,#1 smallint,#2 bigint,#3 datetime2(7),#4 datetime2(7),#5 bigint,#6 uniqueidentifier,#7 datetime2(7),#8 int',#0='DAF771D1-079F-4743-B5C7-FD0FA1C63E19',#1=0,#2=1012,#3='2014-01-24 12:05:36.0608347',#4='2014-02-01 00:00:00',#5=1018,#6='EEDF2C83-2123-4B1C-BF8D-BE2D2FA26D09',#7='2014-01-24 12:05:36.0608347'
go
UPDATE:
Creating a new Fees2DbContext stripping out the other DbSets "fixes" the problem.... but I dont know why... none of these classes / sets are related to the class in question.
public partial class Fees2DbContext : DisconnectedEntityContext
{
public DbSet<MemberFeeDiscountLevel> MemberFeeDiscountLevels { get; set; }
public Fees2DbContext()
: base("FeesDb") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MemberFeeDiscountLevelMap());
}
public static void ApplyChanges<TEntity>(TEntity root)
where TEntity : class, IObjectWithState
{
using (var context = new Fees2DbContext())
{
context.Set<TEntity>().Add(root);
foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = ConvertState(stateInfo.State);
}
context.SaveChanges();
}
}
}
UPDATE 2:
public partial class FeeLevel : AbstractState
{
public FeeLevel()
{
this.FeeLevelDetails = new List<FeeLevelDetail>();
this.MemberFeeDiscountLevels = new List<MemberFeeDiscountLevel>();
}
public long LevelId { get; set; }
public string LevelName { get; set; }
public int CurrencyId { get; set; }
public System.DateTime LastModified { get; set; }
public bool IsSystemOwned { get; set; }
public System.Guid UserId { get; set; }
public virtual Currency Currency { get; set; }
[ScriptIgnore]
public virtual ICollection<FeeLevelDetail> FeeLevelDetails { get; set; }
public virtual ICollection<MemberFeeDiscountLevel> MemberFeeDiscountLevels { get; set; }
}
public class FeeLevelMap : EntityTypeConfiguration<FeeLevel>
{
public FeeLevelMap()
{
// Primary Key
this.HasKey(t => t.LevelId);
this.Ignore(t => t.State);
// Properties
this.Property(t => t.LevelId);
// .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(t => t.LevelName)
.IsRequired()
.HasMaxLength(50);
// Table & Column Mappings
this.ToTable("FeeLevel");
this.Property(t => t.LevelId).HasColumnName("LevelId");
this.Property(t => t.LevelName).HasColumnName("LevelName");
this.Property(t => t.CurrencyId).HasColumnName("CurrencyId");
this.Property(t => t.LastModified).HasColumnName("LastModified");
this.Property(t => t.UserId).HasColumnName("UserId");
// Relationships
this.HasRequired(t => t.Currency)
.WithMany(t => t.FeeLevels)
.HasForeignKey(d => d.CurrencyId);
}
}
FeeLevel.MemberFeeDiscountLevels is a navigation property and it introduces a one-to-many relationship between FeeLevel and MemberFeeDiscountLevel: A FeeLevel can have many MemberFeeDiscountLevels which means at the same time that a MemberFeeDiscountLevel has a single FeeLevel. Although you don't have a navigation and foreign key property in MemberFeeDiscountLevel the database must have a foreign key in the MemberFeeDiscountLevel table in order to model this relationship. EF assumes a default FK name as "related entity name+underscore+primary key name" = FeeLevel_LevelId. Because your database table doesn't have this column you get the exception.

EF Code First: Migrations removing needed columns and adding unwanted ones

Note: there is a "better" iteration of this question here:
EF Code First migrations: Table Per Hierarchy Bug
I am trying to figure out the exact steps need to take an existing database and overlay entity framework code first with a Table Per Hierarchy structure. However no matter what I do, the end result migration is "wrong".
Here is the sample database, exact steps and output.
Existing database structure
CREATE TABLE [dbo].[Categories](
[Id] [int] IDENTITY(1,1) NOT NULL Primary Key,
[Name] [nvarchar](100) NOT NULL,
)
GO
CREATE TABLE [dbo].[A](
[Id] [int] IDENTITY(1,1) NOT NULL Primary Key,
[Discriminator] [nvarchar](20) NOT NULL,
[Description] [nvarchar](100) NOT NULL,
[CategoryId] [int] NULL
)GO
ALTER TABLE [dbo].[A]
WITH CHECK ADD CONSTRAINT [FK_dbo.A_dbo.Categories_CategoryId]
FOREIGN KEY([CategoryId])
REFERENCES [dbo].[Categories] ([Id])
GO
ALTER TABLE [dbo].[A]
CHECK CONSTRAINT [FK_dbo.A_dbo.Categories_CategoryId]
GO
"Reverse Engineer Code First" generates the following:
public partial class EntityContext : DbContext {
static EntityContext() {
Database.SetInitializer<EntityContext>(null);
}
public EntityContext()
: base("Name=Test47Context") {
}
public DbSet<A> A { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Configurations.Add(new AMap());
modelBuilder.Configurations.Add(new CategoryMap());
}
}
public partial class A {
public int Id { get; set; }
public string Discriminator { get; set; }
public string Description { get; set; }
public Nullable<int> CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public partial class Category {
public Category() {
this.A = new List<A>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<A> A { get; set; }
}
public class AMap : EntityTypeConfiguration<A> {
public AMap() {
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Discriminator)
.IsRequired()
.HasMaxLength(20);
this.Property(t => t.Description)
.IsRequired()
.HasMaxLength(100);
// Table & Column Mappings
this.ToTable("A");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Discriminator).HasColumnName("Discriminator");
this.Property(t => t.Description).HasColumnName("Description");
this.Property(t => t.CategoryId).HasColumnName("CategoryId");
// Relationships
this.HasOptional(t => t.Category)
.WithMany(t => t.A)
.HasForeignKey(d => d.CategoryId);
}
}
public class CategoryMap : EntityTypeConfiguration<Category> {
public CategoryMap() {
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Name)
.IsRequired()
.HasMaxLength(100);
// Table & Column Mappings
this.ToTable("Categories");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Name).HasColumnName("Name");
}
}
Implement TPH, by
Adding Derived.cs inherits from A.cs
Moving Category and CategoryId properties from A.cs to Derived.cs
Remove Discriminator property from A.cs
In AMap.cs remove Discriminator, CategoryId and Category Foreign Key mapping.
Adding TPH mapping to OnModelCreating
public partial class EntityContext : DbContext {
static EntityContext() {
Database.SetInitializer<EntityContext>(null);
}
public EntityContext()
: base("Name=Test47Context") {
}
public DbSet<A> A { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Configurations.Add(new AMap());
modelBuilder.Configurations.Add(new CategoryMap());
modelBuilder.Entity<A>()
.Map<Derived>(x => x.Requires("Discriminator").HasValue("Derived"));
}
}
public partial class A {
public int Id { get; set; }
public string Description { get; set; }
}
public class Derived : A {
public Nullable<int> CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public partial class Category {
public Category() {
this.A = new List<A>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<A> A { get; set; }
}
public class AMap : EntityTypeConfiguration<A> {
public AMap() {
// Primary Key
this.HasKey(t => t.Id);
this.Property(t => t.Description)
.IsRequired()
.HasMaxLength(100);
// Table & Column Mappings
this.ToTable("A");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Description).HasColumnName("Description");
}
}
public class CategoryMap : EntityTypeConfiguration<Category> {
public CategoryMap() {
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Name)
.IsRequired()
.HasMaxLength(100);
// Table & Column Mappings
this.ToTable("Categories");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Name).HasColumnName("Name");
}
}
"Add-migration TPH" generates the following
public partial class TPH : DbMigration
{
public override void Up()
{
AddColumn("dbo.A", "Category_Id", c => c.Int());
AddForeignKey("dbo.A", "Category_Id", "dbo.Categories", "Id");
CreateIndex("dbo.A", "Category_Id");
DropColumn("dbo.A", "Discriminator");
}
public override void Down()
{
AddColumn("dbo.A", "Discriminator", c => c.String(nullable: false, maxLength: 20));
DropIndex("dbo.A", new[] { "Category_Id" });
DropForeignKey("dbo.A", "Category_Id", "dbo.Categories");
DropColumn("dbo.A", "Category_Id");
}
}
Why does it drop the Discriminator column instead of just altering it to nvarchar(128)?
Why doesn't just use the existing CategoryId column instead of adding Category_Id?

Fluent NHibernate Mapping Problem (Override)

I have tried to make an inheritation mapping. There are two
alternative. One alternative is with JoinedSubClass (that is
deprecated and give me a warning) and the other and newer is with
SubClassMap.
But I get an error that I have duplicate entity/object mapping. What
could be the problem?
With the JoinedSubClass it runs. Here are the Codes of my two Objects
and the MappingFile with JoinedSubClass which runs, later there are
the code of my mapping file with "SubClassMap" which doesn't run....
Objects:
public class Person {
public virtual int Id { get; private set; }
public virtual string Vorname { get; set; }
public virtual string Nachname { get; set; }
public virtual string Email { get; set; }
public virtual Adresse Adresse { get; set; }
public Person() {
}
}
public class PersonQuelle : Person, IQuelle {
public virtual string Beschreibung { get; set; }
public virtual Institution Institution { get; set; }
public virtual IList<MediaDatei> MediaDateien { get; set; }
public PersonQuelle() {
MediaDateien = new List<MediaDatei>();
}
public virtual void HinzufuegenMediaDatei(MediaDatei mediaDatei) {
mediaDatei.PersonQuelle = this;
MediaDateien.Add(mediaDatei);
}
public virtual void LoeschenMediaDatei(MediaDatei mediaDatei)
{
mediaDatei.PersonQuelle = null;
MediaDateien.Remove(mediaDatei);
}
}
Mapping Files:
public class PersonMap : ClassMap<Person> {
public PersonMap() {
Id(x => x.Id);
Map(x => x.Vorname);
Map(x => x.Nachname);
Map(x => x.Email);
References(x => x.Adresse);
JoinedSubClass<PersonQuelle>("personQuelle_Id", m => {
m.Map(c => c.Beschreibung);
m.References(c => c.Institution);
m.HasMany(c => c.MediaDateien).Inverse().Cascade.All();
});
JoinedSubClass<MMAdminBenutzer>("mmAdminBenutzer_Id", m =>
{
m.Map(c => c.Kuerzel);
m.Map(c => c.Passwort);
m.Map(c => c.Benutzerrolle);
m.HasMany(c => c.MediaDateien).Inverse().Cascade.All();
m.HasMany(c => c.Kategorien).Inverse().Cascade.All();
});
}
}
But why it doesn't run when I do that with the same Objects however
with this mapping file??
Mapping File with SubClassMap:
public class PersonMap : ClassMap<Person> {
public PersonMap() {
Id(x => x.Id);
Map(x => x.Vorname);
Map(x => x.Nachname);
Map(x => x.Email);
References(x => x.Adresse);
}
}
public class PersonQuelleMap : SubclassMap<PersonQuelle>
{
PersonQuelleMap()
{
Map(c => c.Beschreibung);
References(c => c.Institution);
HasMany(c => c.MediaDateien).Inverse().Cascade.All();
}
}
It's hard to tell without the error that you get, but I think you are missing a call to KeyColumn("personQuelle_Id") in your SubclassMap.

Resources