-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Drop/create SQLite columns when changing column type to something not convertible #22598
Comments
From Result and Error Codes in the SQLite docs:
You'll need to use a This might require something like #15586 to improve. Note, you can easily hit this issue on SQL Server and other providers when altering to an incompatible type. |
@bricelam My migration 20200918104754_PostIdToInt32.cs was generated by dotnet ef migrations add PostIdToInt32 command:
And it is not working out-of-the-box. I was thinking that SQLite table rebuilds is an automatic way to update table schema and data in that table. I was expecting that simply adding migration will do all the work. |
@bairog table rebuilds works automatically with most changes, such as adding or removing a column. However, changing a column's type is quite different, since data conversion has to occur, and how exactly that should be done can depend on the user. For example, if you want to convert a text column to a binary one, which encoding should be used? Also, such conversions can simply fail, depending on the data you have. For example, the down migration above converts a TEXT column to an INTEGER, but what happens if there is non-numeric text data in the column? For these reasons, data type conversions may require user intervention, and can't always be done automatically for you. As @bricelam mentioned above, this has nothing to do with table rebuilds or SQLite - other databases will error in the same way when you try to alter a column's type, and require you to specify a conversion method (see npgsql/efcore.pg#144 for a similar discussion on PostgreSQL). |
@roji @bricelam Yes, I understand that automatic data type convertion is not always possible for "ordiary" columns. But Primary Key/Foreign Key columns are different: they stand only to connect tables and data in such columns has no physical meaning. PK Guid 'aaaaaaaa-0000-aaaa-0000-000000000000' itself means nothing - it only connects to FK(s) with same value. So regarding my case (changing PK type from Guid to Int32) conversion can be the following: first row Guid (for example 'aaaaaaaa-0000-aaaa-0000-000000000000') -> 0 (and all FK(s) shul be also 0); second row Guid (for example 'bbbbbbbb-0000-bbbb-0000-000000000123') - > 1 (nd all FK(s) shul be also 1), etc.. Actually Int32 can have any value and just keep connections between tables. But from my point of view it's better simply to start from 0 and perform auto-increment. P.S. From my point of view even some data convertions for "ordinary" columns can be done automatically: at least Boolean -> Int, Int -> Double, Int -> String and Double -> String, maybe even more. For me that auto-convertion is also reasonable to implement (but PK convertions has highest proirity). Thank you in advance. |
That may be true for your specific case, but definitely not in many others. PK (and therefore FK) values aren't always meaningless (or generated), in many cases they're natural keys, and therefore have just as much meaning as any other field. Even a GUID key could be identifying the row in some other database or system, and in that sense it could be "meaningful". Transparently changing all GUIDs to arbitrary numbers could work in your specific case, but could be extremely destructive to another database. In that sense, it makes sense for users to edit their migrations and specify whatever conversion strategy fits their specific case.
For Boolean->Int, I'm not sure what value true should map to: 1? -1? I don't think it's right for an EF provider to make an implicit decision on an important data migration question. Regardless, remember that EF is also responsible for generating Down migrations for each Up migration. While int->string indeed works, the opposite will fail if there are non-numeric values. This isn't an absolute argument - it may be OK to generate an Up migration that always works and preserves values for going back (e.g. int->string), while allowing for the Down migration to not be failsafe. But it does seem better to make the user aware of the potentially problematic migration by requiring their attention. |
Ok, but why not to create automatic migrations and show warning that they are potentially not failsafe (user will decide whether he should write his own migrations or not)? In sutiations like mine (when PK has no meaning) it will be just working out-of-the-box.
From my point of view it's reasonable for EF provider to work like System.Convert class is working:
You are right, it's better to make the user aware of the potentially problematic migration. EF provider should create automatic migrations and show warning in case they are potentially not failsafe. User will make it's own decision - is it correct (he will use automatically created migrations) or not (and he will write his own migrations) in his particular case. That will serve rapid application development a lot. P. S. I will investigate how other SQLite EF providers (Devart.Data.SQLite, System.Data.SQLite, etc.) are working in that situatioon and will report you. But I've already told you my point of view: it is better to have automatic migrations (with a warning in case they are potentially not failsafe) than not to have them at all. |
P. P. S. In attached sample project I've tried to comment database migration
and SQLite EF provider succesfully returned posts ( |
Because it seems like a very bad idea to perform such a potentially destructive operation automatically, with just a warning that many will disregard. We can't just implicitly turn GUIDs into random numbers even if it works in your specific case.
Unless I'm mistaken, these are ADO.NET providers rather than EF providers, so they're not really relevant to this question. Devart may have an EF Core provider, I'm not sure.
I disagree. It's true that in some cases EF Core does scaffold potentially destructive migrations and emits a warning - as you suggest; this happens, for example, for dropping a column. The difference is that in that case the user has removed a property from the model, and so it's reasonable to expect that they're aware and conscious of what they're doing; the model change itself already expresses the destructive action. This is not the case when changing a GUID to a number. To turn the question around, what is the disadvantage of requiring the user to manually edit the migrations? Yes, it is a bit more manual work for the user - for the relatively rare cases of incompatible column type changes - but that's the only downside. Doing it automatically, on the other hand, is extremely dangerous in that it can cause unintentional data loss - isn't the choice clear here?
Even if Sqlite implicitly converts TEXT to INT when reading, that's very different from (implicitly) changing the data on-disk, and irrevocably losing all your GUIDs. |
My mistake - packages with EF providers are Devart.Data.SQLite.EFCore and System.Data.SQLite.EF. I will investigate soon.
For me that "a bit more manual work" is a big disadvantage. I've made a mistake in my SQLite database with 25+ tables - PKs are GUIDs instead if Int32. SQLite doesn't have native datatype for GUID and therefore performs badly in that case, especially in my 250Gb+ database. So writing all Up/Down migrations manually is a real pain, just typing
The choice is not clear for me. A perform automatic database backups before every migration operations (potentially destructive or not). Moreover data in database may become corrupted not only after migration but for any reason (i. e. filesystem or network failure), isn't it what backups intended for? |
System.Data.SQLite is an EF6 provider, not EF Core, and it does not support migrations. |
Yes, I see it now. System.Data.SQLite for now only have EF6 provider and doesn't support EF Core of any version. Devart.Data.SQLite.EFCore doesn't support EF Core 5.0 for a moment. I will wait for some time.. |
Not as much as losing all your GUID values, I'd say. On one side is an inconvenience only when altering column types, on the other is unexpected data loss.
That's weird logic... The fact that filesystem corruption can happen isn't a reason for EF Core to intentionally and implicitly perform destructive data operations that aren't clearly the user's intent. I'd also perform backups before migrations - and carefully inspect all my migration SQL scripts - but many users don't. |
Notes from triage:
Putting on the backlog for this. |
@ajcvickers Just to make it clear:
|
@bairog in a nutshell, table rebuilds work by creating a new table with the new schema, copying all the data across from the old table to the new, and then swapping the tables (while temporarily suspending foreign key enforcing). No data gets lost. One thing you may be missing, is that altering column types is only a very specific change covered by rebuilds - but there are many others where type conversion problems aren't relevant (adding a column, removing a column...). Hope that clarifies things. Try using version 5.0.0-rc1 of the provider, playing around and seeing what happens. |
|
@ajcvickers @roji Let's make my task a little more complicated.
And I need to change User.Id, Post.Id and Post.UserId from Guid to Int32. According to this the correct way is to I've tried to write the following migration (a big code block inside that spoiler):
Looks like migration is working now and data is obtained correctly from updated database. |
@ajcvickers @roji Am I going the right way? As you can see in my migrations I convert Guid PKs simply by auto-incrementing Int32 PKs. |
Hello.
I have simple class library project with a EF Core 5.0-rc1 DbContext that targets .NET 5.0-preview7 (I'm using .NET 5-preview7 because according to info on download page it is the latest one that supports Visual Studio 2019 v16.7 Release. I use only stable IDE builds in my project and cannot use preview IDE versions).
I've have following entites
Then I've added first migration via dotnet ef migrations add InitialCreate command, created test database and populated some data.
After that I've changed Post.Id data type from Guid to Int32 and created second migration.
According to this SQLite migrations table rebuilds are now available since EF Core 5.0.0-preview8.
Also according to docs AlterColumn command should work for SQLite via table rebuild.
But running
context.Database.Migrate();
throws an exceptionSteps to reproduce
context.Database.Migrate();
throws an exceptionException StackTrace:
Further technical details
EF Core version: 5.0-rc1
Database provider: Microsoft.EntityFrameworkCore.Sqlite 5.0-rc1
Target framework: .NET 5.0-preview7
Operating system: Windows 10 2004 x64
IDE: Visual Studio 2019 16.7.3 Professional
The text was updated successfully, but these errors were encountered: