简体   繁体   English

使用实体框架向现有数据库添加或更新数据

[英]Add or update data to existing database with entity framework

I am trying to create a custom migration step to clean up some default values in our database.我正在尝试创建一个自定义迁移步骤来清理我们数据库中的一些默认值。 Basically we have some data that is either invalid and needs to be updated and in other cases it doesn't exist at all.基本上我们有一些无效的数据需要更新,而在其他情况下它根本不存在。 The system is installed on several servers and a lot of this data was added manually at one point so its hard to know what server has what data.该系统安装在多台服务器上,其中很多数据是一次性手动添加的,因此很难知道哪个服务器有哪些数据。

What I want to do is create a migration step to clean it all up.我想要做的是创建一个迁移步骤来清理它。 If this value does not exist in the table i need to insert it.如果表中不存在此值,我需要插入它。 However if it does exist then either I updated it or just delete it then insert it.但是,如果它确实存在,那么要么我更新它,要么删除它然后插入它。 I am having a hard time figuring out how to do this.我很难弄清楚如何做到这一点。

Add-Migration DataCleanup

Create migration step创建迁移步骤

public partial class DataCleanup : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.InsertData(
            table: "Blogs",
            columns: new[] { "BlogId", "Url" },
            values: new object[] { 4, "http://sample4.com" });}

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DeleteData(
            table: "Blogs",
            keyColumn: "BlogId",
            keyValue: 4);
    }
}

This will work if the row didn't exist previously, but if the row did exist then I will need to update to be sure the value is the correct value.如果该行以前不存在,这将起作用,但如果该行确实存在,那么我将需要更新以确保该值是正确的值。 I am not to worried about the primary keys as this is a reference table they "should" be the same.我不担心主键,因为这是一个参考表,它们“应该”相同。

The only other option I can think of would be to run a truncate on these tables and then just run the inserts after.我能想到的唯一其他选择是在这些表上运行截断,然后只运行插入。

migrationBuilder.Sql("TRUNCATE TABLE [Blogs]", true);

Note on down注意下

I am leaning towards not being able to do the down at all.我倾向于根本无法做向下的动作。 There will be no way of knowing what state this one server was in before i ran this.在我运行它之前,将无法知道这台服务器处于什么状态。

First off i was unable to do a truncate on the tables there are foreign keys set up which meant that that was just not going to work. 首先,我无法对表格进行截断,因为设置了外键,这意味着这不会起作用。

I ended up doing a bunch of sql inserts that check first of the row exists and if it doesn then we insert it. 我最终做了一堆sql插入,检查行的第一个存在,如果它没有,那么我们插入它。

migrationBuilder.Sql("INSERT INTO IdentityResources (Description, DisplayName, Emphasize, Enabled, Name, Required, ShowInDiscoveryDocument) " +
                                 "SELECT 'Your email address', 'User email', 1, 1, 'email', 0, 1 " +
                                 "WHERE NOT EXISTS(SELECT * " +
                                                  "FROM IdentityResources " +
                                                  "WHERE name = 'email'); ", true);

Things got a little more complicated when i needed to add the key from the first insert. 当我需要从第一个插入中添加密钥时,事情变得有点复杂。

migrationBuilder.Sql("INSERT INTO IdentityClaims (IdentityResourceId, Type) " +
                                         "SELECT id, 'email' " +
                                         "FROM IdentityResources " +
                                         "WHERE Name = 'email' " +
                                         "AND NOT EXISTS(SELECT * " +
                                         "FROM [IdentityClaims] " +
                                         "WHERE type = 'email')");

This all worked out in the end. 这一切最终都得到了解决。 A new install of the system builds the proper database and everyone gets updated to ensure that they have at least the required data. 系统的新安装会构建正确的数据库,每个人都会更新以确保它们至少具有所需的数据。

Unfortunately removing the unneeded data will have to wait for another day. 不幸的是,删除不需要的数据将不得不等待另一天。

