0

背景

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

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

例子

在此示例中,我正在处理检索位置。DB 有一个Location表和一个表,在和LocationType之间具有一对多的关系。对=有外键约束。LocationTypeLocationLocationLocation.LocationTypeIdLocationType.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?任何人都可以建议这样做的首选方式吗?

4

1 回答 1

1

这个解决方案存在一些问题,在@JulieLerman 的一些指导以及 Hibernating Rhinos 的出色Entity Framework Profiler的一些帮助下,我设法找到了这些问题(强烈推荐):

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

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

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

于 2013-10-01T15:03:24.297 回答