【问题标题】:What is the best approach to saving/updating entities using Entity Framework retrieved from a XML feed?使用从 XML 提要检索的实体框架保存/更新实体的最佳方法是什么?
【发布时间】:2013-10-01 23:03:24
【问题描述】:

背景

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

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

示例

在此示例中,我正在处理检索位置。 DB有一个Location表和一个LocationType表,LocationTypeLocation之间是一对多的关系。 LocationLocation.LocationTypeId 有一个外键约束 = LocationType.LocationTypeId

我需要验证 XML 位置是否存在于数据库中,所以我首先使用 XML 提要位置 ID 检索它:如果它为空,我正在处理一个新位置;如果它不为空,那么我正在处理一个现有位置,我需要更新它。

// 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?任何人都可以建议这样做的首选方式吗?

【问题讨论】:

  • 如果不摆弄这个,我可能会担心将同一实体两次加载到您的上下文中的可能性,这会在您的更新中引发异常。
  • 这将是一个真正的问题。你会在哪里看到这种情况发生?也就是说,你能提供更多细节吗?
  • 假设您执行var x = context.Location.Single(y =&gt; y.id == id) 之类的操作稍后,您执行IEnumerable&lt;Location&gt; locs = context.Locations.Where(z =&gt; z.Name.Contains("a"); 之类的操作,这会再次拉入原始实体 - 这将引发 DbUpdateException,因为您将拥有多个具有相同键的实体在你的上下文中。现在我再次查看您的代码,我认为您没问题,因为您将 context 的范围保持在较小的范围内,我认为不会有两次拉入同一个实体的风险。

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


【解决方案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() 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-08
    • 2022-10-15
    • 1970-01-01
    相关资源
    最近更新 更多