I have the following code I created with help of asp.net mvc tutorials.
These are in a different class library I called Model.
I created an app.config in the class library with the connection string called HRContext.
Also on the webproject I created the same connection string.
When I run the project, I put breakpoints and the Seed method is never executed, however the OnModelCreating is being executed.
I got an exception saying that the table dbo.Position does not exist in the view.
Exception Details: System.Data.SqlClient.SqlException: Invalid object name 'dbo.Position'.
Line 21: var positions = unitOfWork.PositionRepository.Find(p => p.PositionID != null);
Line 22:
Line 23: return View(positions.ToList());
Line 24: }
public class HRContextInitializer : DropCreateDatabaseAlways<HRContext>
{
protected override void Seed(HRContext context)
{
Position netdeveloper = new Position() { name = ".net developer", yearsExperienceRequired = 5 };
Position javadeveloper = new Position() { name = "java developer", yearsExperienceRequired = 5 };
byte[] johnImage = File.ReadAllBytes(#"\Content\Photos\1.jpg");
byte[] luisImage = File.ReadAllBytes(#"\Content\Photos\2.jpg");
Applicant luis = new Applicant()
{
name = "Luis",
skypeuser = "le.valencia",
telephone = "0491732825",
photo = luisImage
};
Applicant john = new Applicant()
{
name = "John",
skypeuser = "jo.valencia",
telephone = "3435343543",
photo = johnImage
};
ApplicantPosition appicantposition = new ApplicantPosition()
{
Applicant = luis,
Position = netdeveloper,
appliedDate = DateTime.Today,
Status = Status.Applied
};
ApplicantPosition appicantposition2 = new ApplicantPosition()
{
Applicant = john,
Position = javadeveloper,
appliedDate = DateTime.Today,
Status = Status.Applied
};
context.Positions.Add(netdeveloper);
context.Positions.Add(javadeveloper);
context.Applicants.Add(luis);
context.Applicants.Add(john);
context.ApplicantsPositions.Add(appicantposition);
context.SaveChanges();
}
}
public class HRContext : DbContext
{
public DbSet<Position> Positions { get; set; }
public DbSet<Applicant> Applicants { get; set; }
public DbSet<ApplicantPosition> ApplicantsPositions { get; set; }
public DbSet<ApplicationPositionHistory> ApplicationsPositionHistory { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Position>().ToTable("Position");
modelBuilder.Entity<Applicant>().ToTable("Applicant");
modelBuilder.Entity<ApplicantPosition>().ToTable("ApplicantPosition");
modelBuilder.Entity<ApplicationPositionHistory>().ToTable("ApplicationsPositionHistory");
modelBuilder.Entity<Position>().Property(c => c.name).IsRequired();
modelBuilder.Entity<Applicant>().Property(c => c.name).IsRequired();
modelBuilder.Entity<ApplicantPosition>().Property(c => c.appliedDate).IsRequired();
base.OnModelCreating(modelBuilder);
}
}
You need to tell EF to use your custom inializer with the Database.SetInitializer method. The best to call it at your application start (if you are using MVC then in the Application_Start). But you should only call it once and before you access your context for the first time
protected void Application_Start()
{
...
Database.SetInitializer(new HRContextInitializer());
...
}
In your HRContext class you need to set database initializer:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new HTContextInitializer());
}
Related
I try to use EF core, but only a part of my model is saved to the database.
This is my model:
public class EngineType
{
public string Name { get; set; }
}
public class Car
{
public long CarId { get; set; }
public string Name { get; set; }
public EngineType Engine { get; set; }
}
The CarId and the Name is saved, but not the EngineType.
This is the test I use, but actual.Engine is always null:
[TestMethod]
public void WhenIAddAndSaveANewCarThenItIsAddedToDB()
{
using var target = new EFCoreExampleContext();
using var concurrentContext = new EFCoreExampleContext();
var expected = new Car() {CarId = 0815, Name = "Isetta", Engine = new EngineType() { Name = "2Takt" }};
target.Cars.Add(expected);
target.SaveChanges();
var actual = concurrentContext.Cars.Single();
Assert.AreEqual(1, concurrentContext.Cars.Count());
Assert.IsNotNull(actual.Engine);
Assert.AreEqual(expected, actual);
}
My Context looks like this:
public class EFCoreExampleContext : DbContext
{
public DbSet<Car> Cars { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase(databaseName: "Add_writes_to_database");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EngineType>(
d =>
{
d.HasKey(e => e.Name);
d.Property(e => e.Name).IsRequired();
});
modelBuilder.Entity<EngineType>(
d =>
{
d.HasKey(e => e.Name);
});
modelBuilder.Entity<Car>(
d =>
{
d.HasKey(e => e.CarId);
d.Property<DateTime>("LastChanged").IsRowVersion().ValueGeneratedOnAddOrUpdate();
d.Property<string>("EngineForeignKey");
d.HasOne(e => e.Engine)
.WithMany()
.HasForeignKey("EngineForeignKey")
.IsRequired();
});
}
}
Any idea what am I doing wrong (or which existing topic answers this question - I even didn't have the right search words to find it).
Thanks!
I think there is no issue with saving. Entity Framework does not do eager loading by default. So you have to explicitly include any navigational properties that should be in result. Try this when you are fetching actual,
using Microsoft.EntityFrameworkCore;
var actual = concurrentContext.Cars.Include(c => c.Engine).Single();
How do you set owned type instance with efcore3?
In following example an exception is raised
'The entity of type 'Owned' is sharing the table 'Principals' with
entities of type 'Principal', but there is no entity of this type with
the same key value that has been marked as 'Added'.
If I set Child property inline savechanges doesn't update child properties
I can't find any example about this. I tried with several efcore3 builds and daily builds. What didn't I understand?
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace TestEF
{
class Program
{
static void Main(string[] args)
{
var id = Guid.NewGuid();
using (var db = new Ctx())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var p = new Principal {Id = id};
db.Principals.Add(p);
db.SaveChanges();
}
using (var db = new Ctx())
{
var p = db.Principals.Single(o => o.Id == id);
p.Child = new Owned();
p.Child.Prop1 = "Test2";
p.Child.Prop2 = "Test2";
db.SaveChanges();
}
}
public class Principal
{
public Guid Id { get; set; }
public Owned Child { get; set; }
}
public class Owned
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
public class Ctx : DbContext
{
public DbSet<Principal> Principals { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=.;Initial Catalog=TestEF;Trusted_Connection=True;Persist Security Info=true");
}
protected override void OnModelCreating(ModelBuilder mb)
{
var emb = mb.Entity<Principal>();
emb
.OwnsOne(o => o.Child, cfg =>
{
cfg.Property(o => o.Prop1).HasMaxLength(30);
//cfg.WithOwner();
});
}
}
}
}
This is a bug, filed at https://github.com/aspnet/EntityFrameworkCore/issues/17422
As a workaround you could make the child appear as modified:
db.ChangeTracker.DetectChanges();
var childEntry = db.Entry(p.Child);
childEntry.State = EntityState.Modified;
db.SaveChanges();
Try this instead:
_context.Update(entity);
This will update all the owned properties so SaveChanges() updates them, too.
I have a model as below
public class Lesson
{
public int Id { get; set; }
public Section Div { get; set; }
}
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
}
I also have DB Context as below
public class MyContext : DbContext
{
public MyContext() : base("DefaultConnection")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Lesson> Lessons { get; set; }
public DbSet<Section> Sections { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Then I use the following code to call the database
using (MyContext c = new EFTest.MyContext())
{
Lesson d = new EFTest.Lesson();
Section ed = new EFTest.Section() { Name = "a" };
d.Div = ed;
c.Entry(d.Div).State = EntityState.Detached;
c.Lessons.Add(d);
c.SaveChanges();
}
I am expecting this code to save just the Lesson object, not to save the full graph of Lesson and Section, but what happens is that it saves the full graph.
How do I prevent it from doing that?
When you add an entity to DbSet, entityframework will add all of its relative. You need to detach the entity you don't want to add, after adding parent entity to DbSet.
using (MyContext c = new EFTest.MyContext())
{
Lesson d = new EFTest.Lesson();
Section ed = new EFTest.Section() { Name = "a" };
d.Div = ed;
c.Lessons.Add(d);
c.Entry(d.Div).State = EntityState.Detached;
c.SaveChanges();
}
if you want to add section, related to the lesson , you need to use the same context, or create a new context and load the lesson.
you can use this code
using (MyContext c = new EFTest.MyContext())
{
Lesson d = new EFTest.Lesson();
Section ed = new EFTest.Section() { Name = "a" };
d.Div = ed;
c.Lessons.Add(d);
c.Entry(d.Div).State = EntityState.Detached;
c.SaveChanges();
//you can use this code
ed.Lesson = d;
// or this code
d.Div = ed;
c.Sections.Add(ed);
c.SaveChanges();
}
I am developing a .Net project. I am using entity framework code first approach to interact with database. I am seeding some mock data to my database during development. But seeding is not working. I followed this link - http://www.entityframeworktutorial.net/code-first/seed-database-in-code-first.aspx.
This is my ContextInitializer class
public class ContextInitializer : System.Data.Entity.CreateDatabaseIfNotExists<StoreContext>
{
protected override void Seed(StoreContext context)
{
IList<Brand> brands = new List<Brand>();
brands.Add(new Brand { Name = "Giordano" ,TotalSale = 1 });
brands.Add(new Brand { Name = "Nike" , TotalSale = 3 });
foreach(Brand brand in brands)
{
context.Brands.Add(brand);
}
base.Seed(context);
context.SaveChanges();
}
}
This is my context class
public class StoreContext : DbContext,IDisposable
{
public StoreContext():base("DefaultConnection")
{
Database.SetInitializer(new ContextInitializer());
}
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Brand> Brands { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
This is my brand class
public class Brand
{
public int Id { get; set; }
[Required]
[MaxLength(40)]
public string Name { get; set; }
public int TotalSale { get; set; }
}
I searched solutions online and I followed instructions. I run context.SaveChanges as well. But it is not seeding data to database. Why it is not working?
You are taking the wrong initializer, CreateDatabaseIfNotExists is called only if the database not exists!
You can use for example DropCreateDatabaseIfModelChanges:
Solution 1)
public class ContextInitializer : System.Data.Entity.DropCreateDatabaseIfModelChanges<StoreContext>
{
You have to take care with this approach, it !!!removes!!! all existing data.
Solution 2)
Create a custom DbMigrationsConfiguration:
public class Configuration : DbMigrationsConfiguration<StoreContext>
{
public Configuration()
{
// Take here! read about this property!
this.AutomaticMigrationDataLossAllowed = true;
this.AutomaticMigrationsEnabled = false;
}
protected override void Seed(StoreContext context)
{
IList<Brand> brands = new List<Brand>();
brands.Add(new Brand { Name = "Giordano", TotalSale = 1 });
brands.Add(new Brand { Name = "Nike", TotalSale = 3 });
foreach (Brand brand in brands)
{
context.Brands.AddOrUpdate(m => m.Name, brand);
}
base.Seed(context);
context.SaveChanges();
}
}
In this way you can called( !!Before you create the DbContext or in the DbContext constructor!!):
// You can put me also in DbContext constuctor
Database.SetInitializer(new MigrateDatabaseToLatestVersion<StoreContext , Yournamespace.Migrations.Configuration>("DefaultConnection"));
Notes:
DbMigrationsConfiguration need to know about the connection string you can provide this info in the constructor or from outside.
In Your DbMigrationsConfiguration you can configure also:
MigrationsNamespace
MigrationsAssembly
MigrationsDirectory
TargetDatabase
If you leave everything default as in my example then you do not have to change anything!
Setting the Initializer for a Database has to happen BEFORE the context is ever created so...
public StoreContext():base("DefaultConnection")
{
Database.SetInitializer(new ContextInitializer());
}
is much to late. If you made it static, then it could work:
static StoreContext()
{
Database.SetInitializer(new ContextInitializer());
}
Your code is working if you delete your existing database and the EF will create and seeding the data
Or
You can use DbMigrationsConfiguration insted of CreateDatabaseIfNotExists and change your code as follow:
First you have to delete the existing database
ContextInitializer class
public class ContextInitializer : System.Data.Entity.Migrations.DbMigrationsConfiguration<StoreContext>
{
public ContextInitializer()
{
this.AutomaticMigrationDataLossAllowed = true;
this.AutomaticMigrationsEnabled = true;
}
protected override void Seed(StoreContext context)
{
IList<Brand> brands = new List<Brand>();
brands.Add(new Brand { Name = "Giordano", TotalSale = 1 });
brands.Add(new Brand { Name = "Nike", TotalSale = 3 });
foreach (Brand brand in brands)
{
context.Brands.AddOrUpdate(m => m.Name, brand);
}
base.Seed(context);
context.SaveChanges();
}
}
StoreContext
public class StoreContext : DbContext, IDisposable
{
public StoreContext() : base("DefaultConnection")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<StoreContext, ContextInitializer>());
// Database.SetInitializer(new ContextInitializer());
}
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Brand> Brands { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Then any change in your seed will automatically reflected to your database
I'm trying to populate my database using Entity Framework. I'm using the Seed override and using set initializer from my DbContext. When I create my database from scratch, it doesn't seem to add these values in!
public class PokemonDatabaseInitializer : CreateDatabaseIfNotExists<PkmnContext>
{
protected override void Seed(PkmnContext context)
{
var pkm = new Pokemon
{
Id = 25,
DisplayName = "Pikachu",
RegionId = 1
};
var region = new PokemonRegion
{
Id = 1,
Kanto = true,
};
var location = new PokemonLocation
{
AreaFound = "Viridian Forest",
Id = 1
};
context.Pokemons.Add(pkm);
context.PokemonRegions.Add(region);
context.PokemonLocations.Add(location);
context.SaveChanges();
base.Seed(context);
}
}
public class PkmnContext : DbContext
{
public PkmnContext()
{
Database.SetInitializer(new PokemonDatabaseInitializer());
}
public DbSet<Pokemon> Pokemons { get; set; }
public DbSet<PokemonRegion> PokemonRegions { get; set; }
public DbSet<PokemonLocation> PokemonLocations { get; set; }
}
you need to call
update-database
from package manager console. It also possible to call it from code after any migrations were applied
Please note that it will seed data each time you call it so i would add some checks if items exists already