在EF 6.1中的DbContext中创建DbSet的顺序

时间:2016-05-26 06:07:17

标签: c# entity-framework dbcontext

我需要订购DbSet<>的创建在DbContext中。因为:

顺便说一下,DbSets已经在DbContext中订购,但在创建迁移时似乎没有效果。

在我的DbContext中:

public class CmsDbContext : DbContext
{
    // Basic
    public DbSet<Country> Countries { get; set; }
    public DbSet<District> Districts { get; set; }
    public DbSet<Province> Provinces { get; set; }
    public DbSet<City> Cities { get; set; }
    public DbSet<Area> Areas { get; set; }
    public DbSet<CustomerType> CustomerTypes { get; set; }
    public DbSet<IdType> IdTypes { get; set; }
    public DbSet<IncomeType> IncomeTypes { get; set; }
    public DbSet<OutcomeType> OutcomeTypes { get; set; }
    public DbSet<PaymentType> PaymentTypes { get; set; }
}

如你所见,国家是第一个。 发出后

add-migration initial

DbSets的顺序不是我想要的:

public partial class initial : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.Area",
            c => new
                {
                    AreaId = c.Int(nullable: false, identity: true),
                    CityId = c.Int(nullable: false),
                    AreaArabicName = c.String(nullable: false, maxLength: 35, unicode: false),
                    AreaEnglishName = c.String(nullable: false, maxLength: 35, unicode: false),
                    AddedDate = c.DateTime(),
                    ModifiedDate = c.DateTime(),
                })
            .PrimaryKey(t => t.AreaId)
            .ForeignKey("dbo.City", t => t.CityId)
            .Index(t => t.CityId);

        CreateTable(
            "dbo.City",
            c => new
                {
                    CityId = c.Int(nullable: false, identity: true),
                    ProvinceId = c.Int(nullable: false),
                    CityArabicName = c.String(nullable: false, maxLength: 35, unicode: false),
                    CityEnglishName = c.String(nullable: false, maxLength: 35, unicode: false),
                    AddedDate = c.DateTime(),
                    ModifiedDate = c.DateTime(),
                })
            .PrimaryKey(t => t.CityId)
            .ForeignKey("dbo.Province", t => t.ProvinceId)
            .Index(t => t.ProvinceId);

依旧......

请注意,国家/地区不是第一个要创建的表。

当我发出:

update-database

创建数据库,表也创建了,但种子方法没有。我有例外:

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Area_dbo.City_CityId".

如果我停止播种城市,就不会发生这种情况。

我希望现在问题很清楚。

这是城市

种子
class CityDefaultData
{
    private readonly string[,] _cities = new string[,]
    {
        {"1", "الرياض", "Riyadh"},
        {"2", "جدة", "Jeddah"},
        {"3", "الدمام", "Dammam"},
        {"4", "بريدة", "Buraidah"}
    };

    private readonly List<City> _newCitiess = new List<City>();

    public List<City> GetDefaultCity()
    {
        for (var i = 0; i <= _cities.GetUpperBound(0); i++)
        {
            _newCitiess.Add(new City()
            {
                ProvinceId = Convert.ToInt32(_cities[i, 0]),
                CityArabicName = _cities[i, 1],
                CityEnglishName = _cities[i, 2]
            });
        }
        return _newCitiess;
    }
}

区域种子

class AreaDefaultData
{
    private readonly string[,] _areas = new string[,]
    {
        {"1", "السلام", "Salam"},
        {"1", "العليا", "Olayya"},
        {"1", "الروابي", "Rawabi"},
        {"1", "القدس", "Quds"},
        {"1", "المنار", "Manar"},
        {"2", "السلام", "Salam"},
        {"2", "العليا", "Olayya"},
        {"2", "الروابي", "Rawabi"},
        {"2", "القدس", "Quds"},
        {"2", "المنار", "Manar"}
    };


    private readonly List<Area> _newAreas = new List<Area>();

