I currently have a "server" entity, defined as such :
public class EntityServer
{
public int Id { get; set; }
public string Name { get; set; }
}
I wanted to add a new "Host" entity, defined as such :
public class EntityHost
{
public int Id { get; set; }
public string Name { get; set; }
public string PublicIP { get; set; }
private ICollection<EntityServer> _servers;
public virtual ICollection<EntityServer> Servers
{
get { return _servers ?? (_servers = new HashSet<EntityServer>()); }
set { _servers = value; }
}
}
So i added
public virtual EntityHost Host { get; set; }
to my server entity to link those entities with a one to many relationship
modelBuilder.Entity<EntityHost>()
.HasMany<EntityServer>(x => x.Servers)
.WithRequired(x => x.Host);
And generated a migration acordingly :
public partial class MultiHosts : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.EntityHosts",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
PublicIP = c.String(),
})
.PrimaryKey(t => t.Id);
AddColumn("dbo.EntityServers", "Host_Id", c => c.Int(nullable: false));
CreateIndex("dbo.EntityServers", "Host_Id");
AddForeignKey("dbo.EntityServers", "Host_Id", "dbo.EntityHosts", "Id", cascadeDelete: true);
}
public override void Down()
{
DropForeignKey("dbo.EntityServers", "Host_Id", "dbo.EntityHosts");
DropIndex("dbo.EntityServers", new[] { "Host_Id" });
DropColumn("dbo.EntityServers", "Host_Id");
DropTable("dbo.EntityHosts");
}
}
I've got some troubble setting a code first migration to add all it together as it outpout me a foreign key violation error when i try to access the context (which i understand as the server entity isn't linked to a host, as required by the model, because the hosts table is empty and I can't access the hosts entities to add one because of the FK violation ....)
So, my question is : how should I insert a default host entites for the existings server ?
As a trick you could first set the Server as Optional
modelBuilder.Entity<EntityHost>()
.HasOptional(x=>x.Server)
.WitMany(x => x.Hosts);
Run
Add-Migrations set_server_optional
update-Database
Update your Database and then change the Server as Required
modelBuilder.Entity<EntityHost>()
.HasRequired(x=>x.Server)
.WithMany(x => x.Hosts);
And finally
Add-Migrations set_server_required
update-Database
Related
I have a migration that got added once and applied successfully:
public partial class AddedTablesForBranchData : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.SalesArea",
c => new
{
PostalCode = c.Int(nullable: false, identity: true),
Location = c.String(),
Branch_Id = c.String(maxLength: 128),
})
.PrimaryKey(t => t.PostalCode)
.ForeignKey("dbo.SalesBranch", t => t.Branch_Id)
.Index(t => t.Branch_Id);
CreateTable(
"dbo.SalesBranch",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
Name = c.String(),
Contacts = c.String(),
})
.PrimaryKey(t => t.Id);
}
public override void Down()
{
DropForeignKey("dbo.SalesArea", "Branch_Id", "dbo.SalesBranch");
DropIndex("dbo.SalesArea", new[] { "Branch_Id" });
DropTable("dbo.SalesBranch");
DropTable("dbo.SalesArea");
}
}
Sadly the the PostalCode is an integer. I had to change it to a string due to localization...
Thus some migrations later I added a new migration:
public partial class ReCreateSalesTables : DbMigration
{
public override void Up()
{
DropForeignKey("SalesArea", "Branch_Id", "SalesBranch");
DropIndex("SalesArea", new[] { "Branch_Id" });
DropTable("dbo.SalesArea");
DropTable("dbo.SalesBranch");
CreateTable("SalesBranch",
c => new
{
Id = c.String(false, maxLength: 128),
Name = c.String(),
Contacts = c.String()
})
.PrimaryKey(t => t.Id);
CreateTable("SalesArea",
c => new
{
Id = c.Int(false, true),
PostalCode = c.String(maxLength: 32),
Location = c.String(),
BranchId = c.String(nullable: false, maxLength: 128)
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.SalesBranch", t => t.BranchId)
.Index(t => t.PostalCode, unique: true);
}
public override void Down()
{
throw new Exception("It has never been our intention to use a down migration, else data might be lost...");
}
}
Then I run into the "a table con not have multiple identity columns" problem due to PostalCode being a identity column and in the new migration I have Id being a column Identity
Thus I had to drop both tables in the new migration and re-create those tables with the new schema.
There seems to be no problem on my local machine/development environment But when I run the integration tests or before any test is run I do this:
[TestClass]
public sealed class InitializeDatabase
{
[AssemblyInitialize]
public static void AssemblyInit(TestContext x)
{
using (var context = new LeadContext())
{
// Create database outside of the test transactions else you get a nice exception...
context.Database.Delete();
context.Database.Create();
new Configuration().FillEnums(context);
}
}
}
It is deleting the old database and creating a new database using all migrations. This AssemblyInit method runs fine when I debug it, but after leaving the method some second later I can see this output in my integration test:
Result Message: Initialization method IntegrationTests.SalesDataTests.Init threw exception. System.Data.Entity.Core.EntityCommandExecutionException: System.Data.Entity.Core.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Invalid column name 'Branch_Id'..
I am not able to debug my integration test directly as I never get there, so the problem must be the context.database.create() method.
Why is EF complaining that the old/former foreign key column 'Branch_Id' is invalid?
I do not understand that scenario.
Can anybody help please :-)
UPDATE
Question:
What changed between AddedTablesForBranchData and migration ReCreateSalesTables ?
Answer:
I introduced a property Id (identity column/string) and changed the property PostalCode to integer/unique:true.
UPDATE 2
There exist no fluent configurations about any SalesX table.
Model
[Table("SalesBranch")]
public class SalesBranch
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
public string Contacts { get; set; }
public virtual ICollection<SalesArea> SalesAreas { get; set; }
}
[Table("SalesArea")]
public class SalesArea
{
public int Id { get; set; }
public string PostalCode { get; set; }
public string Location { get; set; }
public virtual SalesBranch Branch { get; set; }
public int BranchId { get; set; }
}
By annotating the property:
public virtual SalesBranch Branch { get; set; } with [ForeignKey("BranchId")] it fixed the problem!
It seemed the model were out of sync with the migrations!
I have a model:
public class Something
{
public int Id { set; get; }
public string Name{ set; get; }
}
Also I have this class:
public class SomethingConfiguration : EntityTypeConfiguration<Something>
{
public SomethingConfiguration()
{
HasKey(t => t.Id).Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
Everything works fine and the Id generates automatically after inserting to DB and save changes (commit).
Now I want to add another column IdString (Id as string instead of int), so I can use it for searching-by-Id manipulations (autocompletes and more). How can I add IdString column that will get the automatic Id as string and will be save automatically while inserting and saving? Is it possible?
In Sql Server you can define a computed column with an underlying formula.
ALTER TABLE dbo.Something Add IsString AS cast(Id as nvarchar)
These columns can be mapped in you model like this.
public partial class Something
{
public int Id { get; set; }
public string IdString { get; set; }
public string Name { get; set; }
}
this.Property(p => p.IdString)
.HasMaxLength(30)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
And then used in your query.
var data1 = db.Something.Where(p => p.IdString.Contains("123"));
Because of the DatabaseGeneratedOption.Computed definition, EntityFramework will request the current Value of the column every time you update the row.
But if you only want a translatable version of ToString() for an integer value you could just use SqlFunctions.StringConvert instead.
var data = db.Something
.Where(p => SqlFunctions.StringConvert((decimal)p.Id).Contains("12"));
Update:
Add computed column with a Migration.
public override void Up()
{
CreateTable(
"dbo.Something",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 255),
})
.PrimaryKey(t => t.Id);
Sql("ALTER TABLE dbo.Something Add IsString AS cast(Id as nvarchar)");
}
public override void Down()
{
DropTable("dbo.Something");
}
class Person
{
public int Id { get; set; }
//omitted for brevity
public bool IsActive { get; set; }
}
class Person
{
public int Id { get; set; }
//omitted for brevity
public int Status { get; set; }
}
enum Status
{
Active,
Inactive,
NoState,
}
Person is a database table with data. Due to new requirement I would like to remove IsActive and have a Status enum. I would also like to move the data from IsActive column to Status column without losing it. I'm using EF Code First. How do I go about it?
I finally solved this. The steps I followed are as follows;
Scaffold an Add-Migration AlterStatusEnum through package manager console.
In the Up method of the generated partial class, change the
AddColumn("dbo.Person","Status", c => c.Int(nullable:false));
to
RenameColumn("dbo.Person", "IsActive", "Status");
Add an alter statement
AlterColumn("dbo.Person", "Status", c => c.Int(nullable: false));
this got the job done.
Sample code
public partial class AlterStatusEnum : DbMigration
{
public override void Up()
{
RenameColumn("dbo.Person", "IsActive", "Status");
AlterColumn("dbo.Person", "Status", c => c.Int(nullable: false));
}
public override void Down()
{
// Removed for brevity
}
}
In a nutshell all am doing is renaming the existing column and altering its datatype.
I reimplementing database created automatically by SimpleMembershipProvider. Actually I have a question about 2 tables linking:
create table user_profiles
(
id int not null identity, /* PK */
username varchar(128) not null,
.........
);
create table membership
(
userid int not null, /* FK to user_profile. */
..............
);
I'd like to create relationship between initial POCO classes:
public class UserProfile : BaseType
{
public virtual Membership Membership { get; set; }
......
public string UserName { get; set; }
......
}
public class Membership
{
public virtual int UserId { get; set; }
public virtual UserProfile User { get; set; }
......
}
In Membership property UserId used as PK and in the same time as FK in database. I tried following configurations:
public class UserProfileConfiguration : EntityTypeConfiguration<UserProfile> {
public UserProfileConfiguration() {
HasKey(k => k.Id);
Map(m => m.ToTable("user_profiles"));
HasRequired(t => t.Membership)
.WithRequiredPrincipal(t1 => t1.User)
.Map(m => m.MapKey("userid"));
....
}
}
public class MembershipConfiguration : EntityTypeConfiguration<Membership> {
public MembershipConfiguration() {
HasKey(k => k.UserId);
Map(m => m.ToTable("webpages_Membership"));
//Property(x => x.UserId).HasColumnName("userid");
}
}
When line in MembershipConfiguration commented out (like in sample) command Add-Migration creates 2 records in migration command:
c => new {
UserId = c.Int(nullable: false, identity: true),
.............
userid = c.Int(nullable: false),
If I uncommenting it command failed with error message Each property name in a type must be unique. Property name 'userid' was already defined.
How could I claim required result, use column 'userid' as PK and FK in the same time?
We've been using a complex type CreditCardTransaction associated with our purchase orders. It really should have been it's own entity, and I am trying to fix this now.
How would you write a migration from this:
[ComplexType()]
public class CreditCardTransaction
{
public String SomeTransactionData { get; set; }
}
into this entity:
public class CreditCardTransaction
{
public int Id { get; set; }
public String SomeTransactionData { get; set; }
}
This is what is generated for me. Can I easily move the data in the original table to the other table?
public partial class test : DbMigration
{
public override void Up()
{
CreateTable(
"CreditCardTransactions",
c => new
{
Id = c.Int(nullable: false, identity: true),
SomeTransactionData = c.String()
})
.PrimaryKey(t => t.Id);
AddColumn("PurchaseOrder", "Transaction_Id", c => c.Int());
AddForeignKey("PurchaseOrder", "Transaction_Id", "CreditCardTransactions", "Id");
CreateIndex("PurchaseOrders", "Transaction_Id");
DropColumn("PurchaseOrders", "Transaction_SomeTransactionData");
}
// Down() snipped
}
I believe you can. Modify your Up method and prior to call DropColumn use Sql method with custom SQL transferring data from PurchaseOrders to CreditTransactions and fixing FK relationships.