Entity Framework CodeFirst, Add Dbset to DbContext, programmatically - entity-framework

how can i Add DbSet to my dbContext class, programmatically.
[
public class MyDBContext : DbContext
{
public MyDBContext() : base("MyCon")
{
Database.SetInitializer<MyDBContext>(new CreateDatabaseIfNotExists<MyDBContext>());
}
//Do this part programatically:
public DbSet<Admin> Admins { get; set; }
public DbSet<MyXosh> MyProperty { get; set; }
}
][1]
i want to add my model classes by ((C# Code-DOM)) and of course i did. but now i have problem with creating DbSet properties inside my Context class ...

yes i did!..
this: https://romiller.com/2012/03/26/dynamically-building-a-model-with-code-first/
And this: Create Table, Run Time using entity framework Code-First
are solution. no need to dispute with dbSets directly. it just works by do some thing like that:
public class MyDBContext : DbContext
{
public MyDBContext() : base("MyCon")
{
Database.SetInitializer<MyDBContext>(new CreateDatabaseIfNotExists<MyDBContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
var theList = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.Namespace == "FullDynamicWepApp.Data.Domins")
.ToList();
foreach (var item in theList)
{
entityMethod.MakeGenericMethod(item)
.Invoke(modelBuilder, new object[] { });
}
base.OnModelCreating(modelBuilder);
}
}

For those using EF Core that stubble here:
The code below is only for one table with the generic type. If you want more types you can always pass them through the constructor and run a cycle.
public class TableContextGeneric<T> : DbContext where T : class
{
private readonly string _connectionString;
//public virtual DbSet<T> table { get; set; }
public TableContextGeneric(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var entityMethod = typeof(ModelBuilder).GetMethods().First(e => e.Name == "Entity");
//the cycle will be run here
entityMethod?.MakeGenericMethod(typeof(T))
.Invoke(modelBuilder, new object[] { });
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString); // can be anyone
}
}

Related

Separate copy of DbContext class for unit testing?

I have a CatalogDbContext class.
I want to use Bogus library to seed fake data into the database that my unit tests will use.
The example provided in bogus's github repo makes use of the HasData method of the CatalogDbContext class to seed data into the tables.
However, I will not want this HasData method to be executed from the API - meaning, the HasData method should only be run if the DBContext is created from the Unit Tests.
Kindly advise how to achieve this?.
using Bogus;
using Catalog.Api.Database.Entities;
using Microsoft.EntityFrameworkCore;
namespace Catalog.Api.Database
{
public class CatalogDbContext : DbContext
{
public CatalogDbContext(DbContextOptions<CatalogDbContext> options) : base(options)
{
}
public DbSet<CatalogItem> CatalogItems { get; set; }
public DbSet<CatalogBrand> CatalogBrands { get; set; }
public DbSet<CatalogType> CatalogTypes { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new CatalogBrandEntityTypeConfiguration());
builder.ApplyConfiguration(new CatalogTypeEntityTypeConfiguration());
builder.ApplyConfiguration(new CatalogItemEntityTypeConfiguration());
FakeData.Init(10);
builder.Entity<CatalogItem>().HasData(FakeData.CatalogItems);
}
}
internal class FakeData
{
public static List<CatalogItem> CatalogItems = new List<CatalogItem>();
public static void Init(int count)
{
var id = 1;
var catalogItemFaker = new Faker<CatalogItem>()
.RuleFor(ci => ci.Id, _ => id++)
.RuleFor(ci => ci.Name, f => f.Commerce.ProductName());
}
}
}

Seeding not working in Entity Framework Code First Approach

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

Autofac using Constructor