    public List<Area> GetDefaultArea()
    {
        for (var i = 0; i <= _areas.GetUpperBound(0); i++)
        {
            _newAreas.Add(new Area()
            {
                CityId = Convert.ToInt32(_areas[i, 0]),
                AreaArabicName = _areas[i, 1],
                AreaEnglishName = _areas[i, 2]
            });
        }
        return _newAreas;
    }
}

这是我的DbInitialize

class DbInitializer
{
    public void DataSeed(CmsDbContext context)
    {
        // Country
        List<Country> coutry =
            new CountryDefaultData().GetDefaultCountry();
        coutry.ForEach(e => context.Countries.Add(e));
        context.Countries.AddOrUpdate();

        // District
        List<District> district =
            new DistrictDefaultData().GetDefaultDistrict();
        district.ForEach(e => context.Districts.Add(e));
        context.Districts.AddOrUpdate();

        // Province
        List<Province> province =
            new ProvinceDefaultData().GetDefaultProvince();
        province.ForEach(e => context.Provinces.Add(e));
        context.Provinces.AddOrUpdate();

        // City
        List<City> city =
            new CityDefaultData().GetDefaultCity();
        city.ForEach(e => context.Cities.Add(e));
        context.Cities.AddOrUpdate();

        // Area
        List<Area> area =
            new AreaDefaultData().GetDefaultArea();
        area.ForEach(e => context.Areas.Add(e));
        context.Areas.AddOrUpdate();

        // Id Type
        List<IdType> idType =
            new IdTypeDefultData().GetDefaultIdTypes();
        idType.ForEach(e => context.IdTypes.Add(e));
        context.IdTypes.AddOrUpdate();

        // Customer Type
        List<CustomerType> customerType =
            new CustomerTypeDefaultData().GetDefaultCustomerType();
        customerType.ForEach(e => context.CustomerTypes.Add(e));
        context.CustomerTypes.AddOrUpdate();

        // Income Type
        List<IncomeType> incomeType =
            new IncomeTypeDefaultData().GetDefaultIncomeType();
        incomeType.ForEach(e => context.IncomeTypes.Add(e));
        context.IncomeTypes.AddOrUpdate();

        // Outcome Type
        List<OutcomeType> outcomeType =
            new OutcomeTypeDefaultData().GetDefaultOutcomeType();
        outcomeType.ForEach(e => context.OutcomeTypes.Add(e));
        context.OutcomeTypes.AddOrUpdate();

        // Payment Type
        List<PaymentType> paymentType =
            new PaymentTypeDefaultData().GetDefaultPaymentType();
        paymentType.ForEach(e => context.PaymentTypes.Add(e));
        context.PaymentTypes.AddOrUpdate();
    }
}

最后:

internal sealed class Configuration : DbMigrationsConfiguration<CMS.Model.Domain.CmsDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }

    protected override void Seed(CMS.Model.Domain.CmsDbContext context)
    {
        DbInitializer all = new DbInitializer();
        all.DataSeed(context);
    }
}

2 个答案:

答案 0 :(得分:1)

好吧,最后在清除了关于创建表格,播种数据和添加数据的顺序的一些误解后,我们已经达到了一些可以给出一些有希望的有用见解的观点。

AddOrUpdate

此方法专门用于播种数据,因此您处于正确的轨道上。但是你并没有有效地使用它,因为你没有输入任何实体。以下是您如何使用它的示例:

var cities = new City[]
{
    new City { ProvinceId = 1, CityArabicName = "الرياض", CityEnglishName = "Riyadh"},
    new City  { ProvinceId = 2, CityArabicName = "جدة",  CityEnglishName = "Jeddah"},
    new City  { ProvinceId = 3, CityArabicName = "الدمام", CityEnglishName = "Dammam"},
    new City  { ProvinceId = 4, CityArabicName = "بريدة", CityEnglishName = "Buraidah"}
};
context.Cities.AddOrUpdate(c => c.CityArabicName, cities);

lambda表达式c => c.CityArabicName告诉EF按CityArabicName查找城市。如果已存在具有该名称的城市,则该城市将标记为已修改,否则将标记为新城市。请注意,AddOrUpdate不会保存任何数据,最后由SaveChangesCityId电话完成。

