I am wondering if it makes sense to create a migration for a scenario where there aren't necessarily model changes, but the enum property on a model has been changed. I'm using .NET 4.6.2 and Code-First Entity Framework
I have the following EF-tracked log model:
[Table("Logs")]
public class Log
{
public int Id { get; set; }
public DateTime Timestamp { get; set; }
public LogType Type { get; set; }
}
The LogType enum currently has around 40 values, of which, 13 have become obsolete or deprecated. I am going through the process of removing references to the obsolete/deprecated enum values.
As such, the values of the LogType enum are being changed. For example, LogType.ConnectionTimeout used to have the value 16, but now has the value 3.
In my database (MSSQL), the Type column is stored as an int, and I have written SQL that deletes all entries with an obsolete/deprecated enum value, and I have also written SQL that updates the other enum values to match what their new values are (e.g. changing 16 to 3 using my previous example of Type.ConnectionTimeout).
My question is this: Is it a good practice to put that SQL in a migration that is able to be Up()'d and Down()'d? My other question is, is that even possible? Or does there need to be actual model changes to create a migration? I'd like to be able to have this SQL tracked and stored in version control, as well as the ability to Up() and Down() it in the future if need be.
Thanks, and apologies in advance if this is a duplicate -- I wasn't able to find a similar question through my searches.
Related
I am new to Entity Framework. I started with database first approach which created my classes corresponding to the tables I selected. I am using WPF. Unfortunately there is a problem occurred while EF6 was mapping classes. The assigned type for a field is byte while in some cases the value exceeds the byte constraints. So, I want to replace it with either int or double. How do I change the model field types without any changes made for the used database?
namespace DataChrome{
public partial class REGISTRY_MEST{
public byte MEST { get; set; } //Wrong typed field
public string MESTNAME { get; set; }
public Nullable<byte> IDSTRAT { get; set; }
public Nullable<int> MESTOLD { get; set; }
}
}
So, giving 7 hours to this problem I found the right solution. All you need is just to set user mapping rules in appconfig file. For more details: visit this page.
The type change should be possible by editing the edmx model: click the MEST property of the class inside the edmx model, then set the Type accordingly in the Properties Window and save the model.
You are running a risk by doing this, as it might be possible to store a value too big for the column if you just change the type this way. You noted that you are using Oracle as the underlying DB, so it might very well be the case that EF generated a "wrong" type for that property.
If you are absolutely sure that the DB will accept the expanded type (int, double) then it should be safe to edit the property as I mentioned at the start. Otherwise you would have to change the DB and generate the class anew - you might need to delete the class from the model and add it again, because not all changes to the table are picked up by the automatic update process.
Here is the super simple class I'm trying to create.
public class Company
{
public int ID { get; set; }
[Column(TypeName = "VARCHAR(254)")]
[Index]
public string Name { get; set; }
[Index]
public int stupidField { get; set; }
}
My goal was to force Name to be unique, so I added the decoration [Index(IsUnique = true)]. But no unique index was created, so I figured I'll first try to solve the simpler problem of creating any index. Because I read here that indices cannot be created for columns of type varchar(max), I limited the length of the Name field. Still no luck. I even tried a few different syntaxes for limiting the length of the field, but still no index.
To see if something other than string length was at play, I created the integer field stupidField, but I can't index that field either. So now I'm completely out of ideas as to what could be wrong. Please help me!
Check out this screenshot from MS SQL Server Management Studio that shows that my fields are being created but not the indices.
Note: I'm certain migrations are not the issue.
Some of the people I've read about on SO were updating their classes, but those changes were not reflected in the database because of problems with their migrations. That is not relevant here. I delete the database and recreate it every time I make a change. (I even make silly changes like renaming my fields, just to make sure that I can still affect the database.)
Turns out I'm actually using Entity Framework Core, not Entity Framework. In Entity Framework Core, indices cannot be created using attributes, although they can be created using fluent API. See Microsoft's documentation.
Using Entity Framework and ASP.Net, one of my entity models is:
public class Records {
public bool IsPublic {get; set;}
// Some other properties
}
I have changed the model to:
public class Records {
[obsolete]
public bool IsPublic {get; set;}
public RecordAccess AccessLevel {get; set;}
}
Where
public enum RecordAccess {Public, Private, Group };
After this migration, I need to initialize the new column (AccessLevel) based on IsPublic column and some other tables.
So I wrote the c# code to initialize the AccessLevel and put it in the Seed method.
Note
I want to delete IsPublic column since it's no longer being used.
I have 4 different databases which I'm working on: localdb, test server, staging server and production server. These databases might not be in the same state. So data and the table schemas might be different.
Question
Is Seed method the right place to put the initialization?
(If I delete the IsPublic from the Record class the code won't be compiled)
How can I roll back the migration and data to what it was before?
I would write the initialization in the migration itself using the Sql("[INSERT SQL HERE]") method. This is assuming that this is a one time move.
If you put it in the Seed method, the code will run every time the database is updated with new migrations, which means that you will probably end up writing code that checks to see if this has already been done and ignoring it, making it one time use, which basically makes it a migration.
To rollback your database, in the Package Manager Console window, use the Update-Database -TargetMigration [name of last good migration]. More Info here.
I used EF6 Database First tools to generate C# classes for 2 tables from my database, then (as advised in the blog post that helped me through the steps to do that) copied the resulting .cs files into a new project. I made a few edits to the classes to support sensible names in my C# code. Here's a snippet of one of the classes with "LongTableName" replacing a strangely long name used in the database.
namespace RidesData
{
[Table("LongTableName")]
public partial class PhoneData
{
[Key]
[Column("LongTableNameID")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Column("LongTableNameAccountID")]
public int AccountID { get; set; }
// more fields
}
}
I am not in control of the table names, nor the fact that the many of the column names have the table name as prefixes. But the Code First ideas in EF6 should, I thought, let me use reasonable class and field names despite that. (The Database First code generator did a good job of adding code to OnModelCreating to specify that none of the columns corresponding to C# string data used Unicode.)
My model (generated by the EF6 tools and that inherits from DbContext) includes (after some renaming by me)
public virtual DbSet<PhoneData> PhoneRecs { get; set; }
and I thought all would be fine when I created an instance of PhoneData, populated it, and did
Model.PhoneRecs.Add(phoneData);
but the first thing that happened when I ran the code -- well before any call to SaveChanges() -- was that EF generated CREATE TABLE statements for the two tables; the table corresponding to the snippet above was named PhoneDatas (not using the specified table name) and the column names were the same as the field names in the class (not what was specified in the Column(...) attributes).
Of course the table I had specified did not need to be created. EF just had to grok that I wanted to use the table and column names I had specified via attributes.
I did not expect this failure of explicit Code First attributes. Does anyone have a clue why this isn't doing what I want, or how to fix it? (Do I have to do something to specify the table & column names in OnModelCreating as well as -- or instead of -- the attributes?)
Note that the project that I copied these classes into had never "seen" the database before. There are no vestiges of any "models" left over from tooling having looked at the database. Also, I hope it does not matter that I've tried to keep things on .Net 4.0 (avoiding going to 4.5 in this code).
Any assistance would be appreciated.
I'm not a big fan of DataAnotations either. Use EntityTypeConfiguration. It gives you the naming flexibility I think you are looking for.
Example.
public class PhoneData
{
public int ID {get;set;}
public string SomeProperty {get;set;}
}
public class PhoneDataMap : EntityTypeConfiguration<PhoneData>
{
public PhoneDataMap()
{
ToTable("WhatEverYou_Want_to_call_this");
HasKey(m => m.Id);
Property(m => m.SomeProperty).HasColumnName("whatever").IsRequired();
//etc.
}
}
Then in your on ModelCreating you add
modelBuilder.Configuration.Add(new PhoneDataMap());
On a side note, if you are having trouble with pluralization of your table names you can add this to OnModelCreating as well
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
I've a table with 52 columns in my database and I want to write a function to create a row in that table.
In my case, I don't want to use all columns in that table, so I created my model like this.
[Table("CUST_MASTER")]
public class CustomerMaster
{
[Key]
[Column("CUSTOMER_ID")]
public string Id { get; set; }
[Column("CUSTOMER_NAME")]
public string Name { get; set; }
[Column("CUSTOMER_CITY")]
public string City { get; set; }
}
Is there any way to send only this data via Entity framework and set all other not nullable fields to some default data(for strings "", for decimals 0.0, etc.) without writing all that fields in my model and doing it manually?
When you do not incorporate a Table-column in your model then it won't be mapped and it will be totally ignored by all generated SQL.
So the only option is to specify a default value in your Database.
If you set the values in the constructor you will have a default value through your code, but you could look into enabling migrations instead, this way you can set default values. look at this stackoverflow question
I think this old suggestion is what you want. It explicitly mentions the lack of mapping between the conceptual model and the storage model. Not a very popular/understood idea though.
Update: FWIW, this suggests that it is already possible in non-Code-First scenarios.