EF代码优先:定义外键
本文关键字:定义 代码 EF | 更新日期: 2023-09-27 18:27:17
我看过其他一些答案和很多文章,但这一简单的部分仍然让我难以捉摸。我首先使用EF代码4.1,但如果它更容易的话,我很乐意转到一个新的版本。我有一个这样的主要事实表:
namespace Spend.Models
{
public class ExpenseItem
{
[Key]
public String UniqueID_ERLineID { get; set; }
public String ERNum { get; set; }
public String ItemNum { get; set; }
public String Parent_Expense_Item { get; set; }
public String Card_Number { get; set; }
...
以及与ExpenseItems:有多对一关系的几个表
public class ExpenseItemAccounting
{
[Key]
public String UniqueID_Accounting { get; set; }
public String ERLineID { get; set; }
public String ERNum { get; set; }
public String ItemNum { get; set; }
正如我们所看到的,第二个表中的ERLineID连接到第一个表中UniqueID_ERLineID,所以我通常依赖的"约定"不起作用。因此,我需要使用一个虚拟ICollection,但我希望它将这些字段指定为链接。如有任何关于如何做到这一点的帮助,我们将不胜感激。
PS。我目前无法重命名DB字段。
@卢克:
我应用了你提到的更改,它们是有意义的。然而,我得到以下错误:
System.Data.Entity.ModelConfiguration.ModelValidationException occurred
Message=One or more validation errors were detected during model generation:
System.Data.Edm.EdmAssociationType: : Multiplicity conflicts with the referential constraint in Role 'ExpenseItemAccounting_ExpenseItem_Target' in relationship 'ExpenseItemAccounting_ExpenseItem'. Because all of the properties in the Dependent Role are non-nullable, multiplicity of the Principal Role must be '1'.
System.Data.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'ExpenseItemAccounting_ExpenseItem_Source' in relationship 'ExpenseItemAccounting_ExpenseItem'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be �1�.
Source=EntityFramework
StackTrace:
at System.Data.Entity.ModelConfiguration.Edm.EdmModelExtensions.ValidateAndSerializeCsdl(EdmModel model, XmlWriter writer)
at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext)
at System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input)
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.SelectMany[TSource,TCollection,TResult](IQueryable`1 source, Expression`1 collectionSelector, Expression`1 resultSelector)
at EmailClient.Prog.getData() in C:'MF'Dropbox'Dev_LN_Projects'04_QA'EmailClient'EmailClient'Prog.cs:line 172
InnerException:
当我尝试以下linq查询时出现了这种情况:
var geee = (from e in db.ExpenseItems
from f in db.ExpenseItemFbtItems
where
e.Item_Transaction_Date.Value.Year == 2011 &&
e.Item_Transaction_Date.Value.Month == 8
select new { A = e.UniqueID_ERLineID, B = f.ERLineID.First() });
实际上,我希望能够说e.ExpenseItemAccounting.ItemNum或类似的话——我需要在ExpenseItem定义中添加一些东西来实现这一点吗?
我的模型设置如下。基地。OnModelCreating是通过intellisense出现的,我尝试过使用/不使用它,得到了相同的结果:
public class SpendDB : DbContext
{
public DbSet<ExpenseAttachment> ExpenseAttachments {get; set; }
public DbSet<ExpenseComment> ExpenseComments {get; set; }
public DbSet<ExpenseItemAccounting> ExpenseAccountings {get; set; }
public DbSet<ExpenseItemFbtItem> ExpenseItemFbtItems {get; set; }
public DbSet<ExpenseItem> ExpenseItems {get; set; }
public DbSet<ExpenseItemViolation> ExpenseItemViolations {get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ExpenseItemAccounting>().HasOptional(e => e.ExpenseItem).WithMany().HasForeignKey(e => e.UniqueID_Accounting);
}
}
也许我需要将虚拟ICollection放入ExpenseItem定义中?或者它可能是另一种方式——比如modelBuilder.Entity有可选的ExpenseItemAccounting?这对我来说听起来更直观,但我(显然)不太擅长这一点,所以对此持怀疑态度!
再次感谢
这样做:
public class ExpenseItemAccounting
{
[Key]
public String UniqueID_Accounting { get; set; }
public ExpenseItem ExpenseItem{get;set;}
public String ERLineID { get; set; }
public String ERNum { get; set; }
public String ItemNum { get; set; }
}
然后在您的modelBuilder中使用
modelBuilder.Entity<ExpenseItemAccounting>()
.HasOptional(e => e.ExpenseItem).WithMany()
.HasForeignKey(e => e.UniqueID_Accounting );
编辑:
要将导航属性配置为在另一端有一个集合,只需像下面的一样添加即可
public class ExpenseItem
{
[Key]
public String UniqueID_ERLineID { get; set; }
public String ERNum { get; set; }
public String ItemNum { get; set; }
public String Parent_Expense_Item { get; set; }
public String Card_Number { get; set; }
public ICollection<ExpenseItemAccounting> ExpenseItemAccountings{ get; set; }
}
然后通过如下修改模型构建器配置将其连接起来:
modelBuilder.Entity<ExpenseItemAccounting>()
.HasOptional(e => e.ExpenseItem).WithMany(e=> e.ExpenseItems)
.HasForeignKey(e => e.UniqueID_Accounting );
这将把它连接起来,这样ExpenseItem就会有一个所有子ExpensItemAccounting的列表,如果更有意义的话,你也可以添加一个单一版本,比如:
public class ExpenseItem
{
[Key]
public String UniqueID_ERLineID { get; set; }
public String ERNum { get; set; }
public String ItemNum { get; set; }
public String Card_Number { get; set; }
public ExpenseItemAccounting Parent_Expense_Item { get; set; }
}
并使用modelBuilder进行配置:
modelBuilder.Entity<ExpenseItemAccounting>()
.HasOptional(e => e.ExpenseItem)
.WithOptionalDependent(e=>e.Parent_Expense_Item);
我认为,如果你还想连接FK引用(不仅仅是导航属性),你需要在一个单独的语句中这样做,但这有点麻烦。
看看MSDN上关于导航属性的页面,以及如何使用modelBuilder,因为它有很多高级内容的好例子。
http://msdn.microsoft.com/en-us/library/hh295843(v=vs.103).aspx
其中一个解决方案是将ERLineID的可空值更改为TRUE