I use unit of work pattern with entityFramework code first. Now I want to use Autofac to register UnitOfWork, Repositories and My dbContext.
This Is my UnitOfWork code:
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(DbContext context)
{
_context = context;
Contact = new ContractRepository(context);
}
public void Dispose()
{
_context.Dispose();
GC.SuppressFinalize(_context);
}
public IContactRepository Contact { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
}
and this is my repository:
public class Repository<Entity> : IRepository<Entity> where Entity : class
{
protected readonly DbContext _noteBookContext;
public Repository(DbContext noteBookContext)
{
_noteBookContext = noteBookContext;
}
public void Add(Entity entity)
{
_noteBookContext.Set<Entity>().Add(entity);
}
}
and this is one of my repositories:
public class ContractRepository: Repository<Contact>,IContactRepository
{
public ContractRepository(DbContext noteBookContext) : base(noteBookContext)
{
}
public DbContext NotebookContext
{
get
{
return _noteBookContext;
}
}
}
and this is my db context class:
public class NoteBookContext:DbContext
{
public NoteBookContext(string connectionstring):base(connectionstring)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ContactConfig());
modelBuilder.Configurations.Add(new PhoneConfig());
modelBuilder.Configurations.Add(new PhoneTypeConfig());
modelBuilder.Configurations.Add(new GroupConfig());
base.OnModelCreating(modelBuilder);
}
public DbSet<Contact> Contacts { get; set; }
public DbSet<Phone> Phones { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<PhoneType> PhoneTypes { get; set; }
}
Now I want to register UnitOfWork with constructor (a constructor like this: )
var uow = new UnitOfWork(new NotebookdbContext("connectionstring"));
Note that NoteBookContext is my entity framework model.
I write registration but I got Error:
var builder = new ContainerBuilder();
builder.RegisterType<NoteBookContext>()
.As<DbContext>();
builder.RegisterType<UnitOfWork>()
.UsingConstructor(typeof(DbContext))
.As<IUnitOfWork>();
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
Container container = builder.Build();
This is my error:
An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll
Additional information: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'DataLayer.NoteBookContext' can be invoked with the available services and parameters:
Cannot resolve parameter 'System.String connectionstring' of
constructor 'Void .ctor(System.String)'.
Edit 2 :
after help from Cyril Durand's answer I write following registering config:
var builder = new ContainerBuilder();
builder.RegisterType<ConnectionStringProvider>().As<IConnectionStringProvider>();
builder.RegisterType<NoteBookContext>().As<DbContext>().WithParameter((pi, c) => pi.Name == "connectionstring",
(pi, c) => c.Resolve<IConnectionStringProvider>().ConnectionString);
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().WithParameter(ResolvedParameter.ForNamed<DbContext>("connectionstring"));
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
and In my code :
using (var scope = DependencyInjection.Container.BeginLifetimeScope())
{
var ConnectionString = scope.Resolve<IConnectionStringProvider>();
ConnectionString.ConnectionString = "Context";
var uw = scope.Resolve<IUnitOfWork>();
var a =uw.Contact.GetAll();
}
but I got Error again:
An unhandled exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll
Additional information: An exception was thrown while invoking the
constructor 'Void .ctor(System.String)' on type 'NoteBookContext'.
can everyone help me?
The error message :
An unhandled exception of type Autofac.Core.DependencyResolutionException occurred in Autofac.dll
Additional information: None of the constructors found with Autofac.Core.Activators.Reflection.DefaultConstructorFinder on type DataLayer.NoteBookContext can be invoked with the available services and parameters:
Cannot resolve parameter System.String connectionstring of constructor Void .ctor(System.String).
tells you that Autofac can't create a NoteBookContext because it can't resolve a parameter named connectionstring of type String.
Your NoteBookContext implementation needs a connectionstring, Autofac can't know it without you telling it. When you register the NoteBookContext you will have to specify the connectionstring :
builder.RegisterType<NoteBookContext>()
.As<DbContext>()
.WithParameter("connectionstring", "XXX");
Another solution with dynamic resolution and a IConnectionStringProvider interface :
public interface IConnectionStringProvider
{
public String ConnectionString { get; }
}
and registration :
builder.RegisterType<ConnectionStringProvider>()
.As<IConnectionStringProvider>()
.InstancePerLifetimeScope();
builder.RegisterType<NoteBookContext>()
.As<DbContext>()
.WithParameter((pi, c) => pi.Name == "connectionstring",
(pi, c) => c.Resolve<IConnectionStringProvider>().ConnectionString)
.InstancePerLifetimeScope();
It's hard to tell without seeing error. But You don't need to use UsingConstructor.
//Make DbContext per request, if your app is web app (which has http request).
builder.RegisterType<NoteBookContext>()
.As<DbContext>().WithParameter("connectionstring","ConnectionStringValue").InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
Container = builder.Build();
Is there a reason you want to pass the connection string to your context? Create an interface for your unitofwork and do something like this:
public class NoteBookContext:DbContext
{
//Change connectionstring below with the name of your connection string in web.config
public NoteBookContext():base("name=connectionstring")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ContactConfig());
modelBuilder.Configurations.Add(new PhoneConfig());
modelBuilder.Configurations.Add(new PhoneTypeConfig());
modelBuilder.Configurations.Add(new GroupConfig());
base.OnModelCreating(modelBuilder);
}
public DbSet<Contact> Contacts { get; set; }
public DbSet<Phone> Phones { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<PhoneType> PhoneTypes { get; set; }
}
And register like this:
var builder = new ContainerBuilder();
builder.RegisterType<NoteBookContext>()
.As<DbContext>()
.InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>()
.As<IUnitOfWork>()
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
Container container = builder.Build();

