EF6 中的自引用 table
Self-referencing table in EF6
我认为这会很容易......我有一个 table Module
的情况,它可以包含 "base" 个模块,并且 "compound" 模块(由 1-n 个基本模块组成)。
所以我在 SQL Server 2014 中有这两个 table:
CREATE TABLE Module
(
ModuleId INT NOT NULL IDENTITY(1,1)
CONSTRAINT PK_Module PRIMARY KEY CLUSTERED,
ModuleName VARCHAR(100)
)
CREATE TABLE CompoundModule
(
CompoundModuleId INT NOT NULL
CONSTRAINT FK_CompoundModule_MainModule
FOREIGN KEY REFERENCES dbo.Module(ModuleId),
BaseModuleId INT NOT NULL
CONSTRAINT FK_CompoundModule_BaseModules
FOREIGN KEY REFERENCES dbo.Module(ModuleId),
CONSTRAINT PK_CompoundModule
PRIMARY KEY CLUSTERED(CompoundModuleId, BaseModuleId)
)
我填写了几个基础模块:
INSERT INTO dbo.Module (ModuleName)
VALUES ('Base Module #1'), ('Base Module #2'), ('Base Module #3')
现在我创建了一个 EF 6 "code-first, reverse-engineer from database" 模型并得到这个 Module
class:
[Table("Module")]
public partial class Module
{
public Module()
{
Module1 = new HashSet<Module>();
Module2 = new HashSet<Module>();
}
public int ModuleId { get; set; }
public string ModuleName { get; set; }
public virtual ICollection<Module> Module1 { get; set; }
public virtual ICollection<Module> Module2 { get; set; }
}
和这个上下文 class:
public partial class ModuleCtx : DbContext
{
public ModuleCtx() : base("name=ModuleCtx")
{ }
public virtual DbSet<Module> Module { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Module>()
.Property(e => e.ModuleName)
.IsUnicode(false);
modelBuilder.Entity<Module>()
.HasMany(e => e.Module1)
.WithMany(e => e.Module2)
.Map(m => m.ToTable("CompoundModule").MapLeftKey("BaseModuleId").MapRightKey("CompoundModuleId"));
}
}
当我现在尝试使用这段代码创建一个新的复合模块时,事实证明事情并不像我想的那么简单......
using (ModuleCtx ctx = new ModuleCtx())
{
Module newCompound = new Module();
Module baseModule1 = ctx.Module.FirstOrDefault(m => m.ModuleId == 1);
Module baseModule3 = ctx.Module.FirstOrDefault(m => m.ModuleId == 3);
newCompound.BaseModules.Add(baseModule1);
newCompound.BaseModules.Add(baseModule3);
ctx.Module.Add(newCompound);
ctx.SaveChanges();
}
此代码导致错误(在试图获取基本模块 #1 的行上):
System.Data.Entity.Core.EntityCommandExecutionException was unhandled
HResult=-2146232004
Message=An error occurred while executing the command definition. See the inner exception for details.
Source=EntityFramework
InnerException: System.Data.SqlClient.SqlException
HResult=-2146232060
Message=Invalid column name 'Module_ModuleId'.
我在这里错过了什么??为什么 EF6 逆向工程代码不够智能,无法创建在这种情况下 有效 的模型??
到目前为止,我一直在使用 EF4 和数据库优先方法,所以所有这些流畅的代码优先配置对我来说仍然有点神秘(和问题)......有没有人看到我的(很可能非常)明显的菜鸟错误??
PS: 这是 "Code-first from existing database" 逆向工程生成的代码——不是我自己的。那为什么逆向工程最后输出的代码不起作用?
试试我的生成器 EntityFramework Reverse POCO Generator 看看它是否更适合你。
它生成了以下代码(底部有趣的东西):
public interface IMyDbContext : System.IDisposable
{
System.Data.Entity.DbSet<Module> Modules { get; set; } // Module
int SaveChanges();
System.Threading.Tasks.Task<int> SaveChangesAsync();
System.Threading.Tasks.Task<int> SaveChangesAsync(System.Threading.CancellationToken cancellationToken);
}
public class MyDbContext : System.Data.Entity.DbContext, IMyDbContext
{
public System.Data.Entity.DbSet<Module> Modules { get; set; } // Module
static MyDbContext()
{
System.Data.Entity.Database.SetInitializer<MyDbContext>(null);
}
public MyDbContext()
: base("Name=MyDbContext")
{
}
public MyDbContext(string connectionString)
: base(connectionString)
{
}
public MyDbContext(string connectionString, System.Data.Entity.Infrastructure.DbCompiledModel model)
: base(connectionString, model)
{
}
public MyDbContext(System.Data.Common.DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
}
public MyDbContext(System.Data.Common.DbConnection existingConnection, System.Data.Entity.Infrastructure.DbCompiledModel model, bool contextOwnsConnection)
: base(existingConnection, model, contextOwnsConnection)
{
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ModuleConfiguration());
}
public static System.Data.Entity.DbModelBuilder CreateModel(System.Data.Entity.DbModelBuilder modelBuilder, string schema)
{
modelBuilder.Configurations.Add(new ModuleConfiguration(schema));
return modelBuilder;
}
}
public class Module
{
public int ModuleId { get; set; } // ModuleId (Primary key)
public string ModuleName { get; set; } // ModuleName (length: 100)
// Reverse navigation
public virtual System.Collections.Generic.ICollection<Module> BaseModule { get; set; } // Many to many mapping
public virtual System.Collections.Generic.ICollection<Module> CompoundModule { get; set; } // Many to many mapping
public Module()
{
BaseModule = new System.Collections.Generic.List<Module>();
CompoundModule = new System.Collections.Generic.List<Module>();
}
}
// Module
public class ModuleConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<Module>
{
public ModuleConfiguration()
: this("dbo")
{
}
public ModuleConfiguration(string schema)
{
ToTable("Module", schema);
HasKey(x => x.ModuleId);
Property(x => x.ModuleId).HasColumnName(@"ModuleId").IsRequired().HasColumnType("int").HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.ModuleName).HasColumnName(@"ModuleName").IsOptional().IsUnicode(false).HasColumnType("varchar").HasMaxLength(100);
HasMany(t => t.CompoundModule).WithMany(t => t.BaseModule).Map(m =>
{
m.ToTable("CompoundModule", "dbo");
m.MapLeftKey("BaseModuleId");
m.MapRightKey("CompoundModuleId");
});
}
}
我认为这会很容易......我有一个 table Module
的情况,它可以包含 "base" 个模块,并且 "compound" 模块(由 1-n 个基本模块组成)。
所以我在 SQL Server 2014 中有这两个 table:
CREATE TABLE Module
(
ModuleId INT NOT NULL IDENTITY(1,1)
CONSTRAINT PK_Module PRIMARY KEY CLUSTERED,
ModuleName VARCHAR(100)
)
CREATE TABLE CompoundModule
(
CompoundModuleId INT NOT NULL
CONSTRAINT FK_CompoundModule_MainModule
FOREIGN KEY REFERENCES dbo.Module(ModuleId),
BaseModuleId INT NOT NULL
CONSTRAINT FK_CompoundModule_BaseModules
FOREIGN KEY REFERENCES dbo.Module(ModuleId),
CONSTRAINT PK_CompoundModule
PRIMARY KEY CLUSTERED(CompoundModuleId, BaseModuleId)
)
我填写了几个基础模块:
INSERT INTO dbo.Module (ModuleName)
VALUES ('Base Module #1'), ('Base Module #2'), ('Base Module #3')
现在我创建了一个 EF 6 "code-first, reverse-engineer from database" 模型并得到这个 Module
class:
[Table("Module")]
public partial class Module
{
public Module()
{
Module1 = new HashSet<Module>();
Module2 = new HashSet<Module>();
}
public int ModuleId { get; set; }
public string ModuleName { get; set; }
public virtual ICollection<Module> Module1 { get; set; }
public virtual ICollection<Module> Module2 { get; set; }
}
和这个上下文 class:
public partial class ModuleCtx : DbContext
{
public ModuleCtx() : base("name=ModuleCtx")
{ }
public virtual DbSet<Module> Module { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Module>()
.Property(e => e.ModuleName)
.IsUnicode(false);
modelBuilder.Entity<Module>()
.HasMany(e => e.Module1)
.WithMany(e => e.Module2)
.Map(m => m.ToTable("CompoundModule").MapLeftKey("BaseModuleId").MapRightKey("CompoundModuleId"));
}
}
当我现在尝试使用这段代码创建一个新的复合模块时,事实证明事情并不像我想的那么简单......
using (ModuleCtx ctx = new ModuleCtx())
{
Module newCompound = new Module();
Module baseModule1 = ctx.Module.FirstOrDefault(m => m.ModuleId == 1);
Module baseModule3 = ctx.Module.FirstOrDefault(m => m.ModuleId == 3);
newCompound.BaseModules.Add(baseModule1);
newCompound.BaseModules.Add(baseModule3);
ctx.Module.Add(newCompound);
ctx.SaveChanges();
}
此代码导致错误(在试图获取基本模块 #1 的行上):
System.Data.Entity.Core.EntityCommandExecutionException was unhandled
HResult=-2146232004
Message=An error occurred while executing the command definition. See the inner exception for details.
Source=EntityFrameworkInnerException: System.Data.SqlClient.SqlException
HResult=-2146232060
Message=Invalid column name 'Module_ModuleId'.
我在这里错过了什么??为什么 EF6 逆向工程代码不够智能,无法创建在这种情况下 有效 的模型??
到目前为止,我一直在使用 EF4 和数据库优先方法,所以所有这些流畅的代码优先配置对我来说仍然有点神秘(和问题)......有没有人看到我的(很可能非常)明显的菜鸟错误??
PS: 这是 "Code-first from existing database" 逆向工程生成的代码——不是我自己的。那为什么逆向工程最后输出的代码不起作用?
试试我的生成器 EntityFramework Reverse POCO Generator 看看它是否更适合你。
它生成了以下代码(底部有趣的东西):
public interface IMyDbContext : System.IDisposable
{
System.Data.Entity.DbSet<Module> Modules { get; set; } // Module
int SaveChanges();
System.Threading.Tasks.Task<int> SaveChangesAsync();
System.Threading.Tasks.Task<int> SaveChangesAsync(System.Threading.CancellationToken cancellationToken);
}
public class MyDbContext : System.Data.Entity.DbContext, IMyDbContext
{
public System.Data.Entity.DbSet<Module> Modules { get; set; } // Module
static MyDbContext()
{
System.Data.Entity.Database.SetInitializer<MyDbContext>(null);
}
public MyDbContext()
: base("Name=MyDbContext")
{
}
public MyDbContext(string connectionString)
: base(connectionString)
{
}
public MyDbContext(string connectionString, System.Data.Entity.Infrastructure.DbCompiledModel model)
: base(connectionString, model)
{
}
public MyDbContext(System.Data.Common.DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
}
public MyDbContext(System.Data.Common.DbConnection existingConnection, System.Data.Entity.Infrastructure.DbCompiledModel model, bool contextOwnsConnection)
: base(existingConnection, model, contextOwnsConnection)
{
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ModuleConfiguration());
}
public static System.Data.Entity.DbModelBuilder CreateModel(System.Data.Entity.DbModelBuilder modelBuilder, string schema)
{
modelBuilder.Configurations.Add(new ModuleConfiguration(schema));
return modelBuilder;
}
}
public class Module
{
public int ModuleId { get; set; } // ModuleId (Primary key)
public string ModuleName { get; set; } // ModuleName (length: 100)
// Reverse navigation
public virtual System.Collections.Generic.ICollection<Module> BaseModule { get; set; } // Many to many mapping
public virtual System.Collections.Generic.ICollection<Module> CompoundModule { get; set; } // Many to many mapping
public Module()
{
BaseModule = new System.Collections.Generic.List<Module>();
CompoundModule = new System.Collections.Generic.List<Module>();
}
}
// Module
public class ModuleConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<Module>
{
public ModuleConfiguration()
: this("dbo")
{
}
public ModuleConfiguration(string schema)
{
ToTable("Module", schema);
HasKey(x => x.ModuleId);
Property(x => x.ModuleId).HasColumnName(@"ModuleId").IsRequired().HasColumnType("int").HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.ModuleName).HasColumnName(@"ModuleName").IsOptional().IsUnicode(false).HasColumnType("varchar").HasMaxLength(100);
HasMany(t => t.CompoundModule).WithMany(t => t.BaseModule).Map(m =>
{
m.ToTable("CompoundModule", "dbo");
m.MapLeftKey("BaseModuleId");
m.MapRightKey("CompoundModuleId");
});
}
}