如果您不提供此lambda表达式,则EF会假定主键0是查找现有城市的关键。好吧,它们未在cities中指定(即Seed)。因此,找不到现有城市,Area方法将始终复制您的城市。因此,当自动生成主键时,您总是需要一些自然键,以便EF识别现有实体。

但即使您在种子数据中输入主键值,EF也会忽略它们。 使用自动生成的主键,您无法为主键值设定种子。

这与您的问题有什么关系?

好吧,如果主键值不可预测,也不能为硬编码的外键值设置种子。这正是您对CityId所做的。现在,不可否认,当城市 播种时,你得到FK违规是很奇怪的,而不是当它们没有播种时。毕竟,生成TransactionScope s 1和2的可能性很大(尽管Sql Server可以决定以更高的值开始生成)。所以我无法解释你的发现,但我知道你必须修复你的播种代码。

修复它

有两种方法可以在播种脚本中将对象绑定在一起:

  1. 将整个脚本包裹在SaveChanges中,多次调用SaveChanges,并捕获生成的主键值,以便在后续步骤中将它们分配给实体属性。我不是很喜欢这个选项,但有时你必须使用它。更好的选择是 -

  2. 创建对象图。也就是说,创建具有嵌套实体的实体并通过一次City调用保存它们。在您的情况下,这意味着public virtual ICollection<Area> Areas { get; set; } 应该具有导航属性...

    var cities = new City[]
    {
        new City { ProvinceId = 1, CityArabicName = "الرياض", CityEnglishName = "Riyadh",
                   Areas = areas1 },
        new City { ProvinceId = 2, CityArabicName = "جدة",  CityEnglishName = "Jeddah",
                   Areas = areas2 },
        new City { ProvinceId = 3, CityArabicName = "الدمام", CityEnglishName = "Dammam"},
        new City { ProvinceId = 4, CityArabicName = "بريدة", CityEnglishName = "Buraidah"}
    };
    

    ...并且您应该填充种子实体中的区域:

    areas1

    ...其中areas2AddOrUpdate显然属于前两个城市的两个区域集合。现在,您只前往Area城市,因为如果必须添加城市,则必须添加其中的区域。

  3. 后续步骤

    这并不能说明整个故事。播种相关数据可能很难。如果您必须在应用程序的更高版本中向City添加Area,该怎么办?如果您要更新AddOrUpdate或删除一个,该怎么办?这通常需要更多冗长的步骤,不再使用Option Explicit Dim sheetCount As Integer Dim datatoFind Sub Button1_Click() Find_File End Sub Private Sub Find_File() Dim c As Range Dim counter As Integer Dim currentSheet As Integer Dim notFound As Boolean notFound = True For Each c In Selection.Cells On Error Resume Next currentSheet = ActiveSheet.Index datatoFind = StrConv(c.Value, vbLowerCase) If datatoFind = "" Then Exit Sub sheetCount = ActiveWorkbook.Sheets.Count If IsError(CDbl(datatoFind)) = False Then datatoFind = CDbl(datatoFind) For counter = 1 To sheetCount Sheets(counter).Activate Cells.Find(What:=datatoFind, After:=ActiveCell, LookIn:=xlValues, LookAt _ :=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _ False, SearchFormat:=False).Activate If InStr(1, StrConv(ActiveCell.Value, vbLowerCase), datatoFind) Then notFound = False Sheets(counter).Activate Range("datatoFind").Select Exit For End If Next counter If notFound = True Then MsgBox ("Value not found") Sheets(counter).Activate Else Exit Sub End If Next c End Sub

答案 1 :(得分:0)

播种 创建Country的对象,在District添加District中添加ProvinceProvince添加City&gt; Area&gt; TypeID中的TypeID添加该层次结构的客户数据。 现在分别向AreaTypeID添加另一个CityArea

因此Country的对象有多个区域,每个区域有多个省,每个省都有多个城市......同样明智。

现在将此对象Country添加到函数:

context.Countries.Add(

并在此添加多个国家/地区。