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>
//------------------------------------------------------------------------------
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 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.
I have an MVC 5 application that uses Entity Framework 6 Database First approach.
So far It is working well, but I have come across an unwanted behaviour.
If I select 'Update Model From Database', select the 'Add' Tab, and then select the Tables or Views I would like to add and click Finish, it adds the Tables and/or Views I specified no problem.
However, the unwanted behaviour is that, even though I didn't select the Refresh Tab, it seems every single model is automatically being refreshed.
This means all my custom Attributes on my models are being removed.
Is there any way to specify to only add the specified Tables or Views without refreshing all models, or if it refreshes all models, to retain the Attributes I have specified?
Visual Studio information: Microsoft Visual Studio Professional 2013
Version 12.0.40629.00 Update 5
Microsoft .NET Framework
Version 4.5.51650
Installed Version: Professional
Is this a bug or intended use?
Thanks
Neill
In order to modify autogenerated classes it's advised to use a partial class, that way your changes won't be lost when the class is refresh / generated again.
Simple example of a class and a partial class expanding on its attributes and methods
// Assume this is autogenerated by EntityFramework
public class Book {
public int Id {get; set;}
public string Title {get; set;}
}
// In a different file.cs
public partial class Book {
[Required]
public string Author {get; set;}
public override ToString(){
// Some code goes here
}
}
In that example, if EntityFramework generates a new Book model, the changes you've done to that model via the partial class won't be lost at all.
Check out this article for more information on partial classes and this question for more info on the advantages of using partial classes.
Edit: if you need to add attributes to existing properties of your autogenerated class this answer might help you out as well.
You really need to use partial classes so that you can refresh the edmx to your heart's content.
look here for a great tutorial on the subject.
I am currently attempting to implement a revision history screen in an MVC app. I need to be able to retrieve the names of fields which have changed in each revision using Envers. So I am following directions here: http://envers.bitbucket.org/#envers-tracking-modified-entities-revchanges
I am using the second option since we have a custom revision entity. It looks like this:
[RevisionEntity(typeof(MyRevisionListener))]
public class RevisionEntity : DefaultTrackingModifiedEntitiesRevisionEntity
{
public virtual Person User { get; set; }
}
As you can see I am inheriting from DefaultTrackingModifiedEntitiesRevisionEntity in order to make sure the class has the property to hold the modified entities' names.
Per the documentation this should create a table called RevChanges in which this information is stored with reference to the revisions table:
Envers provides a simple mechanism that creates REVCHANGES table which
stores entity names of modified persistent objects. Single record
encapsulates the revision identifier (foreign key to REVINFO table)
and a string value.
I am never seeing this table created. I tried creating such a table myself along with a related class and wiring up the mappings, but I don't see how Envers would know to put the data into that table without me configuring it somehow. I just get an exception saying that the object is different from the target type when the get method is called on the new type.
How can I get this to work?
If you use a custom revision entity, you need to map this just like you do with normal entites.
http://envers.bitbucket.org/#revisionlog
I have a File entity and a User entity. The File entity has a 1:1 relationship with the User entity through a property called LastChangeUser (this records the user who last changed the file). There's also a field within the File entity named LastChangeUserId, which is the actual FK relationship. The relationship is one-way: the User entity has no navigation property leading back to the File entity.
class File
{
public int Id { get; set; }
public int? LastChangeUserId { get; set; }
public virtual User LastChangeUser { get; set; }
}
class User
{
public int Id { get; set; }
}
When a File is changed, I need to set the LastChangeUser for the File. I only have the ID of the user to hand, not the complete User object. So, I'm doing this:
file.LastChangeUser = null;
file.LastChangeUserId = userId;
This seems to work on creating the file, when the File object is newly-created (a POCO which is then added to the entity collection).
However, it does not work when updating the file, when the File object is an existing object retrieved (as a proxy) from the DB.
In the latter case, I end up with a NULL in the DB for the LastChangeUserId field. (After a call to SaveChanges, the object has null in both the LastChangeUser and LastChangeUserId fields).
Maybe I'm just doing the wrong thing here? What's the right way? Do I really need to go get the User object in order to set the LastChangeUser property?
The reason #kevin_fitz solution works here is due to the way changetracking and validation in EF work. The default behavior for change tracking in EF is a method called snapshot tracking which essentially clones an initial state of every entity when its first loaded. When you go to save changes in EF the original snapshot for each entity is compared to the current state object (the one which you are modifying) and any differences are persisted to the database.
Along side this EF also performs pre-submit validation on entities (which can FYI be disabled).
In your case you have made two changes to the model which will be detected by the snapshot tracker on save (and they actually conflict). The tracker however will try and process both of these through the validation rules which will pickup that this is a required relationship and cant be set to null. This is why you are seeing this error and why removing the null update fixes your problem.
On a sidenote, you actually only need to update either the object or the key on a navigation property to trigger that database relationship to be updated. For more details on how navigation properties work in EF codefirst checkout my article here: http://blog.staticvoid.co.nz/2012/07/entity-framework-navigation-property.html