EntityFramework - 一对多关系
EntityFramework - One to many relationship
我在 MapHeader 和 MapDetail 之间有一个一对多的关系 tables
因此,单个 mapheader 可以有多个 mapdetails。
在数据库中,table MapDetail 具有映射到 MapHeader table 中的 pk(MapHeaderId) 的外键 MapHeaderID。
我在EntityFramework代码优先中定义如下:
public class MapHeader
{
public int MapHeaderID { get; set; }
....
public virtual ICollection<MapDetail> mapDetails
{
get;
set;
}
}
public class MapDetail
{
public int MapDetailID { get; set; }
public int MapHeaderID { get; set; }
....
public virtual MapHeader mapheader { get; set; }
}
FLUNETAPI
modelBuilder.Entity<MapDetail>()
.HasRequired<MapHeader>(md => md.mapheader)
.WithMany(mh => mh.mapDetails)
.HasForeignKey(md => md.MapHeaderID);
没有link!!我的 mapheader 属性 inside mapdetail record/object 仍然是空的...我做错了什么?
代码 - 用 RAZOR VIEW 编写
foreach (MapDetail geMapDetail in Model.mapDetails)
{
if(...)
{
if(...){...}
else{
<td>
foreach(..)
{
var term = geMapDetail.Term == 0 ? geMapDetail.mapheader.Term : geMapDetail.Term;
}
</td>
}
}
上面的代码崩溃了 geMapDetail.mapheader 因为 mapheader 是 null
我的查询在存储过程中
select distinct md.yearid, md.assessmentid, md.resulttypeid,
concat(ah.name, ' - ', a.name) as Name, md.term, md.semester, md.month, md.week,
md.MapDetailID, md.MapHeaderID, md.ColourFormatType, md.ResultTypeIDs,
md.RowOrder, md.Attendance, md.EffectSize, md.Growth, md.IndicatorID,
md.TeacherNameRequired, md.AllowEdit, md.PageHeaderID, md.IncludePreviousTerms,
md.IncludePreviousSemesters, y.year
from mapdetail md
left outer join assessments a on a.assessmentid = md.assessmentid
left outer join assessments ah on ah.assessmentID = a.headerID
left outer join years y on md.yearID = y.yearID
left outer join assessmentresulttypes art on a.assessmentid = art.assessmentid
left outer join resulttypes rt on rt.resulttypeid = art.resulttypeid
where md.mapheaderid = 22;
DBCONTEXT
public MapDetailResultSet Find_MapDetails(int mapHeaderId, int yearId, string classIds, int indicatorGroup, string indicatorIds)
{
var query = "CALL Find_MapDetails(@mapHeaderId, @yearId, @classIds, @indicatorGroup, @indicatorIds)";
MySqlParameter[] mySqlParams = new MySqlParameter[] { new MySqlParameter("mapHeaderId", mapHeaderId),
new MySqlParameter("yearId", yearId),
new MySqlParameter("classIds", classIds),
new MySqlParameter("indicatorGroup", indicatorGroup),
new MySqlParameter("indicatorIds", indicatorIds)
};
MapDetailResultSet mapdetails = new MapDetailResultSet();
using (var multiResultSet = DbContextExtensions.MultiResultSetSqlQuery(this, query, mySqlParams))
{
mapdetails.mapDetails = multiResultSet.ResultSetFor<MapDetail>().ToList();
//other result sets
...
}
return mapdetails;
}
我还在 DBContext 中禁用了延迟加载:(没有帮助)
public geContext(string connString):base(connString)
{
this.Configuration.LazyLoadingEnabled = false;
Database.SetInitializer(new MySqlInitializer());
}
更新
select distinct md.yearid, md.assessmentid, md.resulttypeid,
concat(ah.name, ' - ', a.name) as Name, md.term, md.semester, md.month, md.week,
md.MapDetailID, md.MapHeaderID, md.ColourFormatType, md.ResultTypeIDs,
md.RowOrder, md.Attendance, md.EffectSize, md.Growth, md.IndicatorID,
md.TeacherNameRequired, md.AllowEdit, md.PageHeaderID,
md.IncludePreviousTerms,
md.IncludePreviousSemesters, y.year
from mapdetail md
left outer join assessments a on a.assessmentid = md.assessmentid
left outer join assessments ah on ah.assessmentID = a.headerID
left outer join years y on md.yearID = y.yearID
left outer join assessmentresulttypes art on a.assessmentid = art.assessmentid
left outer join resulttypes rt on rt.resulttypeid = art.resulttypeid
left outer join mapheader mh on mh.mapheaderID = md.mapheaderID
where md.mapheaderid = 22;
仅使用您的模型 classes,这对我来说效果很好。您甚至不需要 FluentAPI 代码,因为您的 class 定义遵循代码优先外键约定。我使用了这些模型 classes:
public class MapHeader
{
public int MapHeaderId { get; set; }
public virtual List<MapDetail> MapDetails { get; set; }
}
public class MapDetail
{
public int MapDetailId { get; set; }
public int MapHeaderId { get; set; }
public virtual MapHeader MapHeader { get; set; }
}
然后我写了一个简单的命令行程序来保存一些数据并从数据库中读回:
private static void InsertMapHeader()
{
var header = new MapHeader
{
MapDetails = new List<MapDetail>
{
new MapDetail(),
new MapDetail(),
new MapDetail(),
new MapDetail()
}
};
using (var context = new BreakAwayContext())
{
context.MapHeaders.Add(header);
context.SaveChanges();
}
}
private static void ReadMapDetails()
{
using (var context = new BreakAwayContext())
{
var detail = context.MapDetails.FirstOrDefault();
Console.WriteLine("Header id: {0}", detail.MapHeader.MapHeaderId);
}
}
此代码获取第一个 MapDetail 并跟随导航 属性 到 MapHeader。所有这些工作正常,没有问题,因为 Code-First 使用延迟加载来在需要时获取 MapHeader 导航 属性。
如果您想预先加载依赖 属性,您可以更改查询以使用 Include 方法加入 MapHeaders table,如下所示:
var detail = context.MapDetails.Include(d => d.MapHeader).FirstOrDefault();
您仍然没有显示您的查询代码(它可能在您的控件 class 中),因此很难说出确切的问题可能是什么。
这不是理想的方式,但它还是让我通过了..
我从存储库加载特定的 mapheader,然后手动填充列表中的每个 mapdetail 对象
控制器
MapHeaderRepository repMapHeader = new MapHeaderRepository("name=ge");
MapDetailsRepository repMapDetail = new MapDetailsRepository("name=ge");
MapDetailResultSet mapDetailResultSet = repMapDetail.FindMapDetails(mapHeaderId, yearId, classIds,
indicatorGroup, indicatorIds);
var mapHeader = repMapHeader.Get(22);
foreach (MapDetail md in mapDetailResultSet.mapDetails)
{
md.mapheader = mapHeader;
}
我在 MapHeader 和 MapDetail 之间有一个一对多的关系 tables 因此,单个 mapheader 可以有多个 mapdetails。 在数据库中,table MapDetail 具有映射到 MapHeader table 中的 pk(MapHeaderId) 的外键 MapHeaderID。
我在EntityFramework代码优先中定义如下:
public class MapHeader
{
public int MapHeaderID { get; set; }
....
public virtual ICollection<MapDetail> mapDetails
{
get;
set;
}
}
public class MapDetail
{
public int MapDetailID { get; set; }
public int MapHeaderID { get; set; }
....
public virtual MapHeader mapheader { get; set; }
}
FLUNETAPI
modelBuilder.Entity<MapDetail>()
.HasRequired<MapHeader>(md => md.mapheader)
.WithMany(mh => mh.mapDetails)
.HasForeignKey(md => md.MapHeaderID);
没有link!!我的 mapheader 属性 inside mapdetail record/object 仍然是空的...我做错了什么?
代码 - 用 RAZOR VIEW 编写
foreach (MapDetail geMapDetail in Model.mapDetails)
{
if(...)
{
if(...){...}
else{
<td>
foreach(..)
{
var term = geMapDetail.Term == 0 ? geMapDetail.mapheader.Term : geMapDetail.Term;
}
</td>
}
}
上面的代码崩溃了 geMapDetail.mapheader 因为 mapheader 是 null
我的查询在存储过程中
select distinct md.yearid, md.assessmentid, md.resulttypeid,
concat(ah.name, ' - ', a.name) as Name, md.term, md.semester, md.month, md.week,
md.MapDetailID, md.MapHeaderID, md.ColourFormatType, md.ResultTypeIDs,
md.RowOrder, md.Attendance, md.EffectSize, md.Growth, md.IndicatorID,
md.TeacherNameRequired, md.AllowEdit, md.PageHeaderID, md.IncludePreviousTerms,
md.IncludePreviousSemesters, y.year
from mapdetail md
left outer join assessments a on a.assessmentid = md.assessmentid
left outer join assessments ah on ah.assessmentID = a.headerID
left outer join years y on md.yearID = y.yearID
left outer join assessmentresulttypes art on a.assessmentid = art.assessmentid
left outer join resulttypes rt on rt.resulttypeid = art.resulttypeid
where md.mapheaderid = 22;
DBCONTEXT
public MapDetailResultSet Find_MapDetails(int mapHeaderId, int yearId, string classIds, int indicatorGroup, string indicatorIds)
{
var query = "CALL Find_MapDetails(@mapHeaderId, @yearId, @classIds, @indicatorGroup, @indicatorIds)";
MySqlParameter[] mySqlParams = new MySqlParameter[] { new MySqlParameter("mapHeaderId", mapHeaderId),
new MySqlParameter("yearId", yearId),
new MySqlParameter("classIds", classIds),
new MySqlParameter("indicatorGroup", indicatorGroup),
new MySqlParameter("indicatorIds", indicatorIds)
};
MapDetailResultSet mapdetails = new MapDetailResultSet();
using (var multiResultSet = DbContextExtensions.MultiResultSetSqlQuery(this, query, mySqlParams))
{
mapdetails.mapDetails = multiResultSet.ResultSetFor<MapDetail>().ToList();
//other result sets
...
}
return mapdetails;
}
我还在 DBContext 中禁用了延迟加载:(没有帮助)
public geContext(string connString):base(connString)
{
this.Configuration.LazyLoadingEnabled = false;
Database.SetInitializer(new MySqlInitializer());
}
更新
select distinct md.yearid, md.assessmentid, md.resulttypeid,
concat(ah.name, ' - ', a.name) as Name, md.term, md.semester, md.month, md.week,
md.MapDetailID, md.MapHeaderID, md.ColourFormatType, md.ResultTypeIDs,
md.RowOrder, md.Attendance, md.EffectSize, md.Growth, md.IndicatorID,
md.TeacherNameRequired, md.AllowEdit, md.PageHeaderID,
md.IncludePreviousTerms,
md.IncludePreviousSemesters, y.year
from mapdetail md
left outer join assessments a on a.assessmentid = md.assessmentid
left outer join assessments ah on ah.assessmentID = a.headerID
left outer join years y on md.yearID = y.yearID
left outer join assessmentresulttypes art on a.assessmentid = art.assessmentid
left outer join resulttypes rt on rt.resulttypeid = art.resulttypeid
left outer join mapheader mh on mh.mapheaderID = md.mapheaderID
where md.mapheaderid = 22;
仅使用您的模型 classes,这对我来说效果很好。您甚至不需要 FluentAPI 代码,因为您的 class 定义遵循代码优先外键约定。我使用了这些模型 classes:
public class MapHeader
{
public int MapHeaderId { get; set; }
public virtual List<MapDetail> MapDetails { get; set; }
}
public class MapDetail
{
public int MapDetailId { get; set; }
public int MapHeaderId { get; set; }
public virtual MapHeader MapHeader { get; set; }
}
然后我写了一个简单的命令行程序来保存一些数据并从数据库中读回:
private static void InsertMapHeader()
{
var header = new MapHeader
{
MapDetails = new List<MapDetail>
{
new MapDetail(),
new MapDetail(),
new MapDetail(),
new MapDetail()
}
};
using (var context = new BreakAwayContext())
{
context.MapHeaders.Add(header);
context.SaveChanges();
}
}
private static void ReadMapDetails()
{
using (var context = new BreakAwayContext())
{
var detail = context.MapDetails.FirstOrDefault();
Console.WriteLine("Header id: {0}", detail.MapHeader.MapHeaderId);
}
}
此代码获取第一个 MapDetail 并跟随导航 属性 到 MapHeader。所有这些工作正常,没有问题,因为 Code-First 使用延迟加载来在需要时获取 MapHeader 导航 属性。
如果您想预先加载依赖 属性,您可以更改查询以使用 Include 方法加入 MapHeaders table,如下所示:
var detail = context.MapDetails.Include(d => d.MapHeader).FirstOrDefault();
您仍然没有显示您的查询代码(它可能在您的控件 class 中),因此很难说出确切的问题可能是什么。
这不是理想的方式,但它还是让我通过了.. 我从存储库加载特定的 mapheader,然后手动填充列表中的每个 mapdetail 对象
控制器
MapHeaderRepository repMapHeader = new MapHeaderRepository("name=ge");
MapDetailsRepository repMapDetail = new MapDetailsRepository("name=ge");
MapDetailResultSet mapDetailResultSet = repMapDetail.FindMapDetails(mapHeaderId, yearId, classIds,
indicatorGroup, indicatorIds);
var mapHeader = repMapHeader.Get(22);
foreach (MapDetail md in mapDetailResultSet.mapDetails)
{
md.mapheader = mapHeader;
}