I'm trying to force EF Code First to re-initialize the database. My first thoughts were to call:
dbContext.Database.Delete();
dbContext.Database.Create();
This creates a new database but the seeding strategy (set using Database.SetInitializer<>) is ignored. I'm doing the above in the Application_Start method. Any ideas?
I have also tried:
dbContext.Database.Initialize(true);
One option is to create your own DatabaseInitializer that inherits from DropCreateDatabaseAlways.
An example of this would be.
public class MyInitializer : DropCreateDatabaseAlways<EmployeeContext>
{
protected override void Seed(EmployeeContext context)
{
context.Employees.Add(new Employee() {FirstName = "Marcy"});
base.Seed(context);
}
}
public class EmployeeContext : DbContext
{
static EmployeeContext()
{
Database.SetInitializer(new MyInitializer()); // using my own initializer
}
public IDbSet<Employee> Employees { get; set; }
}
Here's what I did to overcome this:
Add a flag in your context initializer
public bool WasSeeded = false;
Create a public method in your initializer and put your seed code there. I made sure to use only AddOrUpdate or other upsert methods, i.e. check the data is not in the DB before:
context.Students.AddOrUpdate
At the end set the flag to true
After you initialize the context, check the flag and if it is false run the logic manually:
if (!initializer.WasSeeded)
{
initializer.SeedAndUpsert(context);
}
Database.SetInitializer(new CustomInitializer()); //CustomInitializer contains the overriding Seed method
using (var context = new CustomDatabaseContext())
{
context.Database.Initialize(force: true);
}
Related
I am having issues right now with initializing the database using code first. I'm basically having problems on how to trigger my initializer if my database does not exist.
I've tried the following,
https://stackoverflow.com/a/28960111/639713
but my problem with this is I have to call this method on my initial page for it to trigger the create database. Even with that, the tables will not be created unless I manually do it. This will be an issue if I'm going to integrate this with an app that is already using sql server and already have about 50 tables on the dbcontext.
Anyway, here's my code:
DbContext
public class TestMigrationsDatabase : DbContext
{
public TestMigrationsDatabase()
: base(nameOrConnectionString: "TestMigrations.Domain.Entities.TestMigrationsDatabase")
{
//Database.SetInitializer<TestMigrationsDatabase>(null);
Database.SetInitializer<TestMigrationsDatabase>(new TestMigrations.Domain.TestMigrationsInitializer());
}
public DbSet<Base> Bases { get; set; }
public DbSet<Fighter> Fighters { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("public"); // postgresql specific
base.OnModelCreating(modelBuilder);
}
public override int SaveChanges()
{
return base.SaveChanges();
}
}
Initializer:
public class TestMigrationsInitializer : CreateDatabaseIfNotExists<TestMigrationsDatabase>
{
protected override void Seed(TestMigrationsDatabase context)
{
this.CreateDatabase(ConfigurationManager.ConnectionStrings["TestMigrations.Domain.Entities.TestMigrationsDatabase"].ToString());
base.Seed(context);
LoadTestTables(context);
}
private void LoadTestTables(TestMigrationsDatabase context){
Base base = new Base();
base.Name = "Test 1 Base";
context.Bases.Add(base);
context.SaveChanges();
}
public void CreateDatabase(string connectionString)
{
var builder = new NpgsqlConnectionStringBuilder(connectionString);
var databaseName = "TestMigrations"; // REMEMBER ORIGINAL DB NAME
builder.Database = "postgres"; // TEMPORARILY USE POSTGRES DATABASE
// Create connection to database server
using (var connection = new NpgsqlConnection(builder.ConnectionString))
{
connection.Open();
// Create database
var createCommand = connection.CreateCommand();
createCommand.CommandText = string.Format(#"CREATE DATABASE ""{0}"" ENCODING = 'UTF8'", databaseName);
createCommand.ExecuteNonQuery();
connection.Close();
}
}
}
Controller
public class HomeController : Controller
{
public TestMigrationsDatabase _context = new TestMigrationsDatabase();
//
// GET: /Home/
public ActionResult Index()
{
TestMigrations.Domain.TestMigrationsInitializer initializer = new Domain.TestMigrationsInitializer();
initializer.CreateDatabase(ConfigurationManager.ConnectionStrings["TestMigrations.Domain.Entities.TestMigrationsDatabase"].ToString());
return View();
}
}
So with all that, my questions are:
1. Is putting that initializer on the first controller to trigger it correct? Or should I just instantiate the context so that the constructor will trigger teh initializer?
2. How do I properly create the tables after the database is created?
Thanks!!!
I would like to implement nlog to each action to add an element.
So when I do myContext.Society.Add(), I would like to log something.
I create a class DbSetExtension and modify the context StockContext to use DbSetExtension<T> instead DbSet.
public class DbSetExtension<T> : DbSet<T> where T : class
{
public override T Add(T entity)
{
LoggerInit.Current().Trace("Add Done");
return base.Add(entity);
}
}
When i launch the programm, I notice when I access to myContext.Society.Add.
Society is null. So I think I miss something with my class DbSetExtension but I don't find.
public class StockContext : DbContext
{
public StockContext()
: base("StockContext")
{
}
public DbSet<HistoricalDatas> HistoricalDatas { get; set; }
public DbSet<Society> Society { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Do you have any idea,
Regards,
Alex
[UPDATE]
Code allows to add.
If I replace DbSetExtension by DbSet, the same code works.
So my assumption is I miss something when I inherit from DbSet.
public bool SetSymbols()
{
CsvTools csvThreat = new CsvTools();
List<Eoddata> currentEnum =
csvThreat.ExtractData<Eoddata>(ConfigurationManager.GetString("FilePathQuotes", ""));
currentEnum.ForEach(
c =>
{
//LoggerInit.Current().Trace("Add Done");
Sc.Society.Add(
new Society()
{
RealName = c.Description,
Symbol = String.Format("{0}.PA", c.Symbol),
IsFind = !String.IsNullOrEmpty(c.Description)
});
});
if (Sc.SaveChanges() > 0)
return true;
return false;
}
In my opinion you took totally wrong direction. DbContext is made to work with DbSet and not DbSetExtension class. It is able to instantiate objects of type DbSet and not your own type. This is basically why you get this exception. Reparing it would require probably hacking EF internals and I fear that this problem will be just a beginning for you. Instead I would recommend you to use general way of logging with EF with use of interceptor classes. Here this is explained in details at the end of article Logging and Intercepting Database Operations. Generally this approach would be much more advantageous for you. Why? Because DbContext is just man-in-the-middle in communication with db. In logs you generally cares about what happens to db and its data. Calling Add method on DbSet may not have any effect at all if SaveChanges won't be called lated on. On contrary query interceptors lets you log strictly only interaction with db. Basing on query sent to db you may distinguish what is going on.
But if you instist on your approach I would recommend you using extension methods instead of deriving from DbSet:
public static class DbSetExtensions
{
public static T LoggingAdd<T>(this DbSet<T> dbSet, T entity)
{
LoggerInit.Current().Trace("Add Done");
return dbSet.Add(entity);
}
}
and call it like this:
context.Stock.LoggingAdd(entity);
This is my breezeController using EF repository:
[BreezeController]
public class BreezeController : ApiController
{
private readonly MyRepository _repository;
public BreezeController()
{
_repository = new MyRepository(User);
}
[HttpPost]
[ValidateHttpAntiForgeryToken]
public SaveResult SaveChanges(JObject saveBundle)
{
return _repository.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<Compound> Compounds(int id)
{
var compounds = new List<Compound>();
compounds.add(new Compound() { Name = "cmp1" });
compounds.add(new Compound() { Name = "cmp2" });
compounds.add(new Compound() { Name = "cmp3" });
// Save compounds to database
return compounds.AsQueryable();
}
}
I'd like to save the compounds created here to database before returning. Should I call SaveChanges? How?
UPDATE:
I tried to bring the objects to client and save. However, I can't seem to use those objects directly as:
cs.compound = compound;
manager.saveChanges();
Because I'm getting this error "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries". How can I get around this error? I believe I just missed a little tweak.
Instead, I had to create entity as usual, and assign properties one by one like
cs.compound = manager.createEntity("Compound");
cs.compound.name = compound.name;
...
manager.saveChanges();
This is quite cumbersome because I have a lot of properties and nested objects.
So, how can I use the objects created on server to save directly?
I don't have an idea of how you declared the dbContext inside the repository.
Let's say you have it declared this way :
public MyDBContext { get { return _contextProvider.Context; } }
Then you can add the _repository.MyDBContext.SaveChanges();
right before the line
return compounds.AsQueryable();
I'd like to create a generic C# class with a method that will add a row to a database using Entity Framework.
I have one table called Address. I've written the following code to add an address to the database:
public class AddressExchange
{
public int Insert(Address address)
{
using (var db = new DemoWebEntities())
{
//db.AddObject("Address", address);
db.Addresses.AddObject(address);
db.SaveChanges();
return address.Id;
}
}
}
I would like to write a generic class that will perform this operation for any entity in my EDMX. I think that it should look something like this:
public class EntityExchange<T, KeyType>
{
public KeyType Insert(T t)
{
using (var db = new DemoWebEntities())
{
// The entity set name might be wrong.
db.AddObject(typeof(T).Name, t);
// EF doesn't know what the primary key is.
return t.Id;
}
}
}
I think it may be possible to use the AddObject method to add the object to the database, but the entityset name is not necessarily the same as the type name, especially if it has been pluralized!
I also want to return the primary key to the caller, but I don't know how to tell which field contains the primary key.
I have a generic InsertOrUpdate method in a generic repository that also ensures proxies are created. (Proxies are required to support lazy loading and if you create an entity using "new", then proxies are not created). See the question here
public class RepositoryBase<T> : IRepository<T> where T : ModelBase
{
public virtual T InsertOrUpdate(T e)
{
DbSet<T> dbSet = context.Set<T>();
//Generate a proxy type to support lazy loading
T instance = dbSet.Create();
DbEntityEntry<T> entry;
if (e.GetType().Equals(instance.GetType()))
{
//The entity being added is already a proxy type that
//supports lazy loading just get the context entry
entry = context.Entry(e);
}
else
{
//The entity being added has been created using the "new" operator.
//Attach the proxy
//Need to set the ID before attaching or we get
//The property 'ID' is part of the object's key
//information and cannot be modified when we call SetValues
instance.ID = e.ID;
entry = context.Entry(instance);
dbSet.Attach(instance);
//and set it's values to those of the entity
entry.CurrentValues.SetValues(e);
e = instance;
}
entry.State = e.ID == default(int) ?
EntityState.Added :
EntityState.Modified;
return e;
}
}
public abstract class ModelBase
{
public int ID { get; set; }
}
Note that all the models inherit ModelBase so that handles the ID issue and I return the entity rather than just the ID. That is probably not strictly necessary since a reference to the entity is passed in and EF performs fixup on the ID anyway so you can always access it from the refernce passed in.
This might be reliant on a particular version on Entity framework however this is how I do it
public void Create(T entity)
{
using (var db = new DemoWebEntities())
{
db.Set<T>().Add(entity);
}
}
For the primary key issue, can you use partial classes to make your entities implement an interface, something like this:
public interface IEntity
{
Guid PrimaryKey { get; }
}
Your entity classes would then return the appropriate value:
public partial class EntityType : IEntity
{
public Guid PrimaryKey
{
get
{
return this.WhateverId; // Return the primary key
}
}
}
Then, constrain your method to only accept IEntity:
public class EntityExchange<T, KeyType> where T : IEntity
And finally return the primary key after the insert:
return t.PrimaryKey;
May be it can help you.
public T Add(T model)
{
using (BigConceptEntities entity = new BigConceptEntities())
{
entity.Set<T>().Add(model);
entity.SaveChanges();
return model;
}
}
I have an MVC application that uses Entity Framework 5. In few places I have a code that creates or updates the entities and then have to perform some kind of operations on the updated data. Some of those operations require accessing navigation properties and I can't get them to refresh.
Here's the example (simplified code that I have)
Models
class User : Model
{
public Guid Id { get; set; }
public string Name { get; set; }
}
class Car : Model
{
public Guid Id { get; set; }
public Guid DriverId { get; set; }
public virtual User Driver { get; set; }
[NotMapped]
public string DriverName
{
get { return this.Driver.Name; }
}
}
Controller
public CarController
{
public Create()
{
return this.View();
}
[HttpPost]
public Create(Car car)
{
if (this.ModelState.IsValid)
{
this.Context.Cars.Create(booking);
this.Context.SaveChanges();
// here I need to access some of the resolved nav properties
var test = booking.DriverName;
}
// error handling (I'm removing it in the example as it's not important)
}
}
The example above is for the Create method but I also have the same problem with Update method which is very similar it just takes the object from the context in GET action and stores it using Update method in POST action.
public virtual void Create(TObject obj)
{
return this.DbSet.Add(obj);
}
public virtual void Update(TObject obj)
{
var currentEntry = this.DbSet.Find(obj.Id);
this.Context.Entry(currentEntry).CurrentValues.SetValues(obj);
currentEntry.LastModifiedDate = DateTime.Now;
}
Now I've tried several different approaches that I googled or found on stack but nothing seems to be working for me.
In my latest attempt I've tried forcing a reload after calling SaveChanges method and requerying the data from the database. Here's what I've done.
I've ovewrite the SaveChanges method to refresh object context immediately after save
public int SaveChanges()
{
var rowsNumber = this.Context.SaveChanges();
var objectContext = ((IObjectContextAdapter)this.Context).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, this.Context.Bookings);
return rowsNumber;
}
I've tried getting the updated object data by adding this line of code immediately after SaveChanges call in my HTTP Create and Update actions:
car = this.Context.Cars.Find(car.Id);
Unfortunately the navigation property is still null. How can I properly refresh the DbContext immediately after modifying the data?
EDIT
I forgot to originally mention that I know a workaround but it's ugly and I don't like it. Whenever I use navigation property I can check if it's null and if it is I can manually create new DbContext and update the data. But I'd really like to avoid hacks like this.
class Car : Model
{
[NotMapped]
public string DriverName
{
get
{
if (this.Driver == null)
{
using (var context = new DbContext())
{
this.Driver = this.context.Users.Find(this.DriverId);
}
}
return this.Driver.Name;
}
}
}
The problem is probably due to the fact that the item you are adding to the context is not a proxy with all of the necessary components for lazy loading. Even after calling SaveChanges() the item will not be converted into a proxied instance.
I suggest you try using the DbSet.Create() method and copy across all the values from the entity that you receive over the wire:
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
this.Context.Entry(newEntry).CurrentValues.SetValues(obj);
return newEntry;
}
UPDATE
If SetValues() is giving an issue then I suggest you try automapper to transfer the data from the passed in entity to the created proxy before Adding the new proxy instance to the DbSet. Something like this:
private bool mapCreated = false;
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
if (!mapCreated)
{
Mapper.CreateMap(obj.GetType(), newEntry.GetType());
mapCreated = true;
}
newEntry = Mapper.Map(obj, newEntry);
this.DbSet.Add(newEntry;
return newEntry;
}
I use next workaround: detach entity and load again
public T Reload<T>(T entity) where T : class, IEntityId
{
((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);
return _dbContext.Set<T>().FirstOrDefault(x => x.Id == entity.Id);
}