How to initialize a table column after a certain migration - c#

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.

Related

Entity Framework code-first migrations does not generate a foreign key and ignores the ForeignKey data annotation

I am trying to add a foreign key to my database table using EF code-first migrations, but when I run add-migration, the Up() and Down() methods in the generated migration are empty.
The base table to which the foreign key should link is Reservation and the table to which I am trying to add the key is Batch.
Reservation model class:
public class Reservation
{
[Key]
public int ReservationId { get; set; }
public virtual ICollection<Batch> Batches { get; set; }
...
}
Batch model class:
public class Batch
{
[Key]
public int BatchId { get; set; }
public int ReservationId { get; set; }
[ForeignKey("ReservationId")]
public Reservation Reservation { get; set; }
...
}
The Reservation attribute was previously called TempReservation and did not have a [ForeignKey] annotation which is why the foreign key did not get created in the first place.
I tried fixing it by adding the [ForeignKey] annotation and changing the property name to Reservation like in the above code snippet shows, but to no avail.
The migration always ignores my changes, giving me empty Up() and Down().
I have other model classes that follow the same "structure" and they all have foreign keys without any issues. The only difference is that I am adding this FK after the Batch table was already created.
If you started from scratch, your code model setup should work, so your model looks ok. The problem is that by not using the [ForeignKey] attribute the first time, your database schema now likely has an additional Shadow Property column called Reservation_ReservationId. However as this field isn't an active declaration in your model, it's hard to target directly as your convention configuration can greatly affect this, for instance, if you have configured the appropriate convention, the shadow property
may have already been named ReservationId.
tried fixing it by adding the [ForeignKey] annotation and changing the property name to Reservation like in the above code snippet shows, but to no avail.
As suggested above, the database may have already generated the correct foreign keys in the database, based on public virtual ICollection<Batch> Batches { get; set; }.
Something to remember:
Whenever making changes to the model that rename fields and or modify indexes or relationships on existing fields, you must perform these operations in separate migrations or the migration generation logic can't understand you will have to apply the change manually.
If you do these in one hit, you will almost always have to manually edit the migration file in some way.
after applying your manually modified migration logic to the database, if you now run the add-migration command it should generate an empty migration. If this is the case then you can generally move on.
Instead you could have followed this process:
Rename the navigation property, and any other fields.
Add-Migration... review the generated output
'Update-Database`...
Add the ForeignKey attribute
Add-Migration... review the output, if this is empty, check that the foreign key is not already correctly defined in the database
'Update-Database`... if necessary
Code First Migrations is not perfect out of the box
it is still a very usable tool but you need to review the generated code and add to is as necessary. It is not hard to extend it to support default value declarations, or any SQL DDL management queries when or if you need to.

How to Remove a Attribute of a Property in a Class

I want to do some changes in a table that is already exist(i am using sqlite). I want to remove a Attribute of a property in a class. Remove [Required] attribute. How can i do that, what should i change, should i some changes in DbContext or migration folder or what commands can i use in package manager console.
public class Appointment
{
[Required]
[MaxLength(50)]
public string Company { get; set; }
This is a good example of why it is better to use fluent API then attributes to specify your database: you want the same class to be used in a different database.
The DbContext defines the database: what tables are in it, how do the tables relate towards each other, what classes do represent the tables, and what are the constraints to the tables.
For instance, maybe in one database you want Company to have a MaxLength of 50 characters, in another database you might desire a length of 100 characters. Some databases need a simple DateTime, others require a DateTime2. Or maybe you want a different precision for your decimals?
Hence it is usually better to specify the database statistics where it belongs: in the definition of the database, which is the DbContext.
Back to your question
It depends a bit on the entity framework that you are using on how the database reacts if during migration you use remove the Required attribute and move it to fluent API. I guess it is best to experiment with it.
In OnModelCreating, or the migration equivalent of it, you will have something like:
var entityAppointment = modelBuilder.Entity<Appointment>();
var propertyCompany = entityAppointment.Property(appointment => appointment.Company);
propertyCompany.IsOptional()
.HasMaxLength(50)
...; // other non-default specifications of Company
// like columnName, IsUnicode, etc
When migrating the statements might be little different, but I guess you get the gist.

Entity Framework Migration with no model changes

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.

Generate Guid on database but only when not set in entity

I have a case where I need to add a Guid Property that is NOT the primary key, and that could be shared with several objects in the table.
What I'd like to do is:
Generate the Guid on the database when I don't give it a value
Set a Guid (instead of generating it) when I have its value
Both of this would be done on Insert only, Updates won't touch these values.
What I have tried:
Add the [DatabaseGenerated(DatabaseGeneratedOption.Identity)] attribute: only works when I don't need to set the Guid manually
Add the [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribute: doesn't work when I don't set the Guid manually
I've seen quite a lot about this topic, and the closest thing would be this article:
http://www.davepaquette.com/archive/2012/09/23/calculated-columns-in-entity-framework-code-first-migrations.aspx
But we don't (and won't) use Migration in our project, so that doesn't seem fit.
or this SO question, but that would mean generating Guids in .Net (which doesn't seem to be very clean, at least in my opinion): EF, Code First - How to set a custom Guid identity value on insert
Is there a way to generate the Guid Database side, AND set it when I need to in EF Code first?
If not, what would be a good alternative? Is it really a bad idea to generate Guids on the .Net side? (I could go with that if nothing else is possible)
I will assume that you are using MS-SQL , then you can do the following
To use the execute command
public class YourDbContext: DbContext
{
public YourDbContext():base("ConnectionString")
{
if (Database.Exists())
{
Database.ExecuteSqlCommand("if object_id('CT_DefaultGuid') is null alter table YourTable add constraint CT_DefaultGuid default newid() for YourColumn");
}
}
}
To set the Id from .Net, you can do the following
Create a Base Entity contains Id property
In the constructor you check if the Id is empty then initialize it
Let all the entities you have to inherits from this class
Your base class should look like
public class BaseEntity
{
public BaseEntity()
{
if(Id==Guid.Empty)
Id = Guid.NewGuid();
}
public Guid Id{get;set;}
}
To use the migration for existing database
from PMC => Enable-migrations
from PMC => Add-Migration "FirstRun"
open the generated migration file and make sure to empty the Up and Down methods ( this will not apply any changes on the database)
Add the corresponding alter column fluent code using Sql("") method in the Up method
from PMC => Update-Database -Script , to make sure that only sql statement generated is the alter table statement
from PMC => once you are sure that the desired statement is the only appearing in the SQL script , then apply : Update-Database.
Your class should like this
public class FirstRun : DbMigration
{ public override void Up()
{
Sql("alter table YourTable add constraint CT_DefaultGuid default newid() for YourColumn");
}
}
I recommend the last approach, it will be executed once, and you can add changes later to your database.
Hope this will help you

EF6 ignoring [Table] and [Column] in entity classes

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>();

Categories