EF 4.3 在一个数据库中使用多个 DbContext 自动迁移
我正在尝试将 EF 4.3 迁移与多个代码优先 DbContext 结合使用。我的应用程序分为几个插件,这些插件可能有自己的关于其域的 DbContext。应用程序应使用一个 sql 数据库。
当我尝试自动迁移空数据库中的上下文时,这仅对第一个上下文成功。其他所有上下文都需要将 AutomaticMigrationDataLossAllowed-Property 设置为 true,但随后会尝试删除前一个上下文的表。
所以我的问题是:
- 我如何告诉迁移配置只照顾在其相应上下文中定义的表,而不管其他所有表?
- 在单个数据库中处理多个 DbContext 并进行自动迁移的正确工作流程是什么?
谢谢你!
I'm trying to use EF 4.3 migrations with multiple code-first DbContexts. My application is separated into several plugins, which possibly have their own DbContext regarding their domain. The application should use one single sql-database.
When I try to auto migrate the contexts in an empty database, this is only successful for the first context. Every other context needs the AutomaticMigrationDataLossAllowed-Property set to true but then tries to drop the tables of the previous one.
So my question is:
- How can I tell the migration-configuration just to look after the tables defined in their corresponding context and leave all others alone?
- What is the right workflow to deal with multiple DbContexts with auto-migration in a single database?
Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这是您可以做的。很简单。
您可以为每个上下文创建配置类。
例如
现在您添加迁移。您不需要启用迁移,因为您已经完成了上述 2 类迁移。
这将为 context1 创建迁移脚本。您可以对其他上下文再次重复此操作。
更新数据库
这可以按任何顺序完成。除非您需要确保按顺序调用每个配置。
Here is what you can do. very simple.
You can create Configration Class for each of your context.
e.g
Now you add migration. You dont need to enable migration since you already did with the 2 classed above.
This will create migration script for context1. your can repeat this again for other Contexts.
To Update your database
This can be done in any order. Except you need to make sure each configration is called in sequence.
Code First Migrations 假设每个数据库只有一个迁移配置(每个配置有一个上下文)。
我可以想到两种可能的解决方案:
创建一个包含每个上下文的所有实体的聚合上下文,并从迁移配置类中引用这个“超级”上下文。这样,所有表都将在用户的数据库中创建,但数据仅位于他们为其安装插件的表中。
为每个上下文使用单独的数据库。如果您在上下文之间有共享实体,请添加自定义迁移并将
CreateTable(...)
调用替换为Sql("CREATE VIEW ...")
调用从实体的“原始”数据库获取数据。我会尝试#1,因为它将所有内容保存在一个数据库中。您可以在解决方案中创建一个单独的项目来包含您的迁移和这个“超级”上下文。只需添加项目,引用所有插件的项目,创建一个包含所有实体的上下文,然后在这个新项目上调用 Enable-Migrations 即可。之后事情应该按预期进行。
Code First Migrations assumes that there is only one migrations configuration per database (and one context per configuration).
I can think of two possible solutions:
Create an aggregate context that includes all the entities of each context and reference this "super" context from your migrations configuration class. This way all the tables will be created in the user's database, but data will only be in the ones that they've installed plugins for.
Use separate databases for each context. If you have shared entities between the contexts, add a custom migration and replace the
CreateTable(...)
call with aSql("CREATE VIEW ...")
call to get the data from the entity's "originating" database.I would try #1 since it keeps everything in a single database. You could create a seperate project in your solution to contain your migrations and this "super" context. Just add the project, reference all of your plugins' projects, create a context that includes all of the entities, then call Enable-Migrations on this new project. Things should work as expected after that.
我有一个使用迁移的具有多个上下文的工作站点。但是,您确实需要为每个上下文使用单独的数据库,并且它全部由项目的 Migrations 命名空间中的 *Configuration 类驱动,因此例如 CompanyDbContext 使用 CompanyConfiguration 指向 Company.sdf。
更新数据库-configurationtypename CompanyConfiguration
。另一个 LogDbContext 使用 LogConfiguration 等指向 Log.sdf。鉴于此方法有效,您是否尝试创建 2 个指向同一数据库的上下文并告诉模型构建器忽略其他上下文的表列表?
由于迁移与模型构建器一起使用,因此这可能可以完成工作。
蹩脚的替代方案是避免使用自动迁移,每次生成一个迁移,然后手动筛选并删除不需要的语句,然后运行它们,尽管没有什么可以阻止您创建一个简单的工具来查看上下文和生成的语句并执行为您修复迁移问题。
I have a working site with multiple contexts using migrations. However, you do need to use a separate database per context, and it's all driven off of a *Configuration class in the Migrations namespace of your project, so for example CompanyDbContext points to Company.sdf using CompanyConfiguration.
update-database -configurationtypename CompanyConfiguration
. Another LogDbContext points to Log.sdf using LogConfiguration, etc.Given this works, have you tried creating 2 contexts pointing at the same database and telling the modelbuilder to ignore the other context's list of tables?
Since the migrations work with the ModelBuilder, this might do the job.
The crappy alternative is to avoid using Automatic Migrations, generate a migration each time and then manually sift through and remove unwanted statements, then run them, although there's nothing stopping you from creating a simple tool that looks at the Contexts and generated statements and does the migration fixups for you.
好吧,我已经为此苦苦挣扎了一天,这是为那些寻求答案的人提供的解决方案...
我假设大多数阅读这篇文章的人都在这里,因为他们有一个包含大量 DbSet<> 的大型 DbContext 类;属性,并且需要很长时间才能加载。您可能会想,天哪,这是有道理的,我应该拆分上下文,因为我不会立即使用所有数据库集,并且我只会根据我需要的情况加载“部分”上下文它。因此,您将它们分开,却发现 Code First 迁移不支持您的革命性思维方式。
因此,您的第一步必须是拆分上下文,然后为每个新上下文添加 MigrationConfiguration 类,添加与新 Context 类名称完全相同的连接字符串。
然后,您尝试通过执行 Add-Migration Context1 然后执行 Update-Database -Verbose 来逐个运行新分割的上下文...
一切似乎都工作正常,但随后您注意到每个后续迁移都会删除上一个迁移中的所有表,并且只保留最后一次迁移的表。
这是因为,当前的迁移模型期望每个数据库有一个 DbContext,并且它必须是镜像匹配。
我也尝试过,并且有人建议这样做,就是创建一个 SuperContext,其中包含所有 Db 集。创建一个迁移配置类并运行它。将部分 Context 类保留在适当的位置,然后尝试实例化并使用它们。 EF 抱怨支持模型已更改。同样,这是因为 EF 将您的部分 dbcontext 与超级上下文迁移留下的 All-Sets 上下文签名进行比较。
我认为这是一个重大缺陷。
就我而言,我认为性能比迁移更重要。因此,我最终所做的就是在超级上下文中运行并准备好所有表后,我进入数据库并手动删除 _MigrationHistory 表。
现在,我可以实例化并使用我的部分上下文,而 EF 不会抱怨它。它没有找到 MigrationHistory 表,只是继续前进,使我能够获得数据库的“部分”视图。
当然,代价是对模型的任何更改都必须手动传播到数据库,所以要小心。
不过它对我有用。
Ok, I have been struggling with this for a day now, and here is solution for those seeking the answer...
I am assuming that most people reading this post are here because they have a large DbContext class with a lot of DbSet<> properties and it takes a long time to load. You probably thought to yourself, gee, that makes sense, I should split up the context, since I won't be using all of the dbsets at once, and I will only load a "Partial" context based on the situation where I need it. So you split them up, only to find out that Code First migrations don't support your way of revolutionary thinking.
So your first step must have been splitting up the contexts, then you added the MigrationConfiguration class for each of the new contexts, you added the connection strings named exactly the same as your new Context classes.
Then you tried running the newly split up contexts one by one, by doing Add-Migration Context1 then doing Update-Database -Verbose...
Everything seemed to work fine, but then you notice that every subsequent Migration deleted all tables from the Previous migration, and only left the tables in from the very last migration.
This is because, the current Migrations model expects Single DbContext per Database, and it has to be a mirror match.
What I also tried, and someone suggested here doing that, is create a single SuperContext, which has All the Db sets in it. Create a single Migration Configuration class and run that in. Leave your partial Context classes in place, and try to Instantiate and use them. The EF complains that the Backing model has changed. Again, this is because the EF compares your partial dbcontext to the All-Sets context signature that was left over from your Super Context migration.
This is a major flaw in my opinion.
In my case, I decided that PERFORMANCE is more important than migrations. So, what I ended up doing, is after I ran in the Super context and had all the tables in place, I went into the database and Manually deleted _MigrationHistory table.
Now, I can instantiate and use my Partial Contexts without EF complaining about it. It doesn't find the MigrationHistory table and just moves on, allowing me to have a "Partial" view of the database.
The trade off of course is that any changes to the model will have to be manually propagated to the database, so be careful.
It worked for me though.
正如 Brice 上面提到的,最实用的解决方案是每个应用程序/数据库有 1 个超级 DbContext。
整个应用程序必须仅使用 1 个 DbContext 似乎是一个关键的技术和方法缺点,因为它会影响模块化程度等。此外,如果您使用 WCF 数据服务,则每个应用程序只能使用 1 个 DataService,因为 DataService 只能映射到 1 个 DbContext。所以这极大地改变了架构。
从好的方面来说,一个小优点是所有与数据库相关的迁移代码都是集中的。
As mentioned above by Brice, the most practical solution is to have 1 super DbContext per application/database.
Having to use only 1 DbContext for an entire application seems to be a crucial technical and methodological disadvantage, cause it affects Modularity among other things. Also, if you are using WCF Data Services, you can only use 1 DataService per application since a DataService can map to only 1 DbContext. So this alters the architecture considerably.
On the plus side, a minor advantage is that all database-related migration code is centralized.
我刚刚遇到这个问题,并意识到我将它们分成不同上下文的原因纯粹是为了将相关模型分组为可管理的块,而不是出于任何其他技术原因。相反,我已将上下文声明为分部类,现在包含不同模型的不同代码文件可以将 DbSet 添加到 DbContext。
这样,自动迁移的魔力仍然有效。
I just came across this problem and realised the reason I had split them into different contexts was purely to have grouping of related models in manageable chunks and not for any other technical reason. Instead I have declared my context as a partial class and now different code files with different models in them can add DbSets to the DbContext.
This way the automigration magic still works.
我可以使用手动迁移,但您无法降级,因为它无法区分 __MigrationHistory 表中的配置。如果我尝试降级,那么它会将其他配置的迁移视为自动,并且由于我不允许数据丢失,因此它会失败。我们只会使用它来升级,因此它可以满足我们的目的。
虽然这看起来确实有点遗漏,但我相信只要 DbContext 之间没有重叠,支持它并不难。
I've got it working with manual migrations, but you can't downgrade as it can't discrimitate between configurations in the __MigrationHistory table. If I try and downgrade then it treats the migrations from the other configurations as automatic and since I don't allow data loss it fails. We will only ever be using it to upgrade though so it works for our purposes.
It does seem like quite a bit ommision though, I'm sure it wouldn't be hard to support it provided there was no overlap between DbContexts.
当然,解决方案应该是 EntityFramework 团队修改 API,以支持将 _MigrationHistory 表直接修改为您选择的表名称,例如 _MigrationHistory_Context1,以便它可以处理独立 DbContext 实体的修改。这样它们就会被单独处理,并且由开发人员来确保实体的名称不会发生冲突。
似乎有很多人都同意我的观点,即引用实体超集的重复 DbContext 是一种虚假的非企业友好的处理方式。对于基于模块化(Prism 或类似)的解决方案,重复的 DbContext 会严重失败。
Surely the solution should be a modification by the EntityFramework team to change the API to support the direct modification of the _MigrationHistory table to a table name of your choice like _MigrationHistory_Context1 such that it can handle the modification of independent DbContext entities. That way they're all treated separately, and its up to the developer to ensure that the names of entities don't collide.
Seems like there are a lot of people who share my opinion that a duplicate DbContext with references to the superset of entities is a bogus non-enterprise friendly way to go about things. Duplicate DbContexts fail miserably for modular (Prism or similar) based solutions.
我希望人们知道下面的答案对我有用,但有一个警告:不要使用 MigrationsNamespace 行。
但是,我已经建立了两个数据库并定义了它们自己的上下文,因此我发现自己收到一条错误消息“YourProject.Models 命名空间已经定义了 ContextNamespace1”。这是因为“MigrationsNamespace =”YourProject.Models.ContextNamespace2”;”在我尝试 Init 后,导致 dbcontext 在 YourProjects.Models 命名空间下定义两次(一次在迁移 Context1Init 文件中,一次在我之前定义的位置)。
因此,我发现此时我必须做的是按照此处的说明启动数据库并从头开始迁移(谢天谢地,我没有需要保留的数据):
http://pawel.sawicz.eu/entity-framework-reseting-migrations/
然后我将代码更改为不包含 MigrationsNamespace 行。
然后我再次运行 Add-Migration -configuration Configuration1 Context1Init 命令,并再次运行 Update-Database -configuration Configuration1 行(也适用于我的第二个上下文),最后,现在一切似乎都运行良好。
I want people to know that the answer with this below is what worked for me but with one caveat: don't use the MigrationsNamespace line.
However, I already had the 2 databases established with their own contexts defined so I found myself getting an error saying "YourProject.Models namespace already has ContextNamespace1 defined". This was because the "MigrationsNamespace = "YourProject.Models.ContextNamespace2";" was causing the dbcontext to be defined under the YourProjects.Models namespace twice after I tried the Init (once in the migration Context1Init file and once where I had it defined before).
So, I found that what I had to do at that point was start my database and migrations from scratch (thankfully I did not have data I needed to keep) via following the directions here:
http://pawel.sawicz.eu/entity-framework-reseting-migrations/
Then I changed the code to NOT include the MigrationsNamespace line.
Then I ran the Add-Migration -configuration Configuration1 Context1Init command again and the Update-Database -configuration Configuration1 line again (for my 2nd context too), and finally, everything seems to be working great now.