我可以在实体框架6-更新数据库命令的种子方法操作上使用foreach循环吗

本文关键字:操作上 子方法 foreach 循环 种子 框架 实体 更新 命令 数据库 我可以 | 更新日期: 2023-09-27 18:09:54

我主要尝试使用foreach循环来遍历每个状态,插入来自Xml的所有城市数据。我们有5000多个城市记录,这是我唯一能想到的方法。

        // Cities Seeding
        foreach (var uf in Enum.GetValues(typeof(Uf)))
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load("http://servicos.cptec.inpe.br/~rserv/estados/cidade-" + uf.ToString() + ".xml");
            int cityId = Convert.ToInt32(xmlDoc.SelectSingleNode("/climas/clima/estados/estado/cidades/cidade").Attributes["id"].Value);
            string cityName = xmlDoc.SelectSingleNode("/climas/clima/estados/estado/cidades/cidade").Attributes["nome"].Value.ToString();
            bool capital = Convert.ToBoolean(xmlDoc.SelectSingleNode("/climas/clima/estados/estado/cidades/cidade").Attributes["capital"].Value);
            int stateId = 1;
            //if (!capital)
            //{
            context.Cities.AddOrUpdate(
                c => c.CityId,
                new City
                {
                    CityId = cityId,
                    CityName = cityName,
                    StateId = stateId
                });
            //}
            //else
            //{
            //context.Capitals.AddOrUpdate(
            //    cp => cp.CityId,
            //    new Capital
            //    {
            //        CityId = cityId,
            //        CityName = cityName,
            //        StateId = stateId
            //    });
            //}
            stateId++;
        }

我不确定在这种情况下是否有什么不同,但是,State Id是States表中的外键。

这是我的错误信息:

System.Data.Entity.Infrastructure.DbUpdateException:更新条目时出错。有关详细信息,请参阅内部异常。--->System.Data.Entity.Core.UpdateException:更新条目时出错。有关详细信息,请参阅内部异常。--->Npgsql.NpgsqlException:错误:23503:在表"City"上插入或更新违反了外键约束"FK_public.City_public.States_StateId">

更新条目时出错。有关详细信息,请参阅内部异常。

我可以在实体框架6-更新数据库命令的种子方法操作上使用foreach循环吗

正如@Sirwan所说,您看到的异常告诉您,您违反了states表的外键约束。根据FK名称判断,您似乎使用了States表中不存在的stateId

在表"City"上插入或更新违反外键约束"FK_public.City_public.States_StateId

你还说:

State Id是States表中的外键

因此,我假设States表是一个预先填充的表,其中已经有状态记录。换句话说,状态不是基于XML文件插入的。是这样吗?

您的代码还做出了两个主要假设:

  1. 它假设Uf枚举中的项目数小于或等于States表中的记录数。

  2. 它假设Uf枚举中的项目与States表中按StatedId升序排序的记录相匹配。

如果假设1无效,即Uf枚举中的记录比States表中的记录多,则将尝试使用不存在的stateId插入记录。

如果假设2无效,即Uf枚举元素的排序与States表中的stateId不匹配,则将城市插入错误的状态。

根据您发布的异常,我敢打赌假设1是无效的,即Uf枚举上的记录数与States表中的记录数不匹配。或者,另一种选择是States表中的StateId值与枚举中的原始元素数不匹配。

例如,我看到你来自巴西,那里有27个州。对于您的错误,我们有三个选项:

  1. States表没有包含所有27个状态
  2. States表中的StateId值不是从1开始的
  3. States表中的StateId值不是连续的,即它们不是从1-27开始的,而是有间隙,如:1,2,3,6,7,8,9 ...

在上面的所有场景中,您的代码最终将使用一个无效的stateId,这将违反FK约束。

编写此代码的不那么脆弱的方法是迭代State表的值,而不是使用Uf枚举,同时可能需要支付少量的性能税。这样,您可以确保分配给记录的StateId值与表中的值匹配:

foreach (var state in context.States)
{
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.Load("http://servicos.cptec.inpe.br/~rserv/estados/cidade-" + state.Name + ".xml");
    int cityId = Convert.ToInt32(xmlDoc.SelectSingleNode("/climas/clima/estados/estado/cidades/cidade").Attributes["id"].Value);
    string cityName = xmlDoc.SelectSingleNode("/climas/clima/estados/estado/cidades/cidade").Attributes["nome"].Value.ToString();
    context.Cities.AddOrUpdate(
        c => c.CityId,
        new City
        {
            CityId = cityId,
            CityName = cityName,
            StateId = state.StateId
        });
}

此错误表明您违反了外键约束。请检查数据库中是否存在与FK约束冲突的现有数据,从而导致创建失败。

还有一件事:为什么要增加stateId?正如您所提到的,您有5000多个城市记录,因此State表也必须包含多个记录。

这就是我的代码最终的样子:

context.SaveChanges();
    //Cities Seeding
    int stateId = 1;
    foreach (var uf in Enum.GetValues(typeof(Uf)))
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load("http://servicos.cptec.inpe.br/~rserv/estados/cidade-" + uf.ToString() + ".xml");
        XmlElement root = xmlDoc.DocumentElement;
        XmlNodeList nodes = root.SelectNodes("/climas/clima/estados/estado/cidades/cidade");
        foreach (XmlNode node in nodes)
        {
            int cityId = Convert.ToInt32(node.Attributes["id"].Value);
            string cityName = node.Attributes["nome"].Value.ToString();
            bool cityIsCapital = Convert.ToBoolean(node.Attributes["capital"].Value);
            context.Cities.AddOrUpdate(
            c => c.CityId,
            new City()
            {
                CityId = cityId,
                CityName = cityName,
                CityIsCapital = cityIsCapital,
                StateId = stateId
            });
        }
        stateId++;
    }