使用从XML Feed检索的Entity Framework保存/更新实体的最佳方法是什么?

时间:2013-09-23 10:12:47

标签: c# xml asp.net-mvc entity-framework

背景

简单地说,我目前正在使用ASP.NET MVC和Entity Framework开发一个应用程序,该应用程序定期从XML Feed中提取数据并将数据保存到数据库,添加新记录和/或更新现有记录。

我目前的方法是检索XML提要(使用XmlReader将XML数据反序列化为从xsd.exe工具创建的类)。然后,我遍历检索的XML数据集合,创建和保存EF类/实体(通过EF Power Tools和反向工程师代码优先方法创建),并将每个新的/更新的实体保存到数据库中。

示例

在这个例子中,我正在处理retreiving位置。数据库有一个Location表和一个LocationType表,LocationTypeLocation之间存在一对多的关系。 LocationLocation.LocationTypeId = LocationType.LocationTypeId有一个外键约束。

我需要验证数据库中是否存在XML位置,因此我首先使用XML提要位置ID检索它:如果它为null,则我正在处理新位置;如果它不是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;
}

问题/关注

这是添加/更新具有相关实体的实体的正确方法,例如,添加/更新位置及其locationType吗?任何人都可以建议这样做的首选方式吗?

1 个答案:

答案 0 :(得分:1)

这个解决方案存在一些问题,我在@JulieLerman的一些指导下设法追踪,并且通过Hibernating Rhinos的强大Entity Framework Profiler提供了一些帮助(强烈推荐):

  1. 考虑到初始批量导入的记录数(大约20K行),对每个XML实体执行_locationRepository.GetLocationByFeedId(fl.id);是过度的。我已经完全重写了解决方案,但更好的解决方案是对数据库进行一次调用并拉出所有位置并将它们存储在内存中并使用内存集合来检查位置是否已存在。这同样适用于_locationRepository.GetLocationTypeByName(fl.type);

  2. 使用location.LocationType = locationType ?? new LocationType { Name = fl.type };设置locationType可能会导致重复记录(因为EF会使locationType成为新的locationType)。仅设置位置实体的locationType外键更安全,例如, location.LocationTypeId = locationType.locationTypeId

  3. 不是将每个调用包装到using块中的上下文,而是更好的方法是在存储库中声明上下文的私有实例,并声明单独的SaveChanges()方法。