I have an application where I want to use the FILESTREAM feature for storing blobs. I know that EF6 does not support FILESTREAM, so I will manage file and image handling by myself. However, I have to write my own initialize code to add a FILEGROUP and FILE to my Database and:
public class CustomDbInitializer : CreateDatabaseIfNotExists<DatenbankContext>
{
public override void InitializeDatabase(DatenbankContext context)
{
base.InitializeDatabase(context);
SqlConnectionStringBuilder b = new SqlConnectionStringBuilder(context.Database.Connection.ConnectionString);
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction,"ALTER DATABASE "+ b.InitialCatalog + " ADD FILEGROUP GRP CONTAINS FILESTREAM");
//more sql commands
}
}
Then, I call manually the function InitializeDatabase(context)
new CustomDbInitializer().InitializeDatabase(context);
However, I find this a bit clunky and counter-intuitive. I know that I can set an initializer for the context in the constructor, but this does not work the way I want:
public class DatenbankContext : DbContext
{
public DatenbankContext()
:base("name=name")
{
Database.SetInitializer(new CustomDbInitializer());
}
}
Basically, I want the call
new DatenbankContext() // or
context.Database.CreateIfNotExists()
to initialize my Database with my custom strategy in an intuitive way. How can this be done?
Inside your custom initializer add:
if (!context.Database.Exists())
{
context.Database.Create();
}
// Do next steps
Call your initializer inside a static constructor. This insures it only runs once:
public class DatenbankContext : DbContext
{
static DatenbankContext()
{
Database.SetInitializer(new CustomDbInitializer());
}
}
See here for more on custom initializers. You probably don't want to inherit from CreateDatabaseIfNotExists if you want control over the process. See the example in link.
Related
I have simple classes to saves and get data (not like repository pattern). But while saving data to multiple tables I want to maintain a transaction. So I just went through Unit of work pattern, but that will require me to do a lot of changes. So I'm thinking if my approach will do the same as UOF.
Here's my code:
CalalogueRepository:
public interface ICalalogueRepository
{
void Create(string guid, string fileName);
}
public class CalalogueRepository : ICalalogueRepository
{
private CatalogueContext _catalogueContext;
public CalalogueRepository(CatalogueContext catalogueContext)
{
_catalogueContext = catalogueContext;
}
public void Create(string guid, string fileName)
{
_catalogueContext.Catalogues.Add(new Catalogue
{
CatalogueId = guid,
FileName = fileName
});
}
}
StuffRepo:
public interface IStuffRepo
{
void Create(string guid, List<StuffModel> myStuff);
}
public class StuffRepo : IStuffRepo
{
private CatalogueContext _catalogueContext;
public StuffRepo(CatalogueContext catalogueContext)
{
_catalogueContext = catalogueContext;
}
public void Create(string guid, List<StuffModel> myStuff)
{
//add stuff to _catalogueContext.StuffTable.Add
}
}
Finally a class that does the SaveChanges and Commit:
public class UOW : IUOW
{
private CatalogueContext _catalogueContext;
private ICalalogueRepository _calalogueRepo;
private IStuffRepo _stuffRepo;
public UOW(CatalogueContext catalogueContext,
ICalalogueRepository calalogueRepo,
IStuffRepo stuffRepo)
{
_catalogueContext = catalogueContext;
_calalogueRepo = calalogueRepo;
_stuffRepo = stuffRepo;
}
public void Save (string guid, string fileName, List<StuffModel> myStuff)
{
using (IDbContextTransaction transection = _catalogueContext.Database.BeginTransaction())
{
_calalogueRepo.Create(guid, fileName);
_stuffRepo.Create (guid, myStuff);
_catalogueContext.SaveChanges();
transection.Commit();
}
}
}
I think there is only 1 CatalogueContext throughout the call.
Ok, so as you can see here, AddDbContext is the right way to register it as you wrote in the comment on the question.
Here it says that AddDbContext will register the context as scoped.
And here you can find what scoped means.
Overall I think you are right that your code will use the same context throughout the Save method.
Couple thoughts:
Probably you want to have a try-catch in case an exception is thrown and you want to rollback
If you are not sure if it's working why not try it? You should test your code/application anyways.
Probably this could be done in a better way, but I don't have the context about the rest of your code/application, so I cannot tell. (Not sure what you mean by "...Unit of work pattern, but that will require me to do a lot of changes." for example.)
Now the Create methods not self-contained, meaning if you just want to add a new item to the table it is not enough to call Create, but separately call SaveChanges(). This is not an explicit problem, but has to be kept in mind and might be a little bit confusing for new developers on the project.
I wish to ensure Entity Framework never overwrites or tries to create the database I am connecting to. It is an established database previously accessed by ASP website (visual basic script flavour). I've seen the convention where you pass "name=" into the base constructor like so...
//example 1
public class SchoolDBContext: DbContext
{
public SchoolDBContext() : base("name=SchoolDBConnectionString")
{
}
}
BUT I'd prefer to NOT have it HARDCODED (like the above), so perhaps...
//example 2
public SchoolDBContext(string ConnectionString) : base("name=" + ConnectionString) {
}
BUT I'd also like to be able to bug out if the string is empty...
//example 3
public SchoolDBContext(string ConnectionString) : {
if (string.IsNullOrWhitespace(ConnectionString) throw Exception("empty connection string");
base("name=" + ConnectionString);
}
Question 1: I'm unsure if the third bit of code does the same job as the second, does it? As the third example may call the parameterless construction first before calling base with the "name="
Question 2: Is there options available in the constructor that can be used to configure EntityFramework more robustly?
I'm Using EF 6
If you dont want EF to overwrite och create a new database every time you need to add the createinitializer class.
public class CreateInitializer : CreateDatabaseIfNotExists<SchoolDBContext>{}
Question 1:
Im not sure either. I need to admit I never saw something like that in examples. May you tell us/me your documentation source or how you came up with that Idea?
Question 2:
If you wan't a independence constructor, how about you try this?
public SchoolDBContext()
: base(ConnectionString == null ? "name=SchoolDBConnectionString" : WhateverYouDoIfItsWrong)
{
// Your code here! Lucky you
}
Have a look at the DbMigrationsConfiguration.
You can create a new class SchoolDbContextConfiguration-class which inherrit from it and set AutomaticMigrationsEnabled to false.
internal sealed class Configuration : DbMigrationsConfiguration<SchoolDBContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
And in your SchoolDbContext initialize as follows:
public SchoolDBContext(string ConnectionString) : base("name=" + ConnectionString) {
var init = new MigrateDatabaseToLatestVersion<SchoolDBContext, SchoolDbContextConfiguration>(true);
Database.SetInitializer(init);
}
I am currently working on a project with has a console app and few library projects. One library project is a EF code first project which contains my models and the context:
public class MyDbContext: DbContext
{
public MyDbContext() : base("MyConnectionString")
{
}
public DbSet<File> Files { get; set; }
}
I also have a singleton class through which I want to access the database. The singleton looks like this:
public sealed class DbLogger : IDbLogger
{
private static readonly DbLogger instance = new DbLogger();
private static MyDbContext ctx = new MyDbContext();
static DbLogger() {
Database.SetInitializer<MyDbContext>(new DbInitializer());
}
private DbLogger() { }
public static DbLogger Instance
{
get {
return instance;
}
}
public void AddFile(string fileName)
{
ctx.Files.Add(new File() { FullPath = fileName });
}
}
The db initializer is very simple and just implements the CreateDatabaseIfNotExists. Nothing is done in Seed yet.
In the console all which references the library project I just want to use it as:
private DbLogger logger = DbLogger.Instance;
and call the logger from a Task using:
logger.AddFile("myFileName");
When the app gets to logger.AddFile call I get the following exception:
An exception of type 'System.InvalidOperationException' occurred in
EntityFramework.dll but was not handled in user code
Additional information: The context cannot be used while the model is
being created. This exception may be thrown if the context is used
inside the OnModelCreating method or if the same context instance is
accessed by multiple threads concurrently. Note that instance members
of DbContext and related classes are not guaranteed to be thread safe.
How can I delay the using of the context until the model was created?
I am currently a bit stuck with this and any idea on how to solve this would be appreciated.
Thank you!
I recommend this approach
public void AddFile(string fileName){
using(var ctx = new MyDbContext() ){
ctx.Files.Add(new File() { FullPath = fileName });
ctx.SaveChanges();
}
}
You should only use the DbContext when needed. Open the DB connection, interact with the DB and close the connection. The using statement take care of the opening and closing of the DB connection.
EDIT - updated with SaveChanges()
ad to #Kunukn answer:
I think that you should blame
private static MyDbContext ctx = new MyDbContext();
It was trying to acces context before database initializer run.
If you don't wan't to create new context on every AddFile() call, try create context in static constructor.
I see problem with line
Database.SetInitializer(new DbInitializer());
if you use
public void AddFile(string fileName){
using(var ctx = new MyDbContext() ){
ctx.Files.Add(new File() { FullPath = fileName });
ctx.SaveChanges();
}
}
then your purpose of singleton is not getting solved because it will create a new MyDbContext every time AddFile is called ( and this is recommended)
but even if you insist on having a single dbcontext object then you should create some
initialization fucntion and might be call it after object is created.
might be something like
private DbLogger logger = DbLogger.Instance;
logger.Initialize()
I have been learning how to use EF for a week or so now and am stuck on the issue of creating/updating my database. I am able to create an initializer to create the database if it is not there:
static class Program
{
static void Main()
{
Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer());
....
class GumpDatabaseInitializer : CreateDatabaseIfNotExists<GumpDatabase>
{
public GumpDatabaseInitializer()
{
}
protected override void Seed(GumpDatabase context)
{
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)");
// Other stuff
}
}
Or I can create a Configuration to migrate the db
static class Program
{
static void Main()
{
Database.SetInitializer<GumpDatabase>(new MigrateDatabaseToLatestVersion<GumpDatabase, Configuration>());
....
internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
}
protected override void Seed(GumpDatabase context)
{
}
Each works correctly but I haven't figured out a way to do both. I can switch between the two initializers by changing the SetInitializer call but if I want to create the database if it is not there and also migrate it if it is what do I do? Do I need to create a custom initializer?
Thanks
Edit based on NSGaga answer
class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext>
where TContext : DbContext
where TConfiguration : DbMigrationsConfiguration<TContext>, new()
{
private readonly DbMigrationsConfiguration _configuration;
public CreateOrMigrateDatabaseInitializer()
{
_configuration = new TConfiguration();
}
public CreateOrMigrateDatabaseInitializer(string connection)
{
Contract.Requires(!string.IsNullOrEmpty(connection), "connection");
_configuration = new TConfiguration
{
TargetDatabase = new DbConnectionInfo(connection)
};
}
void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
{
Contract.Requires(context != null, "context");
if (context.Database.Exists())
{
if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false))
{
var migrator = new DbMigrator(_configuration);
migrator.Update();
}
}
else
{
context.Database.Create();
Seed(context);
context.SaveChanges();
}
}
protected virtual void Seed(TContext context)
{
}
}
and
internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;
SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
}
protected override void Seed(GumpDatabase context)
{
}
}
and
class GumpDatabaseInitializer : CreateOrMigrateDatabaseInitializer<GumpDatabase,Gump.Migrations.Configuration>
{
public GumpDatabaseInitializer()
{
}
protected override void Seed(GumpDatabase context)
{
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)");
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Sequences (Name)");
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX StationPartNumber ON StationPartNumbers (StationId,PartNumberId)");
}
}
and finally
static void Main()
{
Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer());
I think you're pretty much there - you can lookup the source code for MigrateDatabaseToLatestVersion (it's open source http://entityframework.codeplex.com/) - it's pretty simplistic, what it does pretty much is call the DbMigrator - as far as I could see.
All you have to do seems is to merge the two - use one or the other as a basis, add other functionality in there - that should work fine I think.
class CreateAndMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext>
where TContext : DbContext
where TConfiguration : DbMigrationsConfiguration<TContext>, new()
{
private readonly DbMigrationsConfiguration _configuration;
public CreateAndMigrateDatabaseInitializer()
{
_configuration = new TConfiguration();
}
public CreateAndMigrateDatabaseInitializer(string connection)
{
Contract.Requires(!string.IsNullOrEmpty(connection), "connection");
_configuration = new TConfiguration
{
TargetDatabase = new DbConnectionInfo(connection)
};
}
void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
{
Contract.Requires(context != null, "context");
var migrator = new DbMigrator(_configuration);
migrator.Update();
// move on with the 'CreateDatabaseIfNotExists' for the 'Seed'
base.InitializeDatabase(context);
}
protected override void Seed(TContext context)
{
}
}
call it like this...
Database.SetInitializer(new CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration>());
...actually, override it (since it's generic implementation) like you were doing for CreateDatabaseIfNotExists (you just have extra 'param' for Configuration) - and just supply the 'Seed'.
class GumpDatabaseInitializer : CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration>
{
protected override void Seed(GumpDatabase context)
{
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)");
}
}
...and call it something like
Database.SetInitializer(new GumpDatabaseInitializer());
EDIT:
Based on the comments - DbMigrator should not run twice. It always checks (spends a bit of time) and does a 'blank' update and moves on. However just in case if you'd like to remove that and 'check' before entering - this should work (change the similar piece above)...
var migrator = new DbMigrator(_configuration);
if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false))
if (migrator.GetPendingMigrations().Any())
migrator.Update();
(this is a redundant / double-check - one of the if-s should be enough. Put a break there - and see exactly what's happening, it should not get in - once Db is migrated. As I mentioned, works fine when I test it.
EDIT:
Replace the inside of InitializeDatabase with...
var doseed = !context.Database.Exists();
// && new DatabaseTableChecker().AnyModelTableExists(context);
// check to see if to seed - we 'lack' the 'AnyModelTableExists' - could be copied/done otherwise if needed...
var migrator = new DbMigrator(_configuration);
// if (doseed || !context.Database.CompatibleWithModel(throwIfNoMetadata: false))
if (migrator.GetPendingMigrations().Any())
migrator.Update();
// move on with the 'CreateDatabaseIfNotExists' for the 'Seed'
base.InitializeDatabase(context);
if (doseed)
{
Seed(context);
context.SaveChanges();
}
This works around (half-way) not-seeding - if migration goes first. And migrations have to be first, otherwise you have issues.
You still need to do it properly - this is the gist if not all you might need - but if any issues w/ MySQL etc., probably some more leg work here.
Note: Still seeding doesn't call if you have a db, but it's empty. Problem is mixing of the two different initializers. So you'll have to work that out - either by implementing what Create... does inside (that call we can't call) or something else.
Actually it should be:
var migrator = new DbMigrator(_configuration);
if (!context.Database.CompatibleWithModel(false) || migrator.GetPendingMigrations().Any())
migrator.Update();
because if we have a migration, that is not related to our db model, for example inserting a row in any of our tables, the migration won't be executed.
To do both (seed and migrate) you really only have to use migrations with a MigrateDatabaseToLatestVersion initializer. When you enable migrations for your context a Configuration class derived from DbMigrationsConfiguration is created and you can override the Seed method to seed your database. Note that the database may already contain seed data when this method executes but the AddOrUpdate extension method conveniently helps you make "upserts" in your database.
This is different compared to the Seed method of some of the other database intitializers where the database is only seeded when it is initially created. However, when you are using migrations you may want to change your seed data when the database changes and using MigrateDatabaseToLatestVersion makes that possible.
To combine seeding with migrations you will have to perform the following steps in a new project:
Create a code-first DbContext with associated entities
In the package manager console execute the command Enable-Migrations
In the Migrations folder a Configuration class is generated with a Seed method. You can modify this method to seed your database:
protected override void Seed(MyContext context) {
// Add two entities with name "Foo" and "Bar".
context.MyEntities.AddOrUpdate(
e => e.Name,
new MyEntity { Name = "Foo" },
new MyEntity { Name = "Bar" }
);
}
You need to create a database initializer that derives from MigrateDatabaseToLatestVersion:
class MyContextInitializer
: MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration> { }
You will also have to configure the initializer either by calling Database.SetInitializer(new MyContextInitializer()) when you application starts or in the App.config file by using the <databaseInitializer/> element.
In the constructor for the generated Configuration class you can enable automatic migrations:
public Configuration() {
AutomaticMigrationsEnabled = true
}
However, in a team you might prefer to not do that. In that case you will have to create an initial migration (unless it was created when you did Enable-Migrations). In the package manager execute the command Add-Migration InitialCreate. This creates the first migration required to create your database.
At this point you have a DbContext with migrations and a Seed method.
So to sum it: Enable migrations, use the MigrateDatabaseToLatestVersion initializer and add seed data in the Configuration class that was generated when migrations were enabled.
While MigrateDatabaseToLatestVersion does actually create the DB if it does not exist and even allows you to seed it, if you already have a working solution based on CreateDatabaseIfNotExists and/or don't want to complicate it with testing for existence of seed data, you can just use the below by inheriting from it rather than from CreateDatabaseIfNotExists:
public class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext>
where TContext : DbContext
where TConfiguration : DbMigrationsConfiguration<TContext>, new()
{
void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
{
if (context.Database.Exists())
{
if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false))
{
var migrationInitializer = new MigrateDatabaseToLatestVersion<TContext, TConfiguration>(true);
migrationInitializer.InitializeDatabase(context);
}
}
base.InitializeDatabase(context);
}
}
This is based on previous answers and OP's own solution. This should work with other providers as well, but I only tested with SQL Server.
I am using EF Code First in an Asp.net project. Code First created my database just fine but I am having fits getting it to seed my database with data. I am using a custom database initializer like so:
namespace Toolkit.Model
{
public class EntitiesContextInitializer : DropCreateDatabaseIfModelChanges<ToolkitContext>
{
protected override void Seed(ToolkitContext context)
{
List<Server> Servers = new List<Server>
{
new Server { ServerName = "Server 16" },
new Server { ServerName = "Server 29" }
};
foreach (Server server in Servers)
{
context.Servers.Add(server);
}
context.SaveChanges();
}
}
}
I set the initializer in the the Global.asax.cs and force the database to initialize like so:
using Toolkit.Model;
namespace Toolkit
{
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
Database.SetInitializer(new EntitiesContextInitializer());
var context = new ToolkitContext();
context.Database.Initialize(false);
}
...
}
The problem is that my Initializer never gets called and so my database never gets populated. I have even tried changing the initializer to inherit DropCreateDatabaseAlways instead but still nothing... What am I missing here?
EDIT
So after dropping the database my initializer got called fine. However I still wonder why it didn't get called in other instances such as when I added a table (so the model changed) or when I changed the initialzer to be DropCreateDatabaseAlways.
I'm using a similar configuration for a SQL CE database, but instead of DropCreate, I'm using CreateIfNotExists.
The difference between what I have and yours is that you have are creating a context and calling initialize on the database. Based on that, I would suggest that you remove the context setup and the Database.initialize calls that you have in the application_start method.
try
Database.SetInitializer<MyContextName>(new EntitiesContextInitializer)
in EF 5.0 the library is declared as
public static void SetInitializer<TContext>(IDatabaseInitializer<TContext> strategy) where TContext : DbContext;
You can use Database Migrations with Code-First. Whenever you use Update-database command when your model changes, your Seed method (in Migrations) will be called.
This article explains more:
http://msdn.microsoft.com/en-gb/data/jj591621
You can also force it in Application_Start() your way:
Database.SetInitializer(new EntitiesContextInitializer());
var context = new ToolkitContext();
context.Database.Initialize(true);
At the end of your overriden menthod, Seed, in class, EntitiesContextInitializer, place at the end of the method add a line:
base.Seed (context)