I am using SQLite-net together with the TwinCoders NuGet for extension methods in a MvvmCross Xamarin project. I want to make the database to stay updated even if I will modify the models in future.
My question is: If I use the CreateTable function for creating a SQLite table based on the model and the database already exists on the tablet/phone, but it has a different structure (let's say that the table has a missing column which was added in the last update), will this function alter the existing table? Thanks!
Thanks for your answers! SQLite-Net implements automatic migrations indeed. It treats the lack of "ALTER DROP COLUMN" and "ALTER RENAME COLUMN" commands from SQLite as follows:
if the name of one column is changed, then a new column will be created with the new name and the old one will be ignored by the queries
if the column is dropped, then it will remain in the database, but the queries will ignore it
I didn't try yet what happens when I change the datatype of one column (attribute), but I hope it will not be the case in our project.
For me the answer has always been yes. I changed the name of one of the properties in my model and that ended up leaving the data in place and each existing record had that new property but it was empty.
So it went something like this:
Original:
class Model {
int Id { get; set; }
string Date { get; set; }
}
Changed:
class Model {
int Id { get; set; }
string Dates { get; set; }
}
Now all of the records still had Id values and were not erased but they all also had empty Dates columns, as would be expected.
Needless to say, a new table was not created.
Edit: Also note that I am using SQLite-Net only, no extension libraries or anything but the end result should be the same.
Related
I have an EF Core 6 context that uses temporal tables, and am trying to rename one of the properties on a model.
I have a model that was named PropertyGroupsInvestors, which I renamed to Investment. That worked fine.
I then noticed that the InvestorPayment model had the following...
public int PropertyGroupsInvestorsID { get; set; }
public PropertyGroupsInvestors PropertyGroupsInvestors { get; set; } = new();
...which should also have been renamed. I renamed the two properties as follows...
public int InvestmentsID { get; set; }
public Investment Investment { get; set; } = new();
...and added a migration.
However, when I tried to update the database, it failed with the error "Setting SYSTEM_VERSIONING to ON failed because table 'MyProject.dbo.InvestorPayments' has 10 columns and table 'MyProject.dbo.InvestorPaymentsHistory' has 9 columns."
Looking in SQL Server Management Studio, I can see that both the InvestorPayments and corresponding history tables have both the old PropertyGroupsInvestorsID column and the new InvestmentId column. They both have 10 columns, so I'm not sure what he message means though. See the screenshot...
Any idea how I fix this? Ideally I need to get rid of the PropertyGroupsInvestorsID column from both tables.
Thanks
There is a problem with the renaming of columns in the system versioning table using a code-first approach. So a quick solution (if you don't want to drop temporal tables) can be just to create two migrations. One migration to add a new column, and the second migration to remove the old column.
So first migration will contain both, PropertyGroupsInvestorsID and InvestorsID and then remove PropertyGroupsInvestorsID to create a new migration.
The same problem here like deleting or modifying one field, it looks like when you have a system-versioned table, you can't modify the structure because it's linked to the history table.
Using SQL server doesn't look like there is any problem:
https://learn.microsoft.com/en-us/sql/relational-databases/tables/add-columns-to-a-table-database-engine?view=sql-server-ver15
I have a specific EF Core 6.x question.
If the SQL table has a column removed. Then EF Core will throw a SqlException saying that it's an invalid column name unless I also update the C# model.
For example,
Create Table User
(
FirstName varchar(200)
,MiddleName varchar(200) null -- tried to remove this column after table is created
,LastName varchar(200)
)
I tried deleting the MiddleName column from the SQL Table. When I run a simple read call using EF Core 6, I get the error.
c# model
public class User
{
public virtual string FirstName { get; set; }
public virtual string? MiddleName { get; set; }
public virtual string LastName { get; set; }
}
var db = new EFDbContext(connectionString);
var data = db.Users.ToList(); // SqlException here after column removal
Is there any way to remove columns from the table without needing to update the c# class as well?
Tried making the C# property MiddleName not virtual.
Update:
In the event that I have an existing application. I would need to modify the c# model even if the codebase doesn't refer to the removed column anywhere. Alternatively, I can decorate the property with [NotMapped] or use the Ignore() method in the modelbuilder.
Both approaches means a rebuild of the assembly is needed and downtime during deployment.
NHibernate's mapping can be done using an XML file and thus all it takes would be a simple config file update.
I can't seem to find anything in EF Core that will reduce the headache of maintaining older codebases when schema changes occur.
EF creates a data model mapping internally to track the database schema and your code models. By removing the column in your database table, your code model no longer matches the database. Hence, the exception occurs.
This is definitely not be the answer you're looking for, but as far as I know EF Core need consistency between the models and the DB schemas to work.
I can think of 2 things here:
Maybe you could benefit from using a different ORM (Did you give Dapper a cahnce)
You might be facing an architectural issue, if there's more than one team working with the same database, and more than one system calling that database, the best way to avoid headaches in the future would be to isolate the data access layer and expose an API that serves all the involved systems.
That way, if the database changes, you just need to re-build the data access layer, no downtime for your clients.
And finally... in my opinion the ideal solution is a combination of both, create a decoupled data access layer, map things there and expose an API with the models your application needs.
I am developing an ASP.NET Core 5 application and I just made a modification to one of the model classes as follows
...
public long OfferId { get; set; }
[ForeignKey("OfferId")]
public RequestOffer Offer { get; set; }
...
This requires that I add migration and update database.
However, when I try to run update-database I get the following error
Column names in each table must be unique. Column name 'Discriminator' in table 'AspNetUserTokens' is specified more than once
I have tried to run the migration with the -ignoreChanges flag as I saw in a solution proposed on a similar StackOverflow question but it did not make a difference.
My worry is that I never made any changes to that table (AspNetUserTokens) in this update. However, I realize that in the migration file, I see that all the codes for the previous migrations are repeated as if I am rerunning the migrations afresh which I am not doing.
So, it looks like all the database tables are being recreated. I was expecting the migration file to contain code for only the changes I just made but it rather contains all the changes in the previous migrations as well starting from the very first migration I ran.
I will appreciate any guide to help me resolve this so I can update my database and continue with the project.
Thank you
So I made a little relational database with about seven tables, and I'm using some of these to make a form. So I made an ADO.NET Entity Data Model to diagram the forms and generate classes based on the tables. Now I want to give the class properties DisplayNames (using System.ComponentModel). The forms also get pretty big, and there's a lot of DisplayNames.
public partial class ParticularForm
{
public System.Guid ParticularForm1 { get; set; }
public System.Guid GeneralFormFieldsID { get; set; }
#region Particular Form
[DisplayName("What's your name?")]
public string Name { get; set; }
[DisplayName("How many friends do you have?")]
public Nullable<byte> FriendsAmount { get; set; }
// (etc...)
Ack! So it looks like the ADO.NET Entity Data Model gave one of the columns an incorrect name: "ParticularForm1" should be called "ParticularFormID". So I go into the database, rename the "ParticularForm" column to "ParticularFormID". Now I want to update references to ParticularForm1 in the scaffolded Form.tt classes, changing the name to name ParticularFormID. So here's what I do:
I go into the Form.edmx diagram and click Update Model from Database. It doesn't allow me to refresh a single table, it refreshes all of them at once. Afterwards I save, and now the .edmx model correctly shows the "ParticularFormID" column. But it still shows the weird "ParticularForm1" column, and in addition to this, I've lost like 100 display names.
So I undo the pending changes in TFS and I take a new approach.
public partial class ParticularForm
{
public System.Guid ParticularFormID { get; set; }
I went back into Form.tt > ParticularForm.cs and change the property's name from ParticularForm1 to ParticularFormID. Then I go back to the Designer and save the changes. I get the message:
This file has been modified outside of the source editor. Do you want to reload it?
Sure. Then I get it again for every table in addition to the only one I changed. Every time I click No, no changes are made, but TFS still says there are pending changes. When I click Yes for ParticularForm.cs, all of its DisplayNames are removed again, and the .cs file reverts back to the prior naming conventions that ADO.NET chose, and undoes my change.
I'd like to keep the work I've already done with respect to the DisplayNames, but change a single column name without losing all of my work. Any ideas on what is going on? Why don't ADO.NET Entity Data Models respect me?
The answer turned out to be that one should hold the data annotations in a MetadataType class. Not just [DisplayName()], like, everything.
Part of my issue was that I wasn't reading this:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// **Manual changes to this file will be overwritten if the code is regenerated.**
// </auto-generated>
//------------------------------------------------------------------------------
The situation is that I have a table that models an entity. This entity has a number of properties (each identified by a column in the table). The thing is that in the future I'd need to add new properties or remove some properties. The problem is how to model both the database and the corresponding code (using C#) so that when such an occasion appears it would be very easy to just "have" a new property.
In the beginning there was only one property so I had one column. I defined the corresponding property in the class, with the appropriate type and name, then created stored procedures to read it and update it. Then came the second property, quickly copy-pasted, changed name and type and a bit of SQL and there it was. Obviously this is not a suitable model going forward. By this time some of you might suggest an ORM (EF or another) because this will generate the SQL and code automatically but for now this is not an option for me.
I thought of having only one procedure for reading one property (by property name) and another one to update it (by name and value) then some general procedures for reading a bunch or all properties for an entity in the same statement. This may sound easy in C# if you consider using generics but the database doesn't know generics so it's not possible to have a strong typed solution.
I would like to have a solution that's "as strongly-typed as possible" so I don't need to do a lot of casting and parsing. I would define the available properties in code so you don't go guessing what you have available and use magic strings and the like. Then the process of adding a new property in the system would only mean adding a new column to the table and adding a new property "definition" in code (e.g. in an enum).
It sounds like you want to do this:
MyObj x = new MyObj();
x.SomeProperty = 10;
You have a table created for that, but you dont want to keep altering that table when you add
x.AnotherProperty = "Some String";
You need to normalize the table data like so:
-> BaseTable
RecordId, Col1, Col2, Col3
-> BaseTableProperty
PropertyId, Name
-> BaseTableValue
ValueId, RecordId, PropertyId, Value
Your class would look like so:
public class MyObj
{
public int Id { get; set; }
public int SomeProperty { get; set; }
public string AnotherProperty { get; set; }
}
When you create your object from your DL, you enumerate the record set. You then write code once that inspect the property as the same name as your configuration (BaseTableProperty.Name == MyObj.<PropertyName> - and then attempt the type cast to that type as you enumerate the record set.
Then, you simply add another property to your object, another record to the database in BaseTableProperty, and then you can store values for that guy in BaseTableValue.
Example:
RecordId
========
1
PropertyId Name
========== ====
1 SomeProperty
ValueId RecordId PropertyId Value
======= ======== ========== =====
1 1 1 100
You have two result sets, one for basic data, and one joined from the Property and Value tables. As you enumerate each record, you see a Name of SomeProperty - does typeof(MyObj).GetProperty("SomeProperty") exist? Yes? What it it's data type? int? Ok, then try to convert "100" to int by setting the property:
propertyInfo.SetValue(myNewObjInstance, Convert.ChangeType(dbValue, propertyInfo.PropertyType), null);
For each property.
Even if you said you cannot use them, that is what most ORM do. Depending on which one you use (or even create if it's a learning experience), they will greatly vary in complexity and performance. If you prefer a light weight ORM, check Dapper.Net. It makes use of generics as well, so you can check the code, see how it works, and create your own solution if needed.