EF6: Switching Identity On/Off with a Custom Migration Operation
Here is the scenario, I have a class in my Code First model that has a primary key configured as an identity column. I want to change it so that I can generate values in my code. Alternatively I may want to change an existing column – that my code is generating values for – to have values generated by the database.
Migrations can’t handle this operation yet (although we plan to add support in the future). We don’t support it yet because it’s complicated… in SQL Server you can’t just make the change with an ALTER statement, some Jedi SQL skills are required.
I’m going to be using the techniques shown in my past post on Writing Your Own Migrations Operations. I’m not going to rehash what I already covered there. If you want more details about the components I’m going to write in this post… go read that post
.
Downloadable source code is included at the bottom of the post.
The Process
The steps for changing the identity setting on a column in SQL Server are:
- Drop all foreign key constraints that point to the primary key we are changing
- Drop the primary key constraint
- Rename the existing column (so that we can re-create the foreign key relationships later)
- Add the new primary key column with the new identity setting
- Update existing data so that previous foreign key relationships remain
- If the new column is an identity column, we need to update all foreign key columns with the new values
- If the new column doesn’t have identity on, we can copy the old values from the previous identity column
- Drop old primary key column
- Add primary key constraint
- Add back foreign key constraints
Component 1/3: Custom Migrations Operation
Our first task is a custom migrations operation that holds all the info we need to be able to generate SQL in our provider. In our case that is:
- Whether we are switching identity on or off
- What table and column we are changing the identity setting for
- Any foreign key columns that reference this column
If you want a more detailed explanation of custom migrations operations then see my previous post on writing custom migration operations.
using System.Collections.Generic;
using System.Data.Entity.Migrations.Model;
namespace IdentityDemo.Migrations
{
public class ChangeIdentityOperation : MigrationOperation
{
public ChangeIdentityOperation()
: base(null)
{ }
public IdentityChange Change { get; set; }
public string PrincipalTable { get; set; }
public string PrincipalColumn { get; set; }
public List<DependentColumn> DependentColumns { get; set; }
public override bool IsDestructiveChange
{
get { return false; }
}
}
public enum IdentityChange
{
SwitchIdentityOn,
SwitchIdentityOff
}
public class DependentColumn
{
public string DependentTable { get; set; }
public string ForeignKeyColumn { get; set; }
}
}
Component 2/3: Migration Extensions Methods
The next step is to provide some nice extension methods for creating the migration operation within a DbMigration. I’ve built a little object model that allows us to use a fluent style API to specify any dependent columns. In the last section you’ll see how this looks when used in a migration.
using System.Collections.Generic;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
namespace IdentityDemo.Migrations
{
public static class MigrationExtensions
{
public static IdentityChangeOperationWrapper ChangeIdentity(
this DbMigration migration,
IdentityChange change,
string principalTable,
string principalColumn)
{
var operation = new ChangeIdentityOperation
{
Change = change,
PrincipalTable = principalTable,
PrincipalColumn = principalColumn,
DependentColumns = new List<DependentColumn>()
};
((IDbMigration)migration).AddOperation(operation);
return new IdentityChangeOperationWrapper(operation);
}
public class IdentityChangeOperationWrapper
{
private ChangeIdentityOperation _operation;
public IdentityChangeOperationWrapper(ChangeIdentityOperation operation)
{
_operation = operation;
}
public IdentityChangeOperationWrapper WithDependentColumn(
string table,
string foreignKeyColumn)
{
_operation.DependentColumns.Add(new DependentColumn
{
DependentTable = table,
ForeignKeyColumn = foreignKeyColumn
});
return this;
}
}
}
}
Component 3/3: Custom SQL Generator
Finally, we need to write a SQL Generator that knows how to handle this operation for our database provider. I’m using SQL Server, so I’ve derived from SqlServerMigrationSqlGenerator and added logic to handle our custom operation.
The comments in the code show how each block of code relates to the steps in the ‘Process’ section at the start of the post.
Fortunately, there are existing migration operations that handle every step (except for the data migration in Step 5), so we’re making use of those.
using System.Data.Entity.Migrations.Builders;
using System.Data.Entity.Migrations.Model;
using System.Data.Entity.Migrations.Sql;
namespace IdentityDemo.Migrations
{
class MySqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(MigrationOperation migrationOperation)
{
var operation = migrationOperation as ChangeIdentityOperation;
if (operation != null)
{
var tempPrincipalColumnName = "old_" + operation.PrincipalColumn;
// 1. Drop all foreign key constraints that point to the primary key we are changing
foreach (var item in operation.DependentColumns)
{
Generate(new DropForeignKeyOperation
{
DependentTable = item.DependentTable,
PrincipalTable = operation.PrincipalTable,
DependentColumns = { item.ForeignKeyColumn }
});
}
// 2. Drop the primary key constraint
Generate(new DropPrimaryKeyOperation { Table = operation.PrincipalTable });
// 3. Rename the existing column (so that we can re-create the foreign key relationships later)
Generate(new RenameColumnOperation(
operation.PrincipalTable,
operation.PrincipalColumn,
tempPrincipalColumnName));
// 4. Add the new primary key column with the new identity setting
Generate(new AddColumnOperation(
operation.PrincipalTable,
new ColumnBuilder().Int(
name: operation.PrincipalColumn,
nullable: false,
identity: operation.Change == IdentityChange.SwitchIdentityOn)));
// 5. Update existing data so that previous foreign key relationships remain
if (operation.Change == IdentityChange.SwitchIdentityOn)
{
// If the new column is an identity column we need to update all
// foreign key columns with the new values
foreach (var item in operation.DependentColumns)
{
Generate(new SqlOperation(
"UPDATE " + item.DependentTable +
" SET " + item.ForeignKeyColumn +
" = (SELECT TOP 1 " + operation.PrincipalColumn +
" FROM " + operation.PrincipalTable +
" WHERE " + tempPrincipalColumnName + " = " + item.DependentTable + "." + item.ForeignKeyColumn + ")"));
}
}
else
{
// If the new column doesn’t have identity on then we can copy the old
// values from the previous identity column
Generate(new SqlOperation(
"UPDATE " + operation.PrincipalTable +
" SET " + operation.PrincipalColumn + " = " + tempPrincipalColumnName + ";"));
}
// 6. Drop old primary key column
Generate(new DropColumnOperation(
operation.PrincipalTable,
tempPrincipalColumnName));
// 7. Add primary key constraint
Generate(new AddPrimaryKeyOperation
{
Table = operation.PrincipalTable,
Columns = { operation.PrincipalColumn }
});
// 8. Add back foreign key constraints
foreach (var item in operation.DependentColumns)
{
Generate(new AddForeignKeyOperation
{
DependentTable = item.DependentTable,
DependentColumns = { item.ForeignKeyColumn },
PrincipalTable = operation.PrincipalTable,
PrincipalColumns = { operation.PrincipalColumn }
});
}
}
}
}
}
We need to register our new SQL Generator in our migrations configuration class.
namespace IdentityDemo.Migrations
{
using System.Data.Entity.Migrations;
internal sealed class Configuration : DbMigrationsConfiguration<IdentityDemo.BloggingContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "IdentityDemo.BloggingContext";
SetSqlGenerator("System.Data.SqlClient", new MySqlServerMigrationSqlGenerator());
}
}
}
Testing It Out
I have an existing Code First model that has a Blog class with an identity primary key. I already have migrations to make the database match the current model.
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
Now I change the Blog to have a non-identity key.
public class Blog
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
If I use the Add-Migration command after making the changes it scaffolds an AlterColumn call. But because migrations doesn’t know how to process identity changes the code won’t do anything when I run Update-Database.
namespace IdentityDemo.Migrations
{
using System.Data.Entity.Migrations;
public partial class RemoveIdentity : DbMigration
{
public override void Up()
{
AlterColumn("dbo.Blogs", "BlogId", c => c.Int(nullable: false));
}
public override void Down()
{
AlterColumn("dbo.Blogs", "BlogId", c => c.Int(nullable: false, identity: true));
}
}
}
However, I can replace it with a call to our new operation and everything works as expected.
namespace IdentityDemo.Migrations
{
using System.Data.Entity.Migrations;
public partial class RemoveIdentity : DbMigration
{
public override void Up()
{
this.ChangeIdentity(IdentityChange.SwitchIdentityOff, "dbo.Blogs", "BlogId")
.WithDependentColumn("dbo.Posts", "BlogId");
}
public override void Down()
{
this.ChangeIdentity(IdentityChange.SwitchIdentityOn, "dbo.Blogs", "BlogId")
.WithDependentColumn("dbo.Posts", "BlogId");
}
}
}
Source Code
If you want to grab the source code to play with… you’ll find it here.
Read Full Post | Make a Comment ( 4 so far )EF6: Writing Your Own Code First Migration Operations
Migrations provides a set of strongly typed APIs for performing common operation, for example CreateIndex(“dbo.Blogs”, “Url”). We also provide a method that allows you to run arbitrary SQL when you want to do something that isn’t supported by the API, for example Sql(“GRANT SELECT ON dbo.Blogs to guest”).
There are some disadvantages to the Sql method though… firstly that you have to write SQL and secondly your migration is no longer database provider independent.
Thanks to a recent pull request that we accepted from iceclow you can now add your own strongly typed APIs for operations that you find yourself needing.
Creating Our Own Operation
In our example we’re going to add an operation that allows us to grant permissions to a user for a table. Ultimately we want to write something like GrantPermissions(“dbo.Blogs”, “guest”, Permissions.Select).
The first step is to create a custom operation that derives from MigrationOperation. There really aren’t any requirements on the shape of this class other than implementing the IsDestructiveChange property.
using System.Data.Entity.Migrations.Model;
namespace ExtendingMigrations.Migrations
{
public enum Permission
{
Select,
Update,
Delete
}
public class GrantPermissionOperation : MigrationOperation
{
public GrantPermissionOperation(string table, string user, Permission permission)
: base(null)
{
Table = table;
User = user;
Permission = permission;
}
public string Table { get; private set; }
public string User { get; private set; }
public Permission Permission { get; private set; }
public override bool IsDestructiveChange
{
get { return false; }
}
}
}
Next we can write an extension method that provides a simple way to add one of these operations within a migration. You’ll notice we’re using the IDbMigration interface. This interface gives us access to APIs on DbMigration that are generally hidden when writing migrations. One such method is the ability to add a MigrationOperation – we hide this one so that folks use the nice APIs that we provide, much like the one we are writing here.
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
namespace ExtendingMigrations.Migrations
{
public static class Extensions
{
public static void GrantPermission(this DbMigration migration, string table, string user, Permission permission)
{
((IDbMigration)migration)
.AddOperation(new GrantPermissionOperation(table, user, permission));
}
}
}
Of course, you could just write the code from the extension method in your migration… but now we can write the following code which looks nicer and is consistent with the rest of the migrations API.
namespace ExtendingMigrations.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class GrantGuestPermissions : DbMigration
{
public override void Up()
{
this.GrantPermission("dbo.Blogs", "guest", Permission.Select);
}
public override void Down()
{
}
}
}
Generating SQL for our Operation
If we were to try and apply our new migration we’d get an exception because the default SQL generator doesn’t know how to process our new operation. Fortunately we can just derive from the existing migrator and add logic to generate SQL for our new operation.
Notice that we aren’t calling base.Generate if the operation isn’t one of our custom operations – the Generate(MigrationOperation) method only gets called for operations the default SQL generator doesn’t know how to process.
using System.Data.Entity.Migrations.Model;
using System.Data.Entity.Migrations.Sql;
namespace ExtendingMigrations.Migrations
{
public class MySqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(MigrationOperation migrationOperation)
{
var operation = migrationOperation as GrantPermissionOperation;
if (operation != null)
{
using (var writer = Writer())
{
writer.WriteLine(
"GRANT {0} ON {1} TO {2}",
operation.Permission.ToString().ToUpper(),
operation.Table,
operation.User);
Statement(writer);
}
}
}
}
}
And finally we register our new SQL generator in the migrations configuration class.
namespace ExtendingMigrations.Migrations
{
using System.Data.Entity.Migrations;
internal sealed class Configuration : DbMigrationsConfiguration<ExtendingMigrations.BloggingContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
// Register our custom generator
SetSqlGenerator("System.Data.SqlClient", new MySqlServerMigrationSqlGenerator());
}
}
}
Testing it Out
If we run Update-Database -Script in package manager console, migrations will give us a script of the changes it would apply to the database. We can now see migrations including SQL for our custom operation.
GRANT SELECT ON dbo.Blogs TO guest
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES ('201302272012532_GrantGuestPermissions', 'ExtendingMigrations.Migrations.Configuration', 0x1F8B0800000..., '6.0.0-alpha3-20222')
Source Code
As promised… you can get the complete Visual Studio 2012 solution here…
Read Full Post | Make a Comment ( 7 so far )Extending And Customizing Code First Models – Part 2 of 2
Here is the scenario, your company ships a library or application that contains a Code First model for accessing the database. Your customers want to extend this model to include extra types/properties to meet their specific business needs. These types/properties will be stored in additional tables/columns in the application database.
In Part 1 we looked at how to extend the model, but required the corresponding changes to be manually applied to the database. In the second (and final) part of this series we’ll take a look at how migrations can be used by the team creating the core model, and the team extending the model.
Complete source code is provided for download at the end of this post.
Make sure you have read and understand Part 1 of this series before tackling this post.
Using Migrations in the Core Model
The good news is that the developers of Customer Stalker don’t need to do anything special to use Code First Migrations. They can just use the standard workflow - as shown in this walkthrough.
For example, the Customer Stalker team have added a few properties to track the address of each customer.
using System.Collections.Generic;
namespace CustomerStalker.Core
{
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public List Complaints { get; set; }
}
}
They’ve used Migrations to apply these changes to the database, here is the resulting Migrations folder in their project.
Allowing Customers Extending the Model to Supply Migrations
To use the code from this section you are going to need EF6 because we are making use of the new Multiple Contexts per Database feature.
The Customer Stalker team is going to allow the company extending their model to provide some hand coded migrations. The first time the context is used in an application, they’ll ensure all custom migrations have been applied to the database. They’ll also make sure that any migrations the Customer Stalker team has created have also been applied.
Introducing the Extension Points
Customer Stalker start with a base class that custom migrations will derive from. This derives from DbMigration which allows companies extending the model to provide an Up and Down method.
It also implements IMigrationMetadata – something that is usually taken care of by the Migrations power shell commands. This interface is used by migrations to get metadata about ordering and the state of the model when a migration was generated (used to scaffold the next migration).
For ordering, authors of custom migrations provide a simple sequence number. This is then converted into a ordering number that looks somewhat like the numbers Migrations generates.
Unfortunately Migrations requires you to provide a target model - a limitation we’re planning to remove - but for the moment we’ll just put in the string for an empty model. Please don’t dwell on this code… best to move on before your eyes bleed too much :)
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
namespace CustomerStalker.Core.Migrations
{
public abstract class CustomizingMigration : DbMigration, IMigrationMetadata
{
private const string _emptyModel =
"H4sIAAAAAAAEAL1Y23LbNhB970z/gYOn9iGCJEdJ66GSsWW78bSyO6aT1wxErmRMcGEJ0BX7a33oJ/UXuryKF11o2e6bCKx2z1lwD47079//uB/XUjiPEBmu1ZSMBkPigPJ1wNVqSmK7fPMT+fjh++/cy0CunS9l3Ekah99UZkoerA1PKTX+A0hmBpL7kTZ6aQe+lpQFmo6Hw5/paEQBUxDM5TjuXawsl5A94ONMKx9CGzMx1wEIU6zjjpdldW6YBBMyH6Zknpzzv4hzJjjD4h6IJXHCt6efDXg20mrlhcxyJu6TEHB/yYSBAupp+LYv2uE4RUuZUtpiOq2OYksqHsjkEhnbBJlaxhVEGaUpORdMfUsXYW3r4dUXPLBF6CdurI4S4uQbKcGc/6DYudN/Yiven14bLzEW5JTYKN6Qf98L/GhMR8OMvOSrKONuiEPrTGiLSpdliq2JOsf2bhe2d8djazbtV0i2lGnEYNTvkQ4hsskdLAuY8zLhdbAVJz2cojhFRNAjg0sxrrlSZtyGqDhsG+FYEmfO1r+BWtmHKRlPJsS54msIypXijf+sOE4xlNVvYiHYQkC1fxhhC0+d3k44k9H4f4KTCUWJ5Jwrlk5GDQl+3I7k+aXxMYh9W4jhnm6cvE4zygFM626UkuZSWUoq3aGp7pyFIQKtaWyx4ni5wM7eeE9XO5nnoL7ZJ3pVJZQEtoLWbvqOBXDFI2MvmGULlvZiFshOWB/RLEu1tPP1JaghgiWK9HOOJK89SAkO8rhBNemmVPLBftVs1dsc4RV2VYKyWYOh6sUhFe7kyy5eJli0T5NmWsRSHSefu0vUZaZe4UnqupdDLhwN9PnSs9K2RaGev73Xq5BLW4fafsto5zVr3TDtWdh3h7dDquqVnrR0wy1m+LBh6wx1HkIcbMsjD9KBrg+F94eYCY58NwFzpvgSjL3X3wDNZzqFLQN4hDmjxgSih0PralIvm5aN2l6nlrdhSoKFxjskvwC+fq0m6XUl66Uc3WQXtsmLOrpOmac6ui7Opzq6QxmOcnTqkUX+A4u6nq5rCg4COGjYtlbLLNvzqzX8GNZZZJbsB8nWP75E+u2eayuhkyP49LNUXclzaf2XrHsBhq8285P+rlXgp0e+SVrGXKulLkmietTBlCHtFoNlAYrQWWT5kvkWt30wJnOdX5iIMeRSLiC4VrexDWN7ZgzIhUjqVF26v37mG5uY3dswm8uXoIAwOVKAW3UecxFUuK/yU6I9UqR6/AvgejZK6Lox3SqpMt1o1TNR0b4LCEEFeN/cgwwFJjO3ymOPsBvb4R42O+ZecIaTL02RY/P99I8Vmv6z8uE/KRLfnIsRAAA=";
string IMigrationMetadata.Id
{
get { return string.Format("{0}_{1}", SequenceNo.ToString().PadLeft(15, '0'), GetType().Name); }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return _emptyModel; }
}
public abstract int SequenceNo { get; }
}
}
Next, the Customer Stalker team is adding some hooks to the ModelCustomizer they created in Part 1. These hooks allow the company extending the model to provide an assembly and namespace that contains their custom migrations.
They’re using DbMigrator to run these migrations from code – for more info on running migrations from code see Running & Scripting Migrations from Code.
They’re also using DbContextInfo to find the provider and connection string for the core context. This allows you to get such info about a context – taking into account all the conventions, connection strings in config files, etc. - without creating an instance of it.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
using System.Reflection;
namespace CustomerStalker.Core
{
public class ModelCustomizer
{
// CODE FROM PART 1 EXCLUDED FOR BREVITY
private static Assembly _migrationsAssembly;
private static string _migrationsNamespace;
public static void RegisterMigrations(Assembly assembly, string migrationsNamespace)
{
_migrationsAssembly = assembly;
_migrationsNamespace = migrationsNamespace;
}
internal static void ApplyMigrations()
{
if (_migrationsAssembly != null && _migrationsNamespace != null)
{
var contextInfo = new DbContextInfo(typeof(CustomerStalkerContext));
var extendedMigrator = new DbMigrator(new DbMigrationsConfiguration
{
ContextKey = "CustomerStalker.Core.Migrations.Extensions",
ContextType = typeof(BlankContext),
MigrationsAssembly = _migrationsAssembly,
MigrationsNamespace = _migrationsNamespace,
TargetDatabase = new DbConnectionInfo(contextInfo.ConnectionString, contextInfo.ConnectionProviderName)
});
extendedMigrator.Update();
}
}
private class BlankContext : DbContext
{
}
}
}
Now it’s time to create database initializer that will apply all the core migrations, and any custom ones, when CustomerStalkerContext is used for the first time in an AppDomain.
You’ll notice they are catching an AutomaticMigrationsDisabledException that may be thrown when applying the core migrations. If a company extends a model then it will no longer match the model when the last migration was created by the Customer Stalker team. This is fine because they assume the customer has written their own migrations to handle the required changes.
using CustomerStalker.Core.Migrations;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
namespace CustomerStalker.Core
{
class CustomerStalkerInitializer : IDatabaseInitializer
{
public void InitializeDatabase(CustomerStalkerContext context)
{
var coreMigrator = new DbMigrator(new Configuration());
try
{
coreMigrator.Update();
}
// If the model has been extended it won't match the model recorded in the last migration
catch (AutomaticMigrationsDisabledException) { }
ModelCustomizer.ApplyMigrations();
}
}
}
Finally, they register this initializer in a static constructor for CustomerStalkerContext, ensuring it is always set before attempting to use the context in the application.
using System.Data.Entity;
namespace CustomerStalker.Core
{
internal class CustomerStalkerContext : DbContext
{
static CustomerStalkerContext()
{
Database.SetInitializer(new CustomerStalkerInitializer());
}
// EXISTING CODE EXCLUDED FOR BREVITY
}
}
Using the Extension Points
At the end of Part 1, MyBiz had extended the Customer type. These extensions required a new mybiz.Customers table with a column for the new IsVIP property and a foreign key back to the core dbo.Customers table.
Unfortunately they had to manually make these changes to the database. But now that can just add a custom migration.
using CustomerStalker.Core.Migrations;
namespace MyBiz.Migrations
{
class AddMyBizCustomer : CustomizingMigration
{
public override void Up()
{
CreateTable("mybiz.Customers",
c => new
{
CustomerId = c.Int(nullable: false),
IsVIP = c.Boolean()
})
.PrimaryKey(t => t.CustomerId);
AddForeignKey("mybiz.Customers", "CustomerId", "dbo.Customers", principalColumn: "CustomerId");
}
public override void Down()
{
DropForeignKey("dbo.MyBizCustomers", "CustomerId", "dbo.Customers");
DropTable("dbo.MyBizCustomers");
}
public override int SequenceNo { get { return 1; } }
}
}
Now they can update their test code from Part 1 to register their migrations.
ModelCustomizer.RegisterModelCustomization(
mb =>
{
mb.Entity().Property(c => c.FirstName).HasMaxLength(400);
mb.Entity().Property(c => c.LastName).HasMaxLength(400);
mb.Entity().ToTable("mybiz.Customers");
});
ModelCustomizer.RegisterTypeSubstitution<Customer, MyBizCustomer>();
ModelCustomizer.RegisterMigrations(typeof(AddMyBizCustomer).Assembly, typeof(AddMyBizCustomer).Namespace);
var service = new CustomerService();
var customer = new MyBizCustomer { FirstName = "Jane", LastName = "Doe", IsVIP = true };
service.AddCustomer(customer);
service.QuickAddCustomer("John", "Doe");
After running their test code, their changes will be automatically applied to the database. Looking in the __MigrationsHistory table they can see Migrations keeping track of which migrations (both core and custom) have been applied to the database.
Source Code
As promised… you can get the complete Visual Studio 2012 solution here…
Extending and Customizing Code First Models – Part 1 of 2
Here is the scenario, your company ships a library or application that contains a Code First model for accessing the database. Your customers want to extend this model to include extra types/properties to meet their specific business needs. These types/properties will be stored in additional tables/columns in the application database.
In this post I’ll walk through an example using a company that delivers a CRM product called Customer Stalker. The company provides a library for Customer Stalker that includes a Code First model with some hooks to allow each business that uses Customer Stalker to customize the model. We’ll see how a company called MyBiz uses these hooks to extend the model. MyBiz will use inheritance to add extra properties to existing classes/tables that are part of the Customer Stalker model.
I’m just going to show one possible way you can combine the building blocks to solve this problem. There are plenty of different ways to architect a solution and you can take the principles I show to come up with a way that suits you. This is by no means production quality or well architected code, just a rough sample… take it for what it is
We’ll build on the example as we go, adding more and more functionality. Here are the main stages I’ll cover:
- Part 1 (this post)
In Part 1 we’ll assume that Customer Stalker does *not* use Code First Migrations to create/upgrade the database and MyBiz will manually add their additional tables/columns to the database. In Part 2 we’ll enable migrations.- The Code Before Enabling Customization – Before allowing any customization of the model, we’ll take a quick look at code from the Customer Stalker library and code written by MyBiz.
- Basic Extension – We’ll start with the very basics that allow MyBiz code to provide instances of a custom derived type to the Customer Stalker library.
- Substituting Custom Derived Types – Once MyBiz has added a derived type, they probably want the Customer Stalker library to always create their derived type, rather than the original base type. This means that all data in the database will have the custom properties – not just the instances created by MyBiz code.
- Avoiding Modification of the Core Tables – Next, we’ll look at how MyBiz can add their customizations in a way that does not alter the tables that are created by the Customer Stalker library.
- Customizing the Core Tables – Finally, we’ll show how MyBiz could customize tables/columns that are part of the core Customer Stalker model.
- Part 2 (coming soon)
- Adding Code First Migrations – We’ll allow Customer Stalker to use migrations to create/upgrade the database. We’ll also provide some hooks so that MyBiz can add their own migrations to apply their customizations.
- Source Code Download – I’ll provide a Visual Studio solution that shows the complete Customer Stalker and MyBiz code bases.
The Code Before Enabling Customization
The Customer Stalker Code
Before we add any hooks here is the model that the Customer Stalker library is using. They have a couple of domain classes to track Customers and any Complaints they make.
using System;
using System.Collections.Generic;
namespace CustomerStalker.Core
{
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Complaint> Complaints { get; set; }
}
public class Complaint
{
public int ComplaintId { get; set; }
public DateTime DateRecieved { get; set; }
public string Details { get; set; }
public bool IsResovled { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
}
Customer Stalker uses a derived DbContext to turn these classes in a Code First model. They are using the Fluent API to configure the model – in this case just the maximum length of the name properties.
using System.Data.Entity;
namespace CustomerStalker.Core
{
internal class CustomerStalkerContext : DbContext
{
public DbSet Customers { get; set; }
public DbSet Complaints { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity()
.Property(c => c.FirstName)
.HasMaxLength(200);
modelBuilder.Entity()
.Property(c => c.LastName)
.HasMaxLength(200);
}
}
}
You may have noticed that that the context is internal. The Customer Stalker library exposes an API surface that restricts how businesses such as MyBiz interact with their library. Here is one of the services they expose.
namespace CustomerStalker.Core
{
public class CustomerService
{
public void AddCustomer(Customer customer)
{
using (var db = new CustomerStalkerContext())
{
db.Customers.Add(customer);
db.SaveChanges();
}
}
public Customer QuickAddCustomer(string firstName, string lastName)
{
var customer = new Customer();
customer.FirstName = firstName;
customer.LastName = lastName;
using (var db = new CustomerStalkerContext())
{
db.Customers.Add(customer);
db.SaveChanges();
}
return customer;
}
}
}
MyBiz Code
MyBiz is getting familiar with the Customer Stalker library and has written the following code to test it out.
var service = new CustomerService();
var customer = new Customer {FirstName = "Jane", LastName = "Doe" };
service.AddCustomer(customer);
service.QuickAddCustomer("John", "Doe");
Here is what the Customer Stalker database would look like at MyBiz – this is the default Customer Stalker database with no customization.
Basic Extension
Introducing the Extension Points
The Customer Stalker team is introducing a new ModelCustomizer class into their library. At the moment they just allow businesses to register some custom logic to further configure the Customer Stalker Code First model. They’ve also included an internal method to apply this customization to a model.
using System;
using System.Collections.Generic;
using System.Data.Entity;
namespace CustomerStalker.Core
{
public class ModelCustomizer
{
private static Action<DbModelBuilder> _modelCustomization;
public static void RegisterModelCustomization(Action<DbModelBuilder> modelCustomization)
{
_modelCustomization = modelCustomization;
}
internal static void ApplyCustomization(DbModelBuilder modelBuilder)
{
if (_modelCustomization != null)
{
_modelCustomization(modelBuilder);
}
}
}
}
They’ve also updated their context to apply any customization after they’ve finished configuring their model in the OnModelCreating method.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.Property(c => c.FirstName)
.HasMaxLength(200);
modelBuilder.Entity<Customer>()
.Property(c => c.LastName)
.HasMaxLength(200);
ModelCustomizer.ApplyCustomization(modelBuilder);
}
Using the Extension Points
Let’s see how MyBiz can use these extension points to store some extra information about Customers.
First up, MyBiz defines a domain class that derives from an existing class and adds any properties that MyBiz wants to track.
using CustomerStalker.Core;
namespace MyBiz.ExtendedModel
{
public class MyBizCustomer : Customer
{
public bool IsVIP { get; set; }
}
}
MyBiz also needs to modify their Customer Stalker database to include the new column - we’ll look at using Code First Migrations to do this in Part 2 (coming soon).
By default Code First uses the TPH pattern to store an inheritance hierarchy, hence the new Discriminator column in the Customers table. This allows Entity Framework to differentiate between Customers and MyBizCustomers. Later in this post we’ll take a look at how to introduce a derived type without modifying the existing table.
Now MyBiz can use the hooks provided by Customer Stalker to add their new type to the model. Then they can insert MyBizCustomers using the Customer Stalker library.
ModelCustomizer.RegisterModelCustomization(
mb =>
{
mb.Entity<MyBizCustomer>();
});
var service = new CustomerService();
var customer = new MyBizCustomer { FirstName = "Jane", LastName = "Doe", IsVIP = true };
service.AddCustomer(customer);
service.QuickAddCustomer("John", "Doe");
When MyBiz runs the above code, they’ll see the following data in the Customers table in their database.
As expected, MyBiz can use the hooks to register their own derived type with extended properties and persist it to the database.
Substituting Custom Derived Types
One limitation of our solution thus far is that QuickAddCustomer doesn’t reason about the derived MyBizCustomer type and is just creating an instance of the base Customer type. This may not be ideal, because MyBiz may want all customers to have their extended properties, including those created internally by Customer Stalker.
Introducing the Extension Points
The Customer Stalker team is adding a RegisterTypeSubstitution method which allows a business to register a derived type to be used in place of a type from the base Customer Stalker model. There is also a Create method that internal Customer Stalker code can use to create types based on what substitutions have been applied.
using System;
using System.Collections.Generic;
using System.Data.Entity;
namespace CustomerStalker.Core
{
public class ModelCustomizer
{
private static Action<DbModelBuilder> _modelCustomization;
private static Dictionary<Type, Type> _replacedTypes = new Dictionary<Type, Type>();
public static void RegisterModelCustomization(Action<DbModelBuilder> modelCustomization)
{
_modelCustomization = modelCustomization;
}
internal static void ApplyCustomization(DbModelBuilder modelBuilder)
{
if (_modelCustomization != null)
{
_modelCustomization(modelBuilder);
}
}
public static void RegisterTypeSubstitution<TOld, TNew>()
where TOld : class
where TNew : TOld
{
_replacedTypes.Add(typeof(TOld), typeof(TNew));
}
internal static TType Create<TType>()
{
if (_replacedTypes.ContainsKey(typeof(TType)))
{
return (TType)Activator.CreateInstance(_replacedTypes[typeof(TType)]);
}
return Activator.CreateInstance<TType>();
}
}
}
The QuickAddCustomer method is then updated to take type substitution into account.
public Customer QuickAddCustomer(string firstName, string lastName)
{
var customer = ModelCustomizer.Create<Customer>();
customer.FirstName = firstName;
customer.LastName = lastName;
using (var db = new CustomerStalkerContext())
{
db.Customers.Add(customer);
db.SaveChanges();
}
return customer;
}
Using the Extension Points
MyBiz adds in a call to RegisterTypeSubstitution to their test code to register the MyBizCustomer to be used in place of Customer.
ModelCustomizer.RegisterModelCustomization(
mb =>
{
mb.Entity<MyBizCustomer>();
});
ModelCustomizer.RegisterTypeSubstitution<Customer, MyBizCustomer>();
var service = new CustomerService();
var customer = new MyBizCustomer { FirstName = "Jane", LastName = "Doe", IsVIP = true };
service.AddCustomer(customer);
service.QuickAddCustomer("John", "Doe");
And now Customer Stalker is able to create instances of the custom derived type so that all instances include the extended properties.
Avoiding Modification of the Core Tables
So far, the extra properties defined on a derived type have been stored in the core table that is created by Customer Stalker. In some scenarios this may not be acceptable. For example, Customer Stalker may not provide MyBiz with support for their product if they modify the core tables.
Fortunately Code First supports the TPT inheritance mapping pattern, which allows us to store the extended properties from a derived type in a separate table. We don’t need any additional hooks in the Customer Stalker library, just some different configuration when MyBiz customizes the model.
This new configuration will put all the extended properties of MyBizCustomer in a Customers table in the mybiz schema.
ModelCustomizer.RegisterModelCustomization(
mb =>
{
mb.Entity<MyBizCustomer>().ToTable("mybiz.Customers");
});
Here is what the modified database should look like. Notice that the extended tables primary key is also a foreign key that points to the row in the core table.
When MyBiz runs their test code here is what gets inserted into the database.
Customizing the Core Tables
In addition to adding and configuring new types, the model customization hook allows MyBiz to customize the tables/columns of the core Customer Stalker model.
For example, MyBiz may operate in a part of the world where people have very long names. The maximum length of 200 that Customer Stalker configures for first and last name may not be enough. The following code allows MyBiz to override this with a maximum length of 400.
ModelCustomizer.RegisterModelCustomization(
mb =>
{
mb.Entity<Customer>().Property(c => c.FirstName).HasMaxLength(400);
mb.Entity<Customer>().Property(c => c.LastName).HasMaxLength(400);
mb.Entity<MyBizCustomer>().ToTable("mybiz.Customers");
});
Remember that MyBiz would also need to make this change to the database schema. In Part 2 (coming soon) we’ll look at allowing MyBiz to make this change with Code First Migrations.
Read Full Post | Make a Comment ( 5 so far )EF6 Code First: Configuring Unmapped Base Types
If your object model contains inheritance, Code First gives you two options for the base type – it can either be mapped or unmapped.
A mapped base type means the inheritance hierarchy is represented in the database using either the TPH, TPT or TPC pattern. An unmapped base type means Code First effectively ignores your base type and acts as if the properties defined in the base type were defined on each derived type.
Note: The code in this post is written using EF6 Alpha 2, if you are using a later release you may need to adjust the code to reflect API changes.
Prior to EF6
Prior to EF6 there was no way to configure an unmapped base type. If you wanted to configure a property defined on an unmapped base type you had to explicitly configure it on every derived type.
For example, in the following model we want all types that inherit from EntityBase to use Key as their primary key and to configure ConcurrencyToken as a Rowversion/Timestamp column.
public class EntityBase
{
public int Key { get; set; }
public byte[] ConcurrencyToken { get; set; }
}
public class Blog : EntityBase
{
public string Name { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post : EntityBase
{
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
If we were to configure EntityBase using the fluent API then it would be included in the model and Blog and Post would share a table in the database. The only alternative we are left with is to configure the properties for every type that derives from EntityBase.
public class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().HasKey(b => b.Key);
modelBuilder.Entity<Blog>().Property(b => b.ConcurrencyToken).IsRowVersion();
modelBuilder.Entity<Post>().HasKey(p => p.Key);
modelBuilder.Entity<Post>().Property(p => p.ConcurrencyToken).IsRowVersion();
}
}
This is an annoying violation of the DRY (Don’t Repeat Yourself) principle… fortunately EF6 comes heralding good news.
EF6 to the Rescue
EF6 introduces the Custom Code First Conventions feature – personally it’s my favorite new feature. I can define a convention that performs a set of configuration for every entity that derives from EntityBase in my model.
modelBuilder.Entities<EntityBase>().Configure(c =>
{
c.HasKey(e => e.Key);
c.Property(e => e.ConcurrencyToken).IsRowVersion();
});
I’ve written this convention using ‘lightweight conventions’, this part of the custom conventions feature allows you to build conventions using an API surface that looks and feels similar to the Code First Fluent API. For more information the different types of conventions you can write, check out the walkthrough and feature specification.
Read Full Post | Make a Comment ( 2 so far )EF6 Code First: Mapping All Private Properties Using Custom Conventions
A while back I blogged about mapping to private properties with Code First. The approach shown in that post works with EF4.1 onwards but it requires you to explicitly configure every private property that you want included in your model. That’s going to get tiresome if you have a big model and want all private properties to be mapped.
In EF6 we’ve introduced a new feature called Custom Code First Conventions. This allows you to define some patterns/rules about how your model should be configured. These custom conventions are added inside the OnModelCreating method on your derived context – the same place you perform configuration via the Fluent API.
Note: The code in this post is written using EF6 Alpha 2, if you are using a later release you may need to adjust the code to reflect API changes.
Without further ado… here is a convention that will include all private properties in your model by default.
public class MyContext : DbContext
{
// DbSets etc. defined here
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entities()
.Configure(c =>
{
var nonPublicProperties = c.ClrType.GetProperties(BindingFlags.NonPublic|BindingFlags.Instance);
foreach (var p in nonPublicProperties)
{
c.Property(p).HasColumnName(p.Name);
}
});
}
}
The convention runs for all entities, finds any non-public properties and then configures them to ensure they are included in the model.
To have the property included in the model I need to perform some form of configuration – I’m choosing to call HasColumnName since it’s common to all property types and I can just pass in the name of the property. In the Fluent API, simply calling Property is enough to have the property included in the model. For the final release of EF6 we may update conventions to have the same logic – in which case the HasColumnName call could be removed.
I’ve written this convention using ‘lightweight conventions’, this part of the custom conventions feature allows you to build conventions using an API surface that looks and feels similar to the Code First Fluent API. For more information the different types of conventions you can write, check out the walkthrough and feature specification.
Read Full Post | Make a Comment ( 6 so far )Code First Migrations – Customizing Scaffolded Code
A while back I blogged about customizing the SQL that is generated by Code First Migrations. In this post I’m going to step back up the pipeline a bit and look at customizing the code that gets scaffolded when you call Add-Migration.
When we built Code First Migrations we made an effort to make everything pluggable. For example, the component that scaffolds the code of a migration based on the changes detected in your model should be replaceable with your own implementation.
We also tried to factor our implementations in such a way that it was easy to override small parts of the default behavior without writing a lot of code. For example, if you want to add a small section to the start of each migration that is scaffolded, you shouldn’t have to implement a complete code generator.
Creating a Custom Code Generator
Let’s say we want to add a couple of comments to the top of each migration that records who generated the migration and when they generated it. We can derive from the default code generator and override the method that generates the start of the class. In this example I’m writing the desired information to the IndentedTextWriter and then calling the base implementation to write the remainder of the default code.
using System;
using System.Data.Entity.Migrations.Design;
using System.Data.Entity.Migrations.Utilities;
public class MyCodeGenerator : CSharpMigrationCodeGenerator
{
protected override void WriteClassStart(
string @namespace,
string className,
IndentedTextWriter writer,
string @base,
bool designer = false,
System.Collections.Generic.IEnumerable namespaces = null)
{
writer.WriteLine("// Generated Time: {0}", DateTime.Now);
writer.WriteLine("// Generated By: {0}", Environment.UserName);
writer.WriteLine();
base.WriteClassStart(@namespace, className, writer, @base, designer, namespaces);
}
}
The easiest way to see what methods you can override, and what they do, is to look at the source code for the default implementation. That’s very easy because EF is open source and you can browse the source online. Under src\EntityFramework\Migrations\Design you’ll find CSharpMigrationCodeGenerator.cs and VisualBasicMigrationCodeGenerator.cs.
Registering a Custom Code Generator
Now that we’ve built a custom code generator we can register it in the constructor of our migration configuration class.
public Configuration()
{
AutomaticMigrationsEnabled = false;
CodeGenerator = new MyCodeGenerator();
}
Now if we run the Add-Migration command we’ll see the information is added to the top of the scaffolded code.
// Generated Time: 11/30/2012 2:35:08 PM
// Generated By: rowmil
namespace ConsoleApplication1.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddRating : DbMigration
{
public override void Up()
{
AddColumn("dbo.Blogs", "Rating", c => c.Int(nullable: false));
}
public override void Down()
{
DropColumn("dbo.Blogs", "Rating");
}
}
}
Read Full Post | Make a Comment ( 2 so far )
Customizing ‘Reverse Engineer Code First’ in the EF Power Tools
Beta 2 of the Entity Framework Power Tools was recently released. By far the most popular feature out of these power tools is the ability to reverse engineer a Code First model from an existing database.
Admittedly ‘Code First’ wasn’t the best choice of names… Code First is really just a code-based alternate to the EF Designer, it supports creating a new database or mapping to an existing database. Using Code First against an existing database is often called ‘Code Second’ because… well… you have the database first and then write the code second.
I’ve included a download of the modified templates at the end of this post.
Reverse Engineer Code First
If you are already familiar with ‘Reverse Engineer Code First’ then skip ahead to the next section.
You’ll need the EF Power Tools installed from Visual Studio Gallery.
You start by right clicking on a project and selecting Entity Framework –> Reverse Engineer Code First:
Then You’re prompted for details of the database you want to use:
A derived DbContext, a set of POCO classes and a set of mapping classes will be generated in a Models folder in your project:
The Scenario
If you look in the mapping classes (those are the ones in the Mapping folder) you’ll see that ‘Reverse Engineer Code First’ uses the Fluent API for all it’s configuration.
A common request is to use Data Annotations for configuration, rather than the Fluent API. You’ll also see that it generates table\column mapping code for all classes/properties, even though Code First would correctly work most of these out without configuration.
Let’s modify the code generation so that column and table mappings are done with Data Annotations and are only generated when required.
Adding the Reverse Engineer Templates
Right click on your project and select Entity Framework –> Customize Reverse Engineer Templates:
This will add a set of T4 templates to your project. The Power Tools will use these templates to generate the code for the various classes it adds to your project.
Note: You may see the following error after you add the templates. This is safe to ignore. It’s a result of Visual Studio trying to validate the templates… but they are never run from your project.
Compiling transformation: The type or namespace name ‘EfTextTemplateHost’ could not be found (are you missing a using directive or an assembly reference?)
Removing Fluent API Table/Column Mapping
The first step is to stop generating table and column mapping in the mapping classes. This is the easy part, in Beta 2 of the Power Tools just delete line 126 thru 152 (inclusive) from the Mapping.tt file.
Adding Data Annotation Mapping
Now it’s time to edit Entity.tt to include the [Column] and [Table] attributes where required.
Let’s start by generating a [Column] attribute for any properties whose name doesn’t match the column that they map to.
This can happen when the column name contains characters that aren’t supported by C#. For example the column name may have a space, the reverse engineer process will generate a property that uses an underscore instead of a space. If we don’t supply any mapping then Code First will look for a column with an underscore instead of a space.
In Beta 2 you’ll find the property generation code around line 39 in Entity.tt, add the highlighted section from the code below.
foreach (var property in efHost.EntityType.Properties)
{
var columnName = efHost.PropertyToColumnMappings[property].Name;
if(code.Escape(property) != columnName)
{
#>
[Column("<#= columnName #>")]
<#
}
#>
<#= Accessibility.ForProperty(property) #> <#= code.Escape(property.TypeUsage) #> <#= code.Escape(property) #> { get; set; }
<#
}
Next we need to generate a [Table] attribute when the pluralized name of the class doesn’t match the table it maps to.
This can happen if the table name is not pluralized in the database. For example, if you have a table named Category then the reverse engineer process will generate a class called Category. If you don’t supply any configuration then Code First will look for a table called Categories in the database. We also need to specify configuration if the table is not in the dbo schema.
In Beta 2 you’ll find the code that generates the class definition line around line 13 in Entity.tt, add the highlighted section from the code below.
namespace <#= code.EscapeNamespace(efHost.Namespace) #>
{
<#
var tableName = (string)efHost.TableSet.MetadataProperties["Table"].Value ?? efHost.TableSet.Name;
var conventionTableName = System.Data.Entity.Design.PluralizationServices.PluralizationService
.CreateService(new CultureInfo("en"))
.Pluralize(efHost.EntityType.Name);var schemaName = (string)efHost.TableSet.MetadataProperties["Schema"].Value;
schemaName = string.IsNullOrWhiteSpace(schemaName)
? "dbo"
: schemaName;if(schemaName != "dbo" || conventionTableName != tableName)
{
#>
[Table("<#= tableName #>", Schema="<#= schemaName #>")]
<#
}#>
public class <#= efHost.EntityType.Name #>
{
<#
The final step is to modify the template to generate a using for the namespace that the Data Annotations reside in. Add the highlighted code under the existing usings (around line 10 in the Beta 2 version of Entity.tt).
The namespace for the Table/Column annotations changed after EF4.3, so we check which version we are using and generate the correct namespace.
using System;
using System.Collections.Generic;
<#
if (efHost.EntityFrameworkVersion >= new Version(4, 4))
{
#>
using System.ComponentModel.DataAnnotations.Schema;
<#
}
else
{
#>
using System.ComponentModel.DataAnnotations;
<#
}
#>
The Outcome
Now that we’ve made the edits you can re-run the reverse engineer process – make sure you save the changes to the tt files before you run it.
The table/column mapping code is now omitted from the configuration classes:
Where the mapping doesn’t line up with the Code First conventions we are now generating data annotations in the classes themselves:
But if everything lines up then we don’t generate any mapping at all:
Conclusion
Here are the templates if you want to download them. It would be pretty simple to move specification of primary key etc. to use data annotations as well. You are welcome to edit and redistribute the templates as you see fit ![]()
Dynamically Building a Model with Code First
I’ve answered a few emails recently on this topic so it felt like time to turn it into a blog post.
In this scenario the set of classes that make up your model isn’t known at compile time and is discovered dynamically at runtime. An example of such a scenario is a WordPress/Orchard/etc. style website that allows modules to be plugged in. These modules live in a separate assembly and may contain classes that represent data to be persisted in the application database. These classes are all pulled into a central Code First model to be used for data access.
Finding the Classes
There are lots of different approaches for identifying the classes to be included in the model. For this example lets use a [Persistent] attribute, something like this:
[AttributeUsage(AttributeTargets.Class)] public class PersistentAttribute : Attribute { }
Now we can add some logic to the OnModelCreating method of our context to scan assemblies and add any classes with the [Persist] attribute. We’re going to assume that the assemblies that may contain classes are all loaded into the current AppDomain, of course you may have some other mechanism that provides a list of the assemblies to check.
public class MyContext : DbContext { protected override void OnModelCreating(DbModelBuilder modelBuilder) { var entityMethod = typeof(DbModelBuilder).GetMethod("Entity"); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { var entityTypes = assembly .GetTypes() .Where(t => t.GetCustomAttributes(typeof(PersistentAttribute), inherit: true) .Any()); foreach (var type in entityTypes) { entityMethod.MakeGenericMethod(type) .Invoke(modelBuilder, new object[] { }); } } } }
In this example the entire model is dynamically discovered, but you could also have some static parts of the model that are registered with modelBuilder too.
An Alternative Using EntityConfiguration<TEntity>
Code First allows you to create a class that derives from EntityTypeConfiguration<TEntity> to encapsulate the Fluent API configuration for an entity. If your using this approach to configuration you can just look for these configuration classes and register them, instead of finding the entities to register. Notice that we are filtering out the EntityFramework assembly since it has some configuration classes that it uses internally.
public class MyContext : DbContext { protected override void OnModelCreating(DbModelBuilder modelBuilder) { var addMethod = typeof(ConfigurationRegistrar) .GetMethods() .Single(m => m.Name == "Add" && m.GetGenericArguments().Any(a => a.Name == "TEntityType")); foreach (var assembly in AppDomain.CurrentDomain .GetAssemblies() .Where(a => a.GetName().Name != "EntityFramework")) { var configTypes = assembly .GetTypes() .Where(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); foreach (var type in configTypes) { var entityType = type.BaseType.GetGenericArguments().Single(); var entityConfig = assembly.CreateInstance(type.FullName); addMethod.MakeGenericMethod(entityType) .Invoke(modelBuilder.Configurations, new object[] { entityConfig }); } } } }
What if the Model Changes?
Code First Migrations to the rescue… You may not be able to use Migrations from the Package Manager Console because the logic to discover your model may require you full application to be running. Fortunately those commands are just thin wrappers over an API. Here is some code to automatically change the database when new classes or properties are added to the model. My recent blog post has more details on invoking Migrations from code.
var config = new DbMigrationsConfiguration<MyContext> { AutomaticMigrationsEnabled = true }; var migrator = new DbMigrator(config); migrator.Update();Read Full Post | Make a Comment ( 8 so far )
Testing With a Fake DbContext
One of the most popular posts on my blog has been “EF CTP4 Tips & Tricks: Testing With Fake DbContext”. That post was built on a pre-release version of Entity Framework so I thought I’d provide an updated post on top of the DbContext API surface we ended up shipping.
In this post we are going to take a look at how to replace a DbContext based data access layer with an in-memory fake for unit testing.
I’m going to use an MVC controller as an example but the same approach can be used for any component that consumes a derived DbContext, including repositories.
The Problem
Say we have a very simple model for Employees and Departments and we are using DbContext to persist and query our data:
public class EmployeeContext : DbContext { public DbSet<Department> Departments { get; set; } public DbSet<Employee> Employees { get; set; } } public class Department { public int DepartmentId { get; set; } public string Name { get; set; } public ICollection<Employee> Employees { get; set; } } public class Employee { public int EmployeeId { get; set; } public int FirstName { get; set; } public int LastName { get; set; } public int Position { get; set; } public Department Department { get; set; } }
In an MVC application we could add a controller that can display all departments in alphabetical order:
(Typically we’d also implement ‘detail’, ‘create’, ‘edit’ and ‘delete’ functionality but I’m leaving them out because ‘list’ is enough to demonstrate fakes)
public class DepartmentController : Controller { private EmployeeContext db = new EmployeeContext(); public ViewResult Index() { return View(db.Departments.OrderBy(d => d.Name).ToList()); } protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); } }
The issue is that our controller now has a hard dependency on EF because it needs an EmployeeContext which derives from DbContext and exposes DbSets. We have no way to replace this with any other implementation and we are forced to run any unit tests against a real database, which means they really aren’t unit tests at all.
Adding an Interface
DbSet<T> happens to implement IDbSet<T> so we can pretty easily create an interface that our derived context implements:
public interface IEmployeeContext { IDbSet<Department> Departments { get; } IDbSet<Employee> Employees { get; } int SaveChanges(); } public class EmployeeContext : DbContext, IEmployeeContext { public IDbSet<Department> Departments { get; set; } public IDbSet<Employee> Employees { get; set; } }
Now we can update our controller to be based on this interface rather than the EF specific implementation.
(You’ll notice I’m still using the EF implementation in the default constructor, in a real world app you would probably just have the interface based constructor and use dependency injection to supply the EF implementation at runtime, but I just wanted to keep things simple for this post.)
public class DepartmentController : Controller { private IEmployeeContext db; public DepartmentController() { this.db = new EmployeeContext(); } public DepartmentController(IEmployeeContext context) { this.db = context; } public ViewResult Index() { return View(db.Departments.OrderBy(d => d.Name).ToList()); } protected override void Dispose(bool disposing) { if (db is IDisposable) { ((IDisposable)db).Dispose(); } base.Dispose(disposing); } }
Building Fakes
The first thing we need is a fake implementation of IDbSet<TEntity>, this is pretty easy to implement.
public class FakeDbSet<T> : IDbSet<T> where T : class { ObservableCollection<T> _data; IQueryable _query; public FakeDbSet() { _data = new ObservableCollection<T>(); _query = _data.AsQueryable(); } public virtual T Find(params object[] keyValues) { throw new NotImplementedException("Derive from FakeDbSet<T> and override Find"); } public T Add(T item) { _data.Add(item); return item; } public T Remove(T item) { _data.Remove(item); return item; } public T Attach(T item) { _data.Add(item); return item; } public T Detach(T item) { _data.Remove(item); return item; } public T Create() { return Activator.CreateInstance<T>(); } public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T { return Activator.CreateInstance<TDerivedEntity>(); } public ObservableCollection<T> Local { get { return _data; } } Type IQueryable.ElementType { get { return _query.ElementType; } } System.Linq.Expressions.Expression IQueryable.Expression { get { return _query.Expression; } } IQueryProvider IQueryable.Provider { get { return _query.Provider; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _data.GetEnumerator(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return _data.GetEnumerator(); } }
There isn’t really a good way to generically implement Find, so I’ve left it as a virtual method that throws if called. If our application makes use of the Find method we can create an implementation specific to each type.
public class FakeDepartmentSet : FakeDbSet<Department> { public override Department Find(params object[] keyValues) { return this.SingleOrDefault(d => d.DepartmentId == (int)keyValues.Single()); } } public class FakeEmployeeSet : FakeDbSet<Employee> { public override Employee Find(params object[] keyValues) { return this.SingleOrDefault(e => e.EmployeeId == (int)keyValues.Single()); } }
Now we can create a fake implementation of our context:
public class FakeEmployeeContext : IEmployeeContext { public FakeEmployeeContext() { this.Departments = new FakeDepartmentSet(); this.Employees = new FakeEmployeeSet(); } public IDbSet<Department> Departments { get; private set; } public IDbSet<Employee> Employees { get; private set; } public int SaveChanges() { return 0; } }
Testing Against Fakes
Now that we have our fakes defined we can use them to write a unit test for our controller, that doesn’t use EF:
[TestMethod] public void IndexOrdersByName() { var context = new FakeEmployeeContext { Departments = { new Department { Name = "BBB"}, new Department { Name = "AAA"}, new Department { Name = "ZZZ"}, } }; var controller = new DepartmentController(context); var result = controller.Index(); Assert.IsInstanceOfType(result.ViewData.Model, typeof(IEnumerable<Department>));
var departments = (IEnumerable<Department>)result.ViewData.Model; Assert.AreEqual("AAA", departments.ElementAt(0).Name); Assert.AreEqual("BBB", departments.ElementAt(1).Name); Assert.AreEqual("ZZZ", departments.ElementAt(2).Name); }
Summary
In this post we saw how to build an interface that represents our context and how to build an in-memory fake of that context for use in our unit tests. We used an MVC controller to demonstrate this but the same approach can be used with any component that needs to interact with an EF based context, including repositories. There are a number of reasons to use in-memory fakes for unit testing but some key benefits are stable and robust tests that execute quickly and exercise a single component, making failures easy to isolate.
Read Full Post | Make a Comment ( 16 so far )Running & Scripting Migrations from Code
Code First Migrations is included as part of Entity Framework starting with the EF 4.3 release.
Migrations are normally created and run from the Package Manager Console in Visual Studio. These commands are just thin wrappers over public APIs that you can call directly from your own code. In this post you’ll see how to kick-off the migrations process from code. You’ll also see how to get a SQL script that represents the changes from a set of migrations.
I’m going to assume you have a working knowledge of Code First Migrations… if you don’t, take a minute to read the Code-Based Migrations Walkthrough.
Running Migrations from Code
Running migrations is actually very simple. You need to instantiate an instance on the Configuration class that was added to your project when you enabled migrations, this is then used to construct a DbMigrator. Calling the Update method will then update the target database to the latest migration. There is also an overload that allows you to specify a target migration to upgrade/downgrade to.
var configuration = new Configuration(); var migrator = new DbMigrator(configuration); migrator.Update();
The above code will update that database that your context targets. You can override the database that will be migrated by setting the TargetDatabase property on the configuration.
var configuration = new Configuration(); configuration.TargetDatabase = new DbConnectionInfo( "Server=MyServer;Database=MyDatabase;Trusted_Connection=True;", "System.Data.SqlClient"); var migrator = new DbMigrator(configuration); migrator.Update();
Scripting Migrations from Code
Scripting migrations is very simiar, except you wrap the DbMigrator in a MigratorScriptingDecorator. You can then use the ScriptUpdate method to get the resulting SQL script as a string. The source and target migrations allow you to get a script between any two given migrations. If you supply null an empty database is used for the source migration and the latest migration is used for the target.
var configuration = new Configuration(); var migrator = new DbMigrator(configuration); var scriptor = new MigratorScriptingDecorator(migrator); string script = scriptor.ScriptUpdate(sourceMigration: null, targetMigration: null);
Summary
Our team has been careful to ensure that all the operations available as Power Shell commands can be easily performed from your code. This post showed how to use code to perform the operations available via the Update-Database command.
Read Full Post | Make a Comment ( 25 so far )Customizing Code First Migrations Provider
Code First Migrations is included as part of Entity Framework starting with the EF 4.3 release.
One feature we haven’t really blogged about yet is the ability to include additional arguments in a migration operation. These arguments are then made available to the SQL generator. You can then derive from the SQL generators that we ship, and customize the generated SQL based on the arguments you specify.
I’m going to assume you have a working knowledge of Code First Migrations… if you don’t, take a minute to read the Code-Based Migrations Walkthrough.
The Scenario
Code First Migrations allows you to add additional indexes to your database from within a code-based migration. These indexes are always ascending though… and you may find yourself wanting to add descending indexes. In this post we’ll look at extending the existing SQL Server generator to handle this scenario.
Specifying Additional Arguments
All the migration operation APIs, that are exposed in a code-based migration, include an optional anonymousArguments parameter. This parameter allows you to specify some additional arguments that should be stored in a property bag on the operation.
The additional arguments are specified using anonymous type syntax. The values are converted into a Dictionary<string, object> that is made available during SQL generation.
To specify the sort order of an index we are going to use the SortOrder key. When we create an index we’ll assign either “Ascending” or “Descending”. We’ll write the SQL generator so that it assumes an ascending index if the SortOrder isn’t specified.
Here is an example of the code we could write in a code-based migration:
CreateIndex(table: "Blogs", column: "Title", anonymousArguments: new { SortOrder = "Descending" });
Customizing the SQL Generator
Now it’s time to customize the SQL generator for SQL Server, so that it reacts to our custom argument. We can create our own generator that derives from the existing SqlServerMigrationsSqlGenerator. The generator is structured so that it’s easy for you to override just the operations you want to customize the SQL for. In our case we just want to change the SQL that is generated for a CreateIndexOperation .
using System.Data.Entity.Migrations.Model; using System.Data.Entity.Migrations.Sql; using System.Linq; namespace CustomMigrationsProviders { public class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator { protected override void Generate(CreateIndexOperation createIndexOperation) { using (var writer = Writer()) { writer.Write("CREATE "); if (createIndexOperation.IsUnique) { writer.Write("UNIQUE "); } writer.Write("INDEX "); writer.Write(Quote(createIndexOperation.Name)); writer.Write(" ON "); writer.Write(Name(createIndexOperation.Table)); writer.Write("("); // Calculate sort order object sortOrder; createIndexOperation.AnonymousArguments.TryGetValue("SortOrder", out sortOrder); var sortOrderSql = sortOrder != null && sortOrder.ToString() == "Descending" ? "DESC" : "ASC"; // Specify columns, including sort order writer.Write(string.Join(",", createIndexOperation.Columns.Select(c => Quote(c) + " " + sortOrderSql))); writer.Write(")"); Statement(writer); } } } }
The code inside the overridden Generate method if basically a copy/paste of the code in the base provider. The only customization is to the code that generates the columns to be included in the index. The customized code reads the anonymous arguments on the CreateIndexOperation and calculates the SQL operator to specify the sort order. This operator is then added to each column in the generated SQL.
Here is an example of the SQL that will be generated:
CREATE INDEX [IX_Title] ON [Blogs]([Title] DESC)
NOTE: You may notice that the code makes use of the Writer method to get a text writer to use for building the SQL. This isn’t mandatory, you can use any approach you want to create the SQL. The SQL you generate must be registered with the Statement method to ensure it gets executed.
Registering the Customized Provider
Now that you have a custom provider it’s time to let Migrations know to use your new provider, instead of the default, when running against SQL Server. You do this in the constructor of your migrations configuration class, using the SetSqlGenerator method. SQL generators are registered based on the provider invariant name of the database provider they generate SQL for, for SQL Server this is “System.Data.SqlClient”.
public Configuration() { AutomaticMigrationsEnabled = false; SetSqlGenerator("System.Data.SqlClient", new CustomMigrationsProviders.CustomSqlServerMigrationSqlGenerator()); }
Summary
In this post you saw how to add additional arguments to a migration operation using the anonymousArguments parameter. You then saw how to customize the existing SQL Server generator to generate different SQL based on these arguments. Finally you saw how to register your custom generator so that Code First Migrations will use it in place of the default SQL Server generator.
Read Full Post | Make a Comment ( 7 so far )Magic Free Code First Migrations
This post is now out of date.
Be sure to check out EF 4.3 Beta 1.
<transparency>I work for Microsoft</transparency> (just kidding… but I couldn’t leave the tag open)
We recently released a very early preview of Code First Migrations and it’s been getting some mixed feedback, the negative feedback falls into three buckets:
- Stuff we have on the list but just haven’t done yet (i.e. Provider model, upgrade to specific named version, downgrade, outside of Visual Studio experience)
- Things you want us to do differently (i.e. Get rid of those SQL scripts… I’m a developer not a DBA)
- Functionality we did a bad job of explaining in the original posts (i.e. You don’t have to use the auto-magic behavior… you can have a script for every step if you want)
This post is going to walkthrough what it looks like to use migrations without the magic, a.k.a “Rails style migrations”. Code First Migrations also doesn’t lock you into one approach so you could always decide that you trust the magic in the future, or you can trust the magic for certain migrations.
One HUGE CAVEAT on this post is that all the scripts are going to be in SQL… one overwhelming bit of feedback we’ve heard is ‘get rid of the SQL… give me code’. You’re probably scratching you head asking ‘what exactly where those guys thinking when they used SQL scripts?’… we were thinking that folks would be more comfortable with the magic and that adding ‘custom scripts’ was going to be reserved for complex migration steps when you would likely need to drop to SQL anyway. Looks like we got that wrong (but that’s exactly why we ship CTPs). The plans we have around a provider model would make it very easy to swap in some other means of expressing the non-magical migrations… based on what we are hearing it looks we should be using code instead.
Getting a Model
- Create a new console application and install the EntityFramework.SqlMigrations package from ‘Package Manager Console’, this will also install the EntityFramework package:
PM> Install-Package EntityFramework.SqlMigrations
- Lets build a simple console app with a Code First model. Note that I’m also getting rid of other Code First magic by switching off database initializers and getting rid of the EdmMetadata table:
using System; using System.Data.Entity; using System.Data.Entity.Infrastructure; namespace NoMagic { class Program { static void Main(string[] args) { // Don't do any database creation magic Database.SetInitializer<MyContext>(null); using (var db = new MyContext()) { foreach (var b in db.Books) { Console.WriteLine(b.Name); } } } } public class MyContext : DbContext { public DbSet<Book> Books { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // No EdmMetadata table please modelBuilder.Conventions.Remove<IncludeMetadataConvention>(); } } public class Book { public int BookId { get; set; } public string Name { get; set; } } }
First Migration
- We’re not using any magic, so we want a script that will handle database creation, we use the Add-CustomScript command for that:
PM> Add-CustomScript –Name:”InitialCreate”
Scaffolding custom script by comparing current model to database.
Added custom script: Migrations\20110730045135_InitialCreate
This adds a Migrations folder to our project with a sub-folder representing our script. The folder contains our script and also a Target.xml file… the Target.xml file is there to facilitate downgrade with automatic migrations… it’s always there in the CTP but in the next release we’ll only leave Target.xml around when you are actually using automatic migrations.
The script is scaffolded with what migrations would have done if you wanted it to run automatically… I won’t cover the SQL vs Code point again here, see the top. I could edit the script at this point if I don’t like what it is doing. The script is quite verbose too… I’m just showing the bit that actually creates our table (Beware: Don’t try and remove all that SET <blah, blah, blah> stuff from the script… did I mention this is a super early preview). There is also a create statement for the __ModelSnapshot table which is how the database knows what version it is at etc.
CREATE TABLE [dbo].[Books] ( [BookId] INT IDENTITY (1, 1) NOT NULL, [Name] NVARCHAR (MAX) NULL, PRIMARY KEY CLUSTERED ([BookId] ASC) );
- Now we can use the Update-Database command to run the script
PM> Update-Database 1 pending custom scripts found. Processing custom script [20110730045135_InitialCreate]. - Executing custom script [20110730045135_InitialCreate]. - Creating NoMagic.MyContext... - Creating [dbo].[Books]... - Update complete. Ensuring database matches current model. - Database already matches model.Update-Database : Cannot open database "NoMagic.MyContext" requested by the login. The login failed. Login failed for user 'REDMOND\rowmil'. At line:1 char:16 + Update-Database <<<< + CategoryInfo : NotSpecified: (:) [Update-Database], SqlException + FullyQualifiedErrorId : System.Data.SqlClient.SqlException,System.Data.Entity.Migrations.Commands.MigrateCommand
Ok, so the error is an annoying bug… but everything actually worked ok. It errored out during the automatic upgrade process… which wasn’t going to do anything because we aren’t using the magic. You’ll get this error when you run the first migration against a database that doesn’t exist. Migrations will actually create the empty database for you… is that more magic that you should be able to switch off?
One thing that occurred to me while writing this post is that it would be great to have a way to prevent the automatic pipeline from ever kicking in just incase I accidently let it do something. Sounds like we need to have some settings defined in an xml config file your code.
That’s pretty much it, we just repeat that process indefinitely.
Second Migration
- Let’s rename the Book.Name property to Title:
public class Book { public int BookId { get; set; } public string Title { get; set; } }
- We could just let migrations scaffold the script with a drop/add column and then manually edit the script… but let’s tell it to take the rename into account while scaffolding:
PM> Add-CustomScript -Name:"RenameBookName" -Renames:"Book.Name=>Book.Title" Scaffolding custom script by comparing current model to database. Added custom script: Migrations\20110730051326_RenameBookName
You’ll notice that migrations has added a Model.refactorlog file to your project… Model.refactorlog shouldn’t be there since we scaffolded a script rather than doing an automatic upgrade… another CTP thing that we’ll fix.
Looking at the scaffolded script we see that it contains a rename rather than a drop create:
EXECUTE sp_rename @objname = N'[dbo].[Books].[Name]', @newname = N'Title', @objtype = N'COLUMN';
- Now we can use Update-Database to run our script:
PM> Update-Database 1 pending custom scripts found. Processing custom script [20110730051326_RenameBookName]. - Executing custom script [20110730051326_RenameBookName]. - Rename [dbo].[Books].[Name] to Title - Caution: Changing any part of an object name could break scripts and stored procedures. - Update complete. Ensuring database matches current model. - Database already matches model.
Conclusion
Code First Migrations supports automatic upgrade, custom scripts or a combination of both. We use SQL for the custom scripts at the moment but you are telling us they need to be code based. This is an early CTP which means it is full of rough edges… but that also means we are early enough in the release cycle to change things ![]()
EF 4.1 Multi-Tenant with Code First
After a long break while we got EF 4.1 (Magic Unicorn Edition) polished up and shipped it felt like time to get some content up on here again. We’ve released some good introductory content for EF 4.1 on the MSDN Data Developer Center so I thought I’d dive into something a little more advanced.
The default Code First workflow that is seen in most walkthroughs gently encourages you to write your model configuration code in a way that takes advantage of inbuilt model caching. This is a good thing because the pipeline that processes conventions, data annotations and fluent API calls is pretty expensive. The model for a derived context type is cached at the AppDomain level and is built on the assumption that the context is always used to access the same set of tables. This is fine for most folks but if you have a multi-tenant database then you may have code executing in a single AppDomain that wants to use the same Code First context to access different groups of tables representing multiple instances of the same model.
The Model
The model we will be working with is very simple:
public class Person { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<ContactInfo> ContactInfo { get; set; } } public class ContactInfo { public int Id { get; set; } public string Details { get; set; } public int PersonId { get; set; } public virtual Person Person { get; set; } }
We are going to use different database schemas to distinguish each tenant. An example database with two tenants looks something like this:
The API
The public surface of our API is shown below, we have a derived context and two static methods that allow us to provision a new tenant and create a context to access a given tenant.
public class ContextContext : DbContext { public DbSet<Person> People { get; set; } public DbSet<ContactInfo> ContactInfo { get; set; } /// <summary> /// Creates a context that will access the specified tenant /// </summary> public static ContextContext Create(string tenantSchema, DbConnection connection) { // TODO: Implement } /// <summary> /// Creates the database and/or tables for a new tenant /// </summary> public static void ProvisionTenent(string tenantSchema, DbConnection connection) { // TODO: Implement
} }
The Implementation
Here is a complete code listing of our context, the implementation is explained in the next section.
public class ContactContext : DbContext { private ContactContext(DbConnection connection, DbCompiledModel model) : base(connection, model, contextOwnsConnection: false) { } public DbSet<Person> People { get; set; } public DbSet<ContactInfo> ContactInfo { get; set; } private static ConcurrentDictionary<Tuple<string, string>, DbCompiledModel> modelCache= new ConcurrentDictionary<Tuple<string, string>, DbCompiledModel>(); /// <summary> /// Creates a context that will access the specified tenant /// </summary> public static ContactContext Create(string tenantSchema, DbConnection connection) { var compiledModel = modelCache.GetOrAdd( Tuple.Create(connection.ConnectionString, tenantSchema), t => { var builder = new DbModelBuilder(); builder.Conventions.Remove<IncludeMetadataConvention>(); builder.Entity<Person>().ToTable("Person", tenantSchema); builder.Entity<ContactInfo>().ToTable("ContactInfo", tenantSchema); var model = builder.Build(connection); return model.Compile(); }); return new ContactContext(connection, compiledModel); } /// <summary> /// Creates the database and/or tables for a new tenant /// </summary> public static void ProvisionTenant(string tenantSchema, DbConnection connection) { using (var ctx = Create(tenantSchema, connection)) { if (!ctx.Database.Exists()) { ctx.Database.Create(); } else { var createScript = ((IObjectContextAdapter)ctx).ObjectContext.CreateDatabaseScript(); ctx.Database.ExecuteSqlCommand(createScript); } } } }
The Explanation
What exactly is that code doing?
- Constructor
- We’ve made the constructor private to force consumers to use the Create factory method.
- We are using the base constructor that accepts a compiled model and connection, this bypasses the usual OnModelCreating workflow and forces the context to run with the specified model and connection.
- The contextOwnsConnection flag indicates whether or not the connection should be disposed when the context is disposed. You’ll see in the sample usage that I used the same connection instance to access multiple tenants so I don’t want this behavior.
- Static modelCache Field
- We already know that model creation is expensive so don’t want to recreate the model for every context instance. This field will be used to cache models based on the connection string and tenant schema.
- Obviously the connection string based caching is a little flaky, feel free to substitute Server/Database name or whatever works in your scenario.
- Static Create Method
- This method houses the primary logic that allows us to target multiple tenants.
- We check to see if there is a model already created to access the tenant on this connection string. If not we use the DbModelBuilder –> DbModel –> DbCompiledModel workflow to create a model. This workflow is usually handled internally by DbContext.
- If you look at the API on DbModel you may be wondering what you can achieve by us having separated DbModel and DbCompiledModel… the answer in EF 4.1 is ‘absolutely nothing’! However, the longer term plan is to have DbModel be a mutable object model giving you much finer grain control once you are done with DbModelBuilder.
- You’ll notice I am removing ‘IncludeMetadataConvention’ from the conventions, this removes the EdmMetadata table that Code First includes by default.
- Provider information is required to build the final model because the database part will vary depending on the database provider being used. There is an overload of DbModel.Build that will accept raw provider information but we are using a handy overload that will work out these things based on the DbConnection.
- You’ll notice (probably with some dismay) we need to write code to configure the table schema for each entity. Admittedly there aren’t many magic unicorns grazing around this code… in future versions of EF we will be able to replace this with a much cleaner custom convention.
- Static ProvisionTenant Method
- This is pretty self explanatory, if the database doesn’t exist then we create it along with the schema for the new tenant. If the database does exist then we pull out the create script and run it against the existing database.
The Usage
Does it work? Of course…
using (var connection = new SqlConnection(@"Server=.\SQLEXPRESS;Database=Contacts;Trusted_Connection=True;")) { ContactContext.ProvisionTenant("personal", connection); ContactContext.ProvisionTenant("work", connection); using (var ctx = ContactContext.Create("personal", connection)) { ctx.People.Add(new Person { Name = "Rowan Miller" }); ctx.SaveChanges(); } }
The Problem
Database schemas work fine as a way to differentiate each tenant. Another common practice is to use table prefixes and this hits a bit of an issue. Code First generates names for things such as foreign key constraints and there is no way to change these names. The generated names are often based on the entity type names and you end up with conflicts between two tenants. Unfortunately the only solution at this stage is to manipulate the generated sql script before executing it in the ProvisionTenant method… admittedly that is a pretty ugly solution.
The Summary
You can use Code First to access a multi-tenant database that uses database schemas to differentiate between each tenant. Table prefixes are possible but requires some ugly code.
Read Full Post | Make a Comment ( 17 so far )EF CTP4 Tips & Tricks: Testing With Fake DbContext
This post is based on a pre-release version of Entity Framework.
An updated post is available here.
This is the sixth in a series of posts about the recently released Entity Framework Feature CTP4, now affectionately known as “EF Magic Unicorn Edition”.
In this post we are going to take a look at how to replace a DbContext based data access layer with an in-memory fake for unit testing.
I’m going to use an MVC controller as an example but the same approach can be used for any component that consumes a derived DbContext, including repositories.
The Problem
Say we have a very simple model for Employees and Departments and we are using DbContext in Code First mode to persist and query our data:
public class EmployeeContext : DbContext
{
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int EmployeeId { get; set; }
public int FirstName { get; set; }
public int LastName { get; set; }
public int Position { get; set; }
public Department Department { get; set; }
}
In an MVC application we could add a controller that can display all departments in alphabetical order and allow us to add new departments:
(Typically we’d also implement ‘detail’, ‘edit’ and ‘delete’ functionality but I’m leaving them out because ‘list’ and ‘create’ are enough to demonstrate fakes)
public class DepartmentController : Controller
{
private EmployeeContext context;
public DepartmentController()
{
this.context = new EmployeeContext();
}
public ViewResult Index()
{
return View(this.context.Departments.OrderBy(d => d.Name).ToList());
}
public ViewResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Department dep)
{
context.Departments.Add(dep);
context.SaveChanges();
return RedirectToAction("Index");
}
}
The issue is that our controller now has a hard dependency on EF because it needs an EmployeeContext which derives from DbContext and exposes DbSets. We have no way to replace this with any other implementation and we are forced to run any unit tests against a real database, which means they really aren’t unit tests at all.
Adding an Interface
DbSet<T> happens to implement IDbSet<T> so we can pretty easily create an interface that our derived context implements:
public interface IEmployeeContext
{
IDbSet<Department> Departments { get; }
IDbSet<Employee> Employees { get; }
int SaveChanges();
}
public class EmployeeContext : DbContext, IEmployeeContext
{
public IDbSet<Department> Departments { get; set; }
public IDbSet<Employee> Employees { get; set; }
}
Now we can update our controller to be based on this interface rather than the EF specific implementation. You’ll notice I’m still using the EF implementation in the default constructor, in a real world app you would probably just have the interface based constructor and use dependency injection to supply the EF implementation at runtime, but I just wanted to keep things simple for this post
public class DepartmentController : Controller
{
private IEmployeeContext context;
public DepartmentController()
{
this.context = new EmployeeContext();
}
public DepartmentController(IEmployeeContext context)
{
this.context = context;
}
public ViewResult Index()
{
return View(this.context.Departments.OrderBy(d => d.Name).ToList());
}
public ViewResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Department dep)
{
context.Departments.Add(dep);
context.SaveChanges();
return RedirectToAction("Index");
}
}
Building Fakes
The first thing we need is a fake implementation of IDbSet<TEntity>, this is pretty easy to implement.
public class FakeDbSet<T> : IDbSet<T>
where T : class
{
HashSet<T> _data;
IQueryable _query;
public FakeDbSet()
{
_data = new HashSet<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public void Add(T item)
{
_data.Add(item);
}
public void Remove(T item)
{
_data.Remove(item);
}
public void Attach(T item)
{
_data.Add(item);
}
public void Detach(T item)
{
_data.Remove(item);
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
The one thing you’ll notice is that there isn’t really a good way to generically implement Find so I’ve left it as a virtual method that throws if called. If our application makes use of the Find method we can create an implementation specific to each type:
public class FakeDepartmentSet : FakeDbSet<Department>
{
public override Department Find(params object[] keyValues)
{
return this.SingleOrDefault(d => d.DepartmentId == (int)keyValues.Single());
}
}
public class FakeEmployeeSet : FakeDbSet<Employee>
{
public override Employee Find(params object[] keyValues)
{
return this.SingleOrDefault(e => e.EmployeeId == (int)keyValues.Single());
}
}
Now we can create a fake implementation of our context:
public class FakeEmployeeContext : IEmployeeContext
{
public FakeEmployeeContext()
{
this.Departments = new FakeDepartmentSet();
this.Employees = new FakeEmployeeSet();
}
public IDbSet<Department> Departments { get; private set; }
public IDbSet<Employee> Employees { get; private set; }
public int SaveChanges()
{ return 0; }
}
Testing Against Fakes
Now that we have our fakes defined we can use them to write a unit test for our controller, that doesn’t use EF:
[TestMethod]
public void IndexOrdersByName()
{
var context = new FakeEmployeeContext
{
Departments =
{
new Department { Name = "BBB"},
new Department { Name = "AAA"},
new Department { Name = "ZZZ"},
}
};
var controller = new DepartmentController(context);
var result = controller.Index();
Assert.IsInstanceOfType(result.ViewData.Model, typeof(IEnumerable<Department>));
var departments = (IEnumerable<Department>)result.ViewData.Model;
Assert.AreEqual("AAA", departments.ElementAt(0).Name);
Assert.AreEqual("BBB", departments.ElementAt(1).Name);
Assert.AreEqual("ZZZ", departments.ElementAt(2).Name);
}
Summary
In this post we saw how to build an interface that represents our context and how to build an in-memory fake of that context for use in our unit tests. We used an MVC controller to demonstrate this but the same approach can be used with any component that needs to interact with an EF based context, including repositories. There are a number of reasons to use in-memory fakes for unit testing but some key benefits are stable and robust tests that execute quickly and exercise a single component, making failures easy to isolate.
Read Full Post | Make a Comment ( 33 so far )EF CTP4 Tips & Tricks: Querying Navigations Without Loading
This is the fifth in a series of posts about the recently released Entity Framework Feature CTP4, now affectionately known as “EF Magic Unicorn Edition”.
Today we are going to take a look at how to query the contents of the navigation property of a loaded entity without loading the contents of that navigation into memory. For folks who are familiar with the default code generation in EF, you may have used the CreateSourceQuery method on EntityReference and EntityCollection to achieve this functionality.
The Problem
In the following console app we have a pretty simple model defined and we’ve made the navigation properties virtual so that we get lazy loading. There is however some room for improvement in our Main method, first off we are pulling an entire Category object back into memory just to display its name. Secondly we are pulling all the products of the Food category into memory just to work out how many there are, not only does this result in more memory usage and more data being transferred from the database but we are also issuing a SELECT * FROM dbo.Categories when we really only need to query for the count!
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace CreateNavigationQuerySample
{
class Program
{
static void Main(string[] args)
{
using (var ctx = new ProductCatalog())
{
var prod = ctx.Products.First();
Console.WriteLine("{0} belongs to the {1} category.", prod.Name, prod.Category.Name);
var food = ctx.Categories.Find("FOOD");
Console.WriteLine("There are {0} food products.", food.Products.Count());
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
public class ProductCatalog : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class Category
{
public string CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public virtual Category Category { get; set; }
}
}
The Solution
What we really want is a way to get a query that represents the contents of a navigation property for a given entity, this is possible by making use of the underlying ObjectContext from our ProductCatalog. We are going to make use of the RelationshipManager from the ObjectContext to achieve the mission at hand. The API for doing this is pretty undiscoverable and makes use of a lot of strings so lets wrap it up in a nice method and replace those ugly strings with a nice strongly typed lambda. You’ll notice I have defined two overloads to deal with reference and collection navigations. Here is our updated context:
public class ProductCatalog : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
public IQueryable<TResult> CreateNavigationQuery<TSource, TResult>(TSource entity, Expression<Func<TSource, TResult>> navigationPath)
{
return CreateNavigationQuery<TResult>(entity, (MemberExpression)navigationPath.Body);
}
public IQueryable<TResult> CreateNavigationQuery<TSource, TResult>(TSource entity, Expression<Func<TSource, ICollection<TResult>>> navigationPath)
{
return CreateNavigationQuery<TResult>(entity, (MemberExpression)navigationPath.Body);
}
private IQueryable<TResult> CreateNavigationQuery<TResult>(object entity, MemberExpression navigationPath)
{
var navigationName = navigationPath.Member.Name;
var ose = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(entity);
var rm = this.ObjectContext.ObjectStateManager.GetRelationshipManager(entity);
var entityType = (EntityType)ose.EntitySet.ElementType;
var navigation = entityType.NavigationProperties[navigationName];
var relatedEnd = rm.GetRelatedEnd(navigation.RelationshipType.FullName, navigation.ToEndMember.Name);
return ((dynamic)relatedEnd).CreateSourceQuery();
}
}
The Solution in Action
Once we have our CreateNavigationQuery method defined we can re-write our Main method to avoid querying and loading all that extra data:
static void Main(string[] args)
{
using (var ctx = new ProductCatalog())
{
var prod = ctx.Products.First();
var categoryName = ctx.CreateNavigationQuery(prod, p => p.Category).Select(c => c.Name).Single();
Console.WriteLine("{0} belongs to the {1} category.", prod.Name, categoryName);
var food = ctx.Categories.Find("FOOD");
var prodCount = ctx.CreateNavigationQuery(food, c => c.Products).Count();
Console.WriteLine("There are {0} food products.", prodCount);
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
VB.Net & Overloads
If you are a C# user then the compiler will take care of working out which overload of CreateNavigationQuery to use. If you are using VB.Net then you’ll need to help the compiler out when you call the collection based overload by explicitly including the types for the generics. Here is the sample usage in VB.Net:
Sub Main()
Using ctx = New ProductCatalog()
Dim prod = ctx.Products.First()
Dim categoryName = ctx.CreateNavigationQuery(prod, Function(p) p.Category).[Select](Function(c) c.Name).[Single]()
Console.WriteLine("{0} belongs to the {1} category.", prod.Name, categoryName)
Dim food = ctx.Categories.Find("FOOD")
Dim prodCount = ctx.CreateNavigationQuery(Of Category, Product)(food, Function(c) c.Products).Count()
Console.WriteLine("There are {0} food products.", prodCount)
End Using
Console.WriteLine("Press any key to exit.")
Console.ReadKey()
End Sub
Summary & Source Code
Using the RelationshipManager from the underlying ObjectContext we were able to add a helper method to our context that creates a query for the contents of a navigation property. This reduces the need to bring redundant data into memory, ultimately resulting in better performance for our application.
You can download the completed source code in C# and VB.Net here, I make no claims to being fluent in VB.Net so complaints about syntax and conventions will be cheerfully accepted ignored
EF CTP4 Tips & Tricks: Running Additional DDL
This is the fifth in a series of posts about the recently released Entity Framework Feature CTP4, now affectionately known as “EF Magic Unicorn Edition”.
For this post I’m going to assume you have a basic knowledge of Code First using DbContext.
One common question I hear is “how do I include additional indexes (or other db constructs) in a database generated by Code First?”. In this post I’m going to cover how to run some hardcoded DDL each time the database is created or re-created. I’m also putting together some code that solves the index question in a more generic way (allowing you to attribute the properties you want indexed).
Setup
You’ll need a reference to Microsoft.Data.Entity.Ctp.dll and System.Data.Entity.dll.
We are going to use the following derived context and single Product class. Our mission is to add an index to the column backing the Product.Code property.
public class ProductCatalog : DbContext
{
public DbSet<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
Creating an Initializer
In CTP4 we have the opportunity to register some logic to initialize the database for our context. We encapsulated this logic in a class that implements IDatabaseInitializer<TContext> and the logic will be executed the first time our context is constructed in each AppDomain.
I’m defining the initializer as a nested class inside the ProductCatalog so that we can make use of the protected ObjectContext property to execute commands. We could also have defined a property on ProductCatalog that exposed the protected context or we could have just used context.Database.Connection within our initializer and run commands against the DbConnection (this would have required us to open and close connections etc.). Ultimately the functionality of ExecuteStoreCommand will be available from DbContext (probably via the DbContext.Database property) but it’s not there in CTP4.
public class ProductCatalog : DbContext
{
public DbSet<Product> Products { get; set; }
public class Initializer : IDatabaseInitializer<ProductCatalog>
{
public void InitializeDatabase(ProductCatalog context)
{
if (!context.Database.Exists() || !context.Database.ModelMatchesDatabase())
{
context.Database.DeleteIfExists();
context.Database.Create();
context.ObjectContext.ExecuteStoreCommand("CREATE INDEX IX_Products_Code ON dbo.Products ( Code )");
}
}
}
}
Registering Our Initializer
Initializers are registered at the AppDomain level via the static method; System.Data.Entity.Infrastructure.Database.SetInitializer<TContext>(IDatabaseInitializer<TContext> strategy).
Here is a complete console application that shows all our code in action, including registering the initializer before any contexts are constructed in the AppDomain:
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
namespace AdditionalDDL
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<ProductCatalog>(new ProductCatalog.Initializer());
using (var ctx = new ProductCatalog())
{
Console.WriteLine("{0} products exist in the database.", ctx.Products.Count());
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
public class ProductCatalog : DbContext
{
public DbSet<Product> Products { get; set; }
public class Initializer : IDatabaseInitializer<ProductCatalog>
{
public void InitializeDatabase(ProductCatalog context)
{
if (!context.Database.Exists() || !context.Database.ModelMatchesDatabase())
{
context.Database.DeleteIfExists();
context.Database.Create();
context.ObjectContext.ExecuteStoreCommand("CREATE INDEX IX_Products_Code ON dbo.Products ( Code )");
}
}
}
}
public class Product
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
}
Summary
In this post we covered running custom DDL each time our database is created by Code First, to achieve this we used a custom database initializer. I’m also working on a more generic initializer that is capable of adding indexes based on attributes in the class and will post about this later.
Read Full Post | Make a Comment ( 6 so far )EF CTP4 Tips & Tricks: WCF Data Service on DbContext
This is the fourth in a series of posts about the recently released Entity Framework Feature CTP4, now affectionately known as “EF Magic Unicorn Edition”.
For this post I’m going to assume you are somewhat familiar with the Productivity Improvements for EF and WCF Data Services.
You can download the completed VS2010 project from this post.
Update: If you want to use DbContext with Dynamic Data then you can use a similar approach as described in this post by Stephen Naughton.
Existing ObjectContext Experience
If you’ve used WCF Data Services on top of Entity Framework before then you would have added a new Entity Data Model to your project, which in turn creates a derived ObjectContext class to allow you to query and persist data using the model. Then you would have added a WCF Data Service class that derives from a DataService of your derived ObjectContext, something like this:
using System.Data.Services;
using System.Data.Services.Common;
namespace PI.WCFDataService.Sample
{
public class BlogService : DataService<BlogContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Blogs", EntitySetRights.All);
config.SetEntitySetAccessRule("Posts", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
}
What About DbContext?
Now what if your BlogContext derives from DbContext instead of ObjectContext? In the current CTP4 you can’t just create a DataService of a derived DbContext, although you can expect this to work by the time there is an RTM release.
But there is some good news, DbContext uses ObjectContext under the covers and you can get to the underlying context via a protected member. So lets define our derived context so that it exposes the context for our service to use:
using System.Data.Entity;
using System.Data.Objects;
namespace Service
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public ObjectContext UnderlyingContext
{
get { return this.ObjectContext; }
}
}
}
Now all we need to do is create a DataService of ObjectContext and override the CreateDataSource method. In CreateDataSource we will construct a BlogContext and then return the underlying ObjectContext. Note that we also need to turn off proxy creation to allow WCF Data Services to function correctly.
using System.Data.Objects;
using System.Data.Services;
using System.Data.Services.Common;
namespace Service
{
public class BlogService : DataService<ObjectContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("BlogSet", EntitySetRights.All);
config.SetEntitySetAccessRule("PostSet", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
protected override ObjectContext CreateDataSource()
{
var ctx = new BlogContext();
ctx.UnderlyingContext.ContextOptions.ProxyCreationEnabled = false;
return ctx.UnderlyingContext;
}
}
}
That’s it, now WCF Data Services just thinks it’s interacting with a standard ObjectContext and everything works.
Entity Set Naming
If you are using the Code First capabilities of DbContext then your entity set names will be <ClassName>Set. Obviously this isn’t brilliant and is something that will improve in future releases. The likely solution is to use the name of the DbSet property that you defined on the derived context.
Virtual SaveChanges
One thing to note with this approach is that WCF Data Services is going to call SaveChanges on the underlying ObjectContext and not your derived context. This is only an issue if you have overridden SaveChanges in your derived context to include business logic because this logic won’t get executed when the Data Service persists changes to the database. There isn’t a workaround for this at the moment but the issue will go away once there is native support for DbContext in WCF Data Services.
Summary
You can use DbContext with WCF Data Services, it’s not 100% natively supported just yet but will be in the future. The code to get it to work is pretty simple and makes use of the underlying ObjectContext from your derived DbContext.
Read Full Post | Make a Comment ( 14 so far )EF CTP4 Tips & Tricks: Mapping to an Existing Database
This is the third in a series of posts about the recently released Entity Framework Feature CTP4 (AKA “EF Magic Unicorn Edition”).
Most of the samples/demos of Code First so far have focused on creating a new database that is generated from a set of classes, however Code First also can be used in scenarios where you have an existing database.
For the majority of folks the existing designer/edmx based reverse engineering process (often referred to as Database First) already provides a good story for generating a model based on an existing database, with the option to tweak the object model and mapping. There are however some folks who would rather specify mapping using code than a designer.
You can download the completed VS2010 project from this post.
The Schema
Let’s start by looking at the schema we are going to map to, I’ve intentionally used table/column naming that makes it clear what is happening when we start mapping in code… I’m definitely not implying it’s good database design to post-fix table names with “Table” ![]()
The schema is pretty straightforward, there are a couple of things to call out:
- PostTagsTable represents a many to many relationship between Tags and Posts
- UserTable is used to store two types of Users
- Normal Users will have a value of “U” in the user_type column and should have ‘null’ in the admin_from column
- Administrators will have a value of “A” in the user_type column and will have a valid date value in the admin_from column
The Object Model
Let’s define a set of classes that we are going to use EF to map to. We want our classes to be completely independent of whatever data access technology we are using so I’m just writing my own POCO classes.
The object model is pretty self explanatory, a couple of points:
- For the Post->Blog relationship we are not exposing a foreign key property on Post but for the Post->User relationship we are (Post.AuthorId)
(This is purely to show the difference in mapping, I’d recommend being consistent one way or the other throughout your model) - For the Post->User relationship we are only exposing a navigation property on one side (i.e. there is a Post.Author but no User.Posts)
- There is no class corresponding to the PostTagsTable, just collection properties on the Post and Tag classes to represent the relationship
- Although Users and Admins are stored in the same table in the database we have split them out into an inheritance hierarchy in the object model
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Extract { get; set; }
public string Body { get; set; }
public Blog Blog { get; set; }
public ICollection<Tag> Tags { get; set; }
public int AuthorId { get; set; }
public User Author { get; set; }
}
public class Tag
{
public string TagText { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
}
public class Admin : User
{
public DateTime AdminFrom { get; set; }
}
The Context
Before we dive into mapping, we need to define a context to use for interaction with the database.
A couple of points to note:
- I’m exposing a DbConnection based constructor just to keep everything in code for this post
The easier alternative is to use the default constructor and add a connection string in your app/web.config file with the name “BlogContext”EF will match the app/web.config entry with the context name at runtime and use it
- I’m overriding the protected OnModelCreating method which gives us the opportunity to tweak the model that is built via convention, this method is where we will add our mapping code
public class BlogContext : DbContext
{
public BlogContext(DbConnection connection)
: base(connection)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// TODO: Insert mapping here
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Admin> Admins { get; set; }
}
Switch Off Database Initialization
DbContext has some smarts built in to try and help you out with managing database initialization, because we are taking care of managing the database schema we should turn that off.
The initialization strategy is an AppDomain wide setting for each context type so this code should execute prior to constructing any instances of the context in your application:
Database.SetInitializer<BlogContext>(null);
Mapping
If we tried to use our context for data access now we’d get a bunch of exceptions because the database we defined at the start doesn’t match what EF is expecting. For example Code First has used a convention to decide that the table for Blog should be called Blogs, which is fine if we let Code First create the database but not so good if we already have a table called BlogTable. In fact the first exception we get would tell us the Code First doesn’t know what the primary key property is for Tag because it doesn't follow the Id or <ClassName>Id convention.
There is however a bunch of stuff that the conventions will correctly take care of for us, like detecting that Blog.BlogId is a primary key and set as an Identity column in the database. Let’s take a look at the mapping code we would add in the OnModelCreating method for each of our five classes.
Blog
Blog is pretty simple to configure, first we are specifying that Name is a required property in the database and has a maximum length of 50.
modelBuilder.Entity<Blog>()
.Property(b => b.Name)
.IsRequired()
.HasMaxLength(50);
Secondly we are specifying which table it maps to and how the properties map to the columns. Notice the anonymous type projection syntax for specifying which properties are mapped to the database and what their respective column names are. On the upside this syntax is concise and reads pretty well if you look at the code, on the down side it isn’t very discoverable when you look at the method signature for MapSingleType; public EntityMap MapSingleType(Expression<Func<TEntity, object>> propertyMap). Any feedback or comments you have on this syntax will help us shape the Fluent API in the future.
modelBuilder.Entity<Blog>()
.MapSingleType(b => new { blog_id = b.BlogId, name = b.Name })
.ToTable("BlogTable");
Post
Post is probably the most interesting mapping, we have a few properties to configure which are just variations on code we have already seen.
modelBuilder.Entity<Post>()
.Property(p => p.Title)
.IsRequired()
.HasMaxLength(50);
modelBuilder.Entity<Post>()
.Property(p => p.Extract)
.HasMaxLength(500);
modelBuilder.Entity<Post>()
.Property(p => p.Body)
.IsRequired();
Next we are configuring the many to many relationship between Post and Tags, here we see the relationship API in action and also the syntax for specifying how the join table of a many to many relationship is controlled.
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(t => t.Posts)
.Map("PostTagsTable", (p, t) => new
{
post_id = p.PostId,
tag_id = t.TagText
});
The one to many relationship between Post and User only exposed a navigation property on Post. We can still specify that Users have many Posts, even though there is no User.Posts property, by using the parameterless overload of HasMany(). We also exposed a foreign key property on the Post class so we need to specify that Post.Author and Post.AuthorId both represent the same relationship, we use the HasConstraint method to do this.
modelBuilder.Entity<Post>()
.HasRequired<User>(p => p.Author)
.WithMany()
.HasConstraint((p, u) => p.AuthorId == u.UserId);
Finally we specify the table and column mappings. Note how the syntax for specifying foreign key columns differs depending on whether a corresponding foreign key property was exposed on the object. If there is a foreign key property in the object mdoel then we can just map that directly to the column (author_id = p.AuthorId), if not then we can dot through the navigation property to the primary key of the related entity (blog_id = p.Blog.BlogId).
modelBuilder.Entity<Post>()
.MapSingleType(p => new
{
post_id = p.PostId,
title = p.Title,
extract = p.Extract,
body = p.Body,
author_id = p.AuthorId,
blog_id = p.Blog.BlogId
})
.ToTable("PostTable");
Tag
Because Tag doesn’t have an obvious primary key property for Code First to detect by convention we need to configure which property is the key.
modelBuilder.Entity<Tag>()
.HasKey(t => t.TagText);
Apart from the key the rest is just a simple table and column mapping.
modelBuilder.Entity<Tag>()
.MapSingleType(t => new
{
tag_id = t.TagText
})
.ToTable("TagTable");
User & Admin
We map User and Admin at the same time because they form part of the same inheritance hierarchy. Note that we are now using the MapHierarchy method rather than MapSingleType. We then use the Case<T> methods to specify how the two types map to the same table. We specify the values that should go in the discriminator column (user_type) by specifying constants in each mapping fragment. Also note that we don’t need to reconfigure properties as we move up the inheritance hierarchy (i.e. in the Admin block we don’t need to map ‘user_id’ or ‘user_name’ because we already mapped them for the base type).
modelBuilder.Entity<User>()
.Property(u => u.UserName)
.IsRequired()
.HasMaxLength(50);
modelBuilder.Entity<User>()
.MapHierarchy()
.Case<User>(u => new
{
user_id = u.UserId,
user_name = u.UserName,
user_type = "U"
})
.Case<Admin>(a => new
{
admin_from = a.AdminFrom,
user_type = "A"
})
.ToTable("UserTable");
Access Data
With all the mapping specified we are now ready to start accessing data:
using (var connection = new SqlConnection(@"Server=.\SQLEXPRESS;Database=ExistingBlogDb;Trusted_Connection=True;"))
{
using (var context = new BlogContext(connection))
{
var blog = new Blog { Name = "romiller.com" };
var me = new Admin { UserName = "Rowan", AdminFrom = DateTime.Today };
var ctp4 = new Tag { TagText = "CTP4" };
var post = new Post
{
Title = "Mapping to an existing database",
Extract = "Blah blah blah",
Body = "Lost of useful info",
Blog = blog,
Author = me,
Tags = new List<Tag> { ctp4 }
};
context.Posts.Add(post);
context.SaveChanges();
}
}
Configuration Classes
Obviously our OnModelCreating method is getting pretty huge and this is only a small model, this is where derived configuration classes come in handy.
For example I could define the following configuration for Blog:
public class BlogConfig : EntityConfiguration<Blog>
{
public BlogConfig()
{
this.Property(b => b.Name)
.IsRequired()
.HasMaxLength(50);
this.MapSingleType(b => new { blog_id = b.BlogId, name = b.Name })
.ToTable("BlogTable");
}
}
And then just register it in the OnModelCreating method:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BlogConfig());
...
}
Code Generation
If we had a big database with a lot of tables then writing this configuration code is going to become a painful (probably unrealistic) task, in much the same way mapping a huge model to a database from scratch using the designer or xml wouldn’t be a fun task. For this very reason the designer will automatically generate a default 1:1 mapping for you as a starting point. So the logical next step would be to have something analyze your database and give you an object model and mapping code that you could then start to tweak… for a little side project I’m playing around with a generator that does exactly this, it’s not quite ready to share yet but I’ll post it up shortly.
Summary
Mapping to an existing database is achievable with Code First, although the edmx/designer based Database First approach is probably a better fit for the majority of folks. If your database schema varies a lot from the default Code First conventions then you will end up writing quite a bit of code. It is possible to write a generator that will spit out a starting point to reduce the amount of code you need to write by hand, I’ll be sharing the results of a pet project on this shortly.
Read Full Post | Make a Comment ( 20 so far )EF CTP4 Tips & Tricks: Find
This is the second in a series of posts taking a quick look at the some of the features in the recent Entity Framework Feature CTP4, also recently dubbed “EF Magic Unicorn Edition”. In the last post we looked at Include with lambda and today we are going to look at the new Find method.
The Problem
Let’s say I want to get a hold of the “FOOD” category. Prior to having the Find method I can do this pretty easily using LINQ:
var food = context.Categories.Single(c => c.CategoryId == "FOOD");
Actually the above line of code is going to query the database every time it runs so I should really use the TryGetObjectByKey method to avoid the query if “FOOD” is already loaded into memory. TryGetObjectByKey isn’t strongly typed so I need to supply an out parameter that is typed as object and then cast if it was found.
Category food;
object searchFood;
if (context.TryGetObjectByKey(key, out searchFood))
{
food = (Category)searchFood;
}
Now let’s update the code to create the “FOOD” category if it doesn’t exist:
Category food;
object searchFood;
if (context.TryGetObjectByKey(key, out searchFood))
{
food = (Category)searchFood;
}
else
{
food = new Category { CategoryId = "FOOD", Name = "Food Products" };
context.Categories.Add(food);
}
This works fine if I only run the code once but what if I want to find the “FOOD” category again? I now need to account for the fact that it might be sitting in the context in an added state. In this case it has a temporary key so TryGetObjectByKey won’t find it. That’s OK I have access to the ObjectStateManager to check for added objects:
Category food;
object searchFood;
if (context.TryGetObjectByKey(key, out searchFood))
{
food = (Category)searchFood;
}
else
{
food = context
.ObjectStateManager
.GetObjectStateEntries(System.Data.EntityState.Added)
.Where(ose => ose.Entity != null)
.Select(ose => ose.Entity)
.OfType<Category>()
.Where(c => c.CategoryId == "FOOD")
.SingleOrDefault();
if (food == null)
{
food = new Category { CategoryId = "FOOD", Name = "Food Products" };
context.Categories.Add(food);
}
}
The Solution
If you feel like you shouldn’t have to write that much code to achieve this fairly simple task you are definitely not alone. That block of code above can now be replaced with:
var food = context.Categories.Find("FOOD");
if (food == null)
{
food = new Category { CategoryId = "FOOD", Name = "Food Products" };
context.Categories.Add(food);
}
The rules for find are:
- Look for an entity with the supplied key that has already been loaded from the database
- If there isn’t one then check if there is an added entity that has the supplied key
- Finally query the database and if there still isn’t one that matches then return null
In CTP4 Find is an instance method on DbSet<T> so you do have to be using the new “Productivity Improvement” surface to get the benefit at least for the moment.
Composite Keys
Find takes a “params object[]” as it’s parameter so if you have composite keys you just specify the values for each key property:
var plate = context.LicensePlates.Find("WA", "555-555");
Code First requires that you specify the ordering of composite keys, you can do this via the Fluent API:
public class DMVContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<LicensePlate>().HasKey(p => new { p.State, Plate = p.Number });
}
public DbSet<LicensePlate> Plates { get; set; }
}
Or via attributes in you class:
public class LicensePlate
{
[DataMember(Order = 0)]
public string State { get; set; }
[DataMember(Order = 1)]
public string Number { get; set; }
}
“Yes” that is DataMember from the System.Runtime.Serialization namespace and “no” we shouldn’t make you add a reference to System.Runtime.Serialization.dll just to specify key ordering… we’ll fix that.
The Future
We’ve also looking at modifying the Add method so that it returns the newly added entity, this means we could reduce the code down to one line:
var food =
context.Categories.Find("FOOD")
?? context.Categories.Add(new Category { CategoryId = "FOOD", Name = "Food Products" );
Ok ok that’s a bit long to actually put on one physical line… but you get the idea.
Summary
Finding objects based on primary key value(s) used to be a fairly painful exercise that required using advanced API surface… now it’s a lot simpler and encourages you to write performant code that reduces hits to the database.
Read Full Post | Make a Comment ( 3 so far )« Previous Entries










