C# 使用从XML提要检索的实体框架保存/更新实体的最佳方法是什么?
背景: 简单地说,我目前正在使用ASP.NET MVC和实体框架开发一个应用程序,它定期从XML提要中提取数据,并将数据保存到数据库,添加新记录和/或更新现有记录 我目前的方法是检索XML提要(使用XmlReader将XML数据反序列化为从C# 使用从XML提要检索的实体框架保存/更新实体的最佳方法是什么?,c#,xml,asp.net-mvc,entity-framework,C#,Xml,Asp.net Mvc,Entity Framework,背景: 简单地说,我目前正在使用ASP.NET MVC和实体框架开发一个应用程序,它定期从XML提要中提取数据,并将数据保存到数据库,添加新记录和/或更新现有记录 我目前的方法是检索XML提要(使用XmlReader将XML数据反序列化为从xsd.exe工具创建的类)。然后,我遍历检索到的XML数据集合,创建并创建EF类/实体(通过EF Power Tools和反向工程代码优先方法创建),并将这些新的/更新的实体保存到数据库中 示例 在本例中,我处理的是检索位置。DB有一个位置表和位置类型表,在
xsd.exe
工具创建的类)。然后,我遍历检索到的XML数据集合,创建并创建EF类/实体(通过EF Power Tools和反向工程代码优先方法创建),并将这些新的/更新的实体保存到数据库中
示例
在本例中,我处理的是检索位置。DB有一个位置
表和位置类型
表,在位置类型
和位置
之间有一对多关系Location
对Location.LocationTypeId
=LocationType.LocationTypeId
具有外键约束
我需要验证数据库中是否存在XML位置,因此我首先使用XML提要位置ID检索它:如果为null,则表示我正在处理一个新位置;如果它不是空的,那么我正在处理一个现有的位置,我需要更新它
// LOCATION SERVICE
private void LoadLocations()
{
// retreive XML location data
List<locationsLocation> locationsFeed = _xmlFeedRepository.GetLocations().ToList();
// iterate through each location and save to DB
foreach (var fl in locationsFeed)
{
// get location from DB using XML location feedId
var location = _locationRepository.GetLocationByFeedId(fl.id);
if (location == null)
{
// add location
HydrateLocation(ref location, fl);
_locationRepository.AddLocation(location);
}
else
{
// update location
HydrateLocation(ref location, fl);
_locationRepository.UpdateLocation(location);
}
}
}
private void HydrateLocation(ref Location location, locationsLocation fl)
{
if (location == null)
{
// create new location
location = new Location();
}
// get location type
var locationType = _locationRepository.GetLocationTypeByName(fl.type);
location.Name = fl.name;
location.FeedId = fl.id;
// add existing locationType or create new locationType
location.LocationType = locationType ?? new LocationType { Name = fl.type };
}
// LOCATION REPOSITORY
public void AddLocation(Location location)
{
if (location != null)
{
using (var context = new MyDBContext())
{
context.Locations.Add(location);
context.SaveChanges();
}
}
}
public void UpdateLocation(Location location)
{
if (location != null)
{
using (var context = new MyDBContext())
{
context.Locations.Attach(location);
context.Entry(location).State = EntityState.Modified;
context.SaveChanges();
}
}
}
public Location GetLocationByFeedId(int feedId)
{
Location location = null;
if (feedId > 0)
{
using (var context = new MyDBContext())
{
location = context.Locations.FirstOrDefault(l => l.FeedId == feedId);
}
}
return location;
}
//位置服务
专用void加载位置()
{
//检索XML位置数据
List locationsFeed=_xmlFeedRepository.GetLocations().ToList();
//遍历每个位置并保存到数据库
foreach(位置中的var fl FEED)
{
//使用XML位置feedId从数据库获取位置
var location=\u locationRepository.GetLocationByFeedId(fl.id);
if(位置==null)
{
//添加位置
水合定位(参考位置,fl);
_locationRepository.AddLocation(位置);
}
其他的
{
//更新位置
水合定位(参考位置,fl);
_locationRepository.UpdateLocation(位置);
}
}
}
专用位置(参考位置,位置位置fl)
{
if(位置==null)
{
//创建新位置
位置=新位置();
}
//获取位置类型
var locationType=\u locationRepository.GetLocationTypeByName(fl.type);
location.Name=fl.Name;
location.FeedId=fl.id;
//添加现有locationType或创建新locationType
location.LocationType=LocationType??新LocationType{Name=fl.type};
}
//位置存储库
公共无效添加位置(位置)
{
如果(位置!=null)
{
使用(var context=new MyDBContext())
{
context.Locations.Add(位置);
SaveChanges();
}
}
}
public void UpdateLocation(位置)
{
如果(位置!=null)
{
使用(var context=new MyDBContext())
{
上下文。位置。附加(位置);
context.Entry(location).State=EntityState.Modified;
SaveChanges();
}
}
}
公共位置GetLocationByFeedId(int feedId)
{
位置=空;
如果(feedId>0)
{
使用(var context=new MyDBContext())
{
location=context.Locations.FirstOrDefault(l=>l.FeedId==FeedId);
}
}
返回位置;
}
问题/担忧
这是添加/更新具有相关实体(例如添加/更新位置及其locationType)的实体的正确方法吗?有人能推荐一种更好的方法吗?这个解决方案有一些问题,在@Julierlerman的指导和休眠犀牛的帮助下,我设法找到了这些问题(强烈推荐):
\u locationRepository.GetLocationByFeedId(fl.id)代码>对于每个XML实体来说都是过火了。我已经完全修改了解决方案,但更好的解决方案是对DB进行一次调用,取出所有位置并将它们存储在内存中,然后使用内存中的集合检查该位置是否已经存在。这同样适用于\u locationRepository.GetLocationTypeByName(fl.type)代码>
location.locationType=locationType??新位置类型{Name=fl.type}
可能会导致重复记录(因为EF认为locationType是一个新的locationType)。更安全的做法是只设置location实体的locationType外键,例如location.LocationTypeId=locationType.LocationTypeId
SaveChanges()
方法,而不是使用块将每个对上下文的调用包装在中
如果不做这些,我可能会担心将同一实体加载两次到您的上下文中的可能性,这会在您的更新中引发异常。这将是一个真正的问题。你会在哪里看到这种情况发生?i、 你能提供一些更详细的信息吗?假设你做了类似
var x=context.Location.Single(y=>y.id==id)
之后,你做了类似IEnumerable locs=context.Locations.Where(z=>z.Name.Contains(“a”)的事情;
,它再次拉入原始实体-这将抛出DbUpdateException,因为在您的上下文中有多个具有相同密钥的实体。现在我再次查看您的代码,我认为您没有问题,因为您将上下文的范围保持在很小的范围内,我不相信拉入同一实体的风险两次。