dbcontext - non dbo owner

I'm using EF 5 to connect to my tables, but my tables don't have dbo as the owner. EF 5 queries insert dbo as the default owner. Can you tell me how to override this? Here are some code snippets:
public class MessageBoardContext : DbContext
{
public MessageBoardContext()
: base("DefaultConnection")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
Database.SetInitializer(
new MigrateDatabaseToLatestVersion<MessageBoardContext, MessageBoardMigrationsConfiguration>()
);
}
public DbSet<Topic> Topics { get; set; }
public DbSet<Reply> Replies { get; set; }
}
public class MessageBoardRepository : IMessageBoardRepository
{
MessageBoardContext _ctx;
public MessageBoardRepository(MessageBoardContext ctx)
{
_ctx = ctx;
}
public IQueryable<Topic> GetTopics()
{
return _ctx.Topics; //Uses dbo.Topics here! Which I don't want.
}
}
Found it! Here is the link:
http://devproconnections.com/entity-framework/working-schema-names-entity-framework-code-first-design
Here is a quick code snippet:
public class OrderingContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().ToTable("Customers", schemaName: "Ordering");
}}

Breeze EF SaveChanges() on a DTO

I've been struggling with Breeze to SaveChanges to a projection and admit I new to both EF and breeze. There were some similar questions earlier when I was trying to use WCF, but now I have abandoned WCF and added EF directly to my solution.
In my controller I return the DTO for the metadata to breeze along with the DTO and it binds perfectly.
After altering the data on the client my Breese Controllers [HttpPost] SaveChanges(save Bundle) is called and map contains the DTO and the changes.
How Do I persist the Changes? If I re-read the DTO projection for breeze to update then EF cant save a projection because it's not "tracked", if I read the Full entity, then Breeze error with "Sequence contains no matching element" because its looking for the DTO? Am I suppose to use AutoMapper?
Controller:
[BreezeController]
public class BreezeController : ApiController
{
readonly EFContextProvider<ManiDbContext> _contextProvider = new EFContextProvider<ManiDbContext>();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpGet]
public IQueryable<ConsigneDTO> Consignee(string refname)
{
return _contextProvider.Context.consigneDTO(refname);
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
ManiDbContextProvider _mcontextProvider = new ManiDbContextProvider();
return _mcontextProvider.SaveChanges(saveBundle);
}
ManiDbContext (the main DBContext is CifContext which is Database First/EF Reverse Engineer)
public class ManiDbContext : DbContext
{
public CifContext CifDbContext = new CifContext();
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
Database.SetInitializer<ManiDbContext>(null);
modelBuilder.Configurations.Add(new ConsigneDTOMap());
}
public override int SaveChanges()
{
CifDbContext.SaveChanges();
return 1;
}
public IQueryable<ConsigneDTO> consigneDTO(string refname)
{
IQueryable<ConsigneDTO> q = this.CifDbContext.Consignes
.Where(x => x.Refname == refname)
.Select(f => new ConsigneDTO {Refname = f.Refname, Consignee = f.Consignee, Address1 = f.Address1, Address2 = f.Address2, Address3 = f.Address3});
return q;
}
ManiDbContextProvider
public class ManiDbContextProvider : EFContextProvider<CifContext>
// public class ManiDbContextProvider : EFContextProvider<ManiDbContext>
{
public ManiDbContextProvider() : base() { }
protected override void OpenDbConnection()
{// do nothing
}
protected override void CloseDbConnection()
{ // do nothing
}
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
var entity = entityInfo.Entity;
if (entity is ConsigneDTO)
{
return BeforeSaveConsignee(entity as ConsigneDTO, entityInfo);
}
throw new InvalidOperationException("Cannot save entity of unknown type");
}
private bool BeforeSaveConsignee(ConsigneDTO c, EntityInfo info)
{
var consdata = this.Context.CifDbContext.Consignes
.Where(x => x.Refname == c.Refname)
.FirstOrDefault(); // ENTITY
// var consdata = this.Context.consigneDTO(c.Refname); // DTO
return (null != consdata) || throwCannotFindConsignee();
}
CifContext (Full Columns - First/EF Reverse Engineer/ Consigne class contains Keys)
public partial class CifContext : DbContext
{
static CifContext()
{
Database.SetInitializer<CifContext>(null);
}
public CifContext()
: base("Name=CifContext")
{
}
public DbSet<Consigne> Consignes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); // Use singular table names
Database.SetInitializer<CifContext>(null);
modelBuilder.Configurations.Add(new ConsigneMap());
}
Regardless if I Read the Entity or the DTO - I'm Clueless on how breeze updates EF
Any Help greatly appreciated :)
Regards,
Mike