For those who are wondering if there is any way to solve this problem without dealing with SQL statements, there is one workaround which some might not favor, but it was worked for us.对于那些想知道是否有任何方法可以在不处理 SQL 语句的情况下解决此问题的人,有一种解决方法可能有些人不喜欢,但它对我们有用。

This works for a scenario where you want to insert some data to the database, but someone already inserted some data previously to the database manually, and there is no record of this operation in the migration history.这适用于您想向数据库插入一些数据,但之前有人已经手动向数据库插入了一些数据,并且迁移历史中没有此操作的记录的场景。 You want to be a good developer and keep things in record when you insert new data to your tables, but the existing data prevents you to do this properly.您想成为一名优秀的开发人员,并在向表中插入新数据时进行记录,但现有数据阻止您正确执行此操作。

In your mappings, use your EntityTypeBuild builder object in the Entity Framework and take advantage of builder.在您的映射中,在实体框架中使用您的EntityTypeBuild构建器对象并利用构建器。 HasData(IEnumerable data) method. HasData(IEnumerable 数据)方法。 Just add your entities to a list and pass this to the HasData method.只需将您的实体添加到列表并将其传递给HasData方法。 When you create a new migration, this data will appear as:创建新迁移时,此数据将显示为:

migrationBuilder.InsertData(
                schema: "dbo",
                table: "Country",
                columns: new[] { "Id", "CreatedOnUtc", "ModifiedOnUtc", "Name" },
                values: new object[,]
                {
                    { 1, new DateTime(2020, 04, 17, 13, 31, 19, 188, DateTimeKind.Utc).AddTicks(8570), null, "USA" },
                    { 2, new DateTime(2020, 04, 17, 13, 31, 19, 188, DateTimeKind.Utc).AddTicks(8570), null, "Netherlands" },
                    .
                    .
                });

In this InsertMigration method, there might be existing data already available in your database.在此InsertMigration方法中,您的数据库中可能已有可用数据。 Copy and paste the InsertData block to one of your old migrations, where it's already ran against the database.InsertData块复制并粘贴到您的旧迁移之一,它已针对数据库运行。 In this code, remove new objects from the values array.在此代码中,从值数组中删除新对象。 So we are moving the old data to a previous migration, only keeping the new data in the new migration.所以我们将旧数据移动到之前的迁移中,只保留新迁移中的新数据。 Because otherwise you would get an error during the SQL Insert operation as the previous records were already in the table as you are trying to insert the same data with the same Primary Key.因为否则您会在 SQL 插入操作期间收到错误,因为在您尝试使用相同的主键插入相同的数据时,先前的记录已经在表中。

So when you run this migration against your DB, it will only insert the new rows.因此,当您针对数据库运行此迁移时,它只会插入新行。 The main advantage of this approach is that;这种方法的主要优点是; if you happen to deploy your application to a new environment along with an empty SQL DB in the future, the previous migration will insert previous seed data and your new migration will insert the remaining part.如果您将来碰巧将应用程序连同空的 SQL DB 一起部署到新环境,则先前的迁移将插入先前的种子数据,而您的新迁移将插入剩余部分。 So this trick will work for new environments as well.所以这个技巧也适用于新环境。

One thing to keep in mind that, ContextModelSnapshot is an auto-generated file when crating migrations.要记住的一件事是, ContextModelSnapshot是创建迁移时自动生成的文件。 You should put builder.HasData(data) to there before creating the new migration.在创建新迁移之前,您应该将builder.HasData(data)放在那里。 Because otherwise the Entity Framework does not understand, the seed operation is already applied with the previous migration.因为否则实体框架不理解,种子操作已经在之前的迁移中应用了。 This is required for just one time, before creating the new migration, which includes new seed data.在创建包含新种子数据的新迁移之前,这仅需要一次。 After Entity Framework creates the new migration file, this will be overwritten again, so don't worry about touching this auto-generated file. Entity Framework 创建新的迁移文件后,这将再次被覆盖,所以不要担心触及这个自动生成的文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM