Separate copy of DbContext class for unit testing? - entity-framework-core

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());
}
}
}

Related

EF Core 3.1: Navigation property doesn't lazy load entities when calling the backing field first

I am using EF Core 3.1.7. The DbContext has the UseLazyLoadingProxies set. Fluent API mappings are being used to map entities to the database. I have an entity with a navigation property that uses a backing field. Loads and saves to the database seem to work fine except for an issue when accessing the backing field before I access the navigation property.
It seems that referenced entities don't lazy load when accessing the backing field. Is this a deficiency of the Castle.Proxy class or an incorrect configuration?
Compare the Student class implementation of IsRegisteredForACourse to the IsRegisteredForACourse2 for the behavior in question.
Database tables and relationships.
Student Entity
using System.Collections.Generic;
namespace EFCoreMappingTests
{
public class Student
{
public int Id { get; }
public string Name { get; }
private readonly List<Course> _courses;
public virtual IReadOnlyList<Course> Courses => _courses.AsReadOnly();
protected Student()
{
_courses = new List<Course>();
}
public Student(string name) : this()
{
Name = name;
}
public bool IsRegisteredForACourse()
{
return _courses.Count > 0;
}
public bool IsRegisteredForACourse2()
{
//Note the use of the property compare to the previous method using the backing field.
return Courses.Count > 0;
}
public void AddCourse(Course course)
{
_courses.Add(course);
}
}
}
Course Entity
namespace EFCoreMappingTests
{
public class Course
{
public int Id { get; }
public string Name { get; }
public virtual Student Student { get; }
protected Course()
{
}
public Course(string name) : this()
{
Name = name;
}
}
}
DbContext
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace EFCoreMappingTests
{
public sealed class Context : DbContext
{
private readonly string _connectionString;
private readonly bool _useConsoleLogger;
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
public Context(string connectionString, bool useConsoleLogger)
{
_connectionString = connectionString;
_useConsoleLogger = useConsoleLogger;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddFilter((category, level) =>
category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information)
.AddConsole();
});
optionsBuilder
.UseSqlServer(_connectionString)
.UseLazyLoadingProxies();
if (_useConsoleLogger)
{
optionsBuilder
.UseLoggerFactory(loggerFactory)
.EnableSensitiveDataLogging();
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>(x =>
{
x.ToTable("Student").HasKey(k => k.Id);
x.Property(p => p.Id).HasColumnName("Id");
x.Property(p => p.Name).HasColumnName("Name");
x.HasMany(p => p.Courses)
.WithOne(p => p.Student)
.OnDelete(DeleteBehavior.Cascade)
.Metadata.PrincipalToDependent.SetPropertyAccessMode(PropertyAccessMode.Field);
});
modelBuilder.Entity<Course>(x =>
{
x.ToTable("Course").HasKey(k => k.Id);
x.Property(p => p.Id).HasColumnName("Id");
x.Property(p => p.Name).HasColumnName("Name");
x.HasOne(p => p.Student).WithMany(p => p.Courses);
});
}
}
}
Test program which demos the issue.
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Linq;
namespace EFCoreMappingTests
{
class Program
{
static void Main(string[] args)
{
string connectionString = GetConnectionString();
using var context = new Context(connectionString, true);
var student2 = context.Students.FirstOrDefault(q => q.Id == 5);
Console.WriteLine(student2.IsRegisteredForACourse());
Console.WriteLine(student2.IsRegisteredForACourse2()); // The method uses the property which forces the lazy loading of the entities
Console.WriteLine(student2.IsRegisteredForACourse());
}
private static string GetConnectionString()
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
return configuration["ConnectionString"];
}
}
}
Console Output
False
True
True
When you declare a mapped property in an EF entity as virtual, EF generates a proxy which is capable of intercepting requests and assessing whether the data needs to be loaded. If you attempt to use a backing field before that virtual property is accessed, EF has no "signal" to lazy load the property.
As a general rule with entities you should always use the properties and avoid using/accessing backing fields. Auto-initialization can help:
public virtual IReadOnlyList<Course> Courses => new List<Course>().AsReadOnly();

issue with new create dbcontext class object in asp.net core 2.1

I m new in .net core 2.1
I m working with .net core 2.1 with code first approach
issue is when I create a new object dbcontext class then give error see below line
dbcontextstudent db=new dbcontextstudent(); //here give an red line
appsettings.json
},
"ConnectionStrings": {
"sqlserverconn": "Server=DEVISSHAHID; Database=studdbs; User id=xxxx;Password=xxxxx;"
},
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//connection string
services.AddDbContext<DbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("sqlserverconn")));
student.cs
namespace WebApplication1.Models
{
public class student
{
[Key]
public int studid { get; set; }
public string studname { get; set; }
public string studsalary { get; set; }
public int studage { get; set; }
}
}
dbcontextstudent.cs
namespace WebApplication1.Models
{
public class dbcontextstudent : DbContext
{
public dbcontextstudent(DbContextOptions<dbcontextstudent> options) : base(options)
{
}
public DbSet<student> stud { get; set; }
}
}
HomeController.cs
I m not understood the above intellisense
I write the code as per intellisense but still give an error I know error is clear but not solved
which place doing I m wrong?
You will have to pass your DbContext type to the AddDbContext method in ConfigureServices method like this:
services.AddDbContext<dbcontextstudent>(options => options.UseSqlServer(Configuration.GetConnectionString("sqlserverconn")));
After that, you have registered the dbcontextstudent class in dependency injection.
You shouldn't create the instance of dbcontextstudent on your own like you did:
dbcontextstudent db=new dbcontextstudent();
Instead you can inject it though the constructor of your controller like this:
public HomeController : Controller
{
private readonly dbcontextstudent _db;
public HomeController(dbcontextstudent db)
{
_db = db;
}
... and then you can use the _db variable in your post action
}

Entity Framework CodeFirst, Add Dbset to DbContext, programmatically

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
}
}

EF5 Code first TPH Mapping error using DBSet.Find()

When using Entity Framework 5 Code First, with Table Per Hierarchy.
This combined with a Repository and Unit of Work (tried several implementations).
I'm having the following error:
(34,10) : error 3032: Problem in mapping fragments starting at lines 19, 34:EntityTypes T, T are being mapped to the same rows in table T. Mapping conditions can be used to distinguish the rows that these types are mapped to.
I have resolved this issue using the following guide:
Entity Framework 4.3 - TPH mapping and migration error
This works when using a general look-up of all records, then no errors.
When using the DBSet<T>.Find(id), I receive the above error message.
When using DBSet<T>.Where(t => t.id == id) all works fine.
Please does anyone have the solution for this problem?
public class TDataContext : DbContext
{
// Models
public abstract class BaseTrackable
{
public DateTime DateModified { get; set; }
}
public abstract class ParentClass : BaseTrackable
{
public int ParentId { get; set; }
public string ParentString { get; set; }
}
public class Foo : ParentClass
{
public string FooString { get; set; }
}
public class Bar : ParentClass
{
public string BarString { get; set; }
}
// Configuration
public class ParentConfiguration : EntityTypeConfiguration<ParentClass>
{
public ParentConfiguration()
{
ToTable("Parent");
}
}
public class FooConfiguration : EntityTypeConfiguration<Foo>
{
public FooConfiguration()
{
Map(m => m.Requires("FooIndicator").HasValue(true));
}
}
public class BarConfiguration : EntityTypeConfiguration<Bar>
{
public BarConfiguration()
{
Map(m => m.Requires("BarIndicator").HasValue(true));
}
}
public DbSet<ParentClass> Parent { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations
.Add(new ParentConfiguration())
.Add(new FooConfiguration())
.Add(new BarConfiguration());
}
}
public class Controller
{
TDataContext _context = new TDataContext();
// Repository function
public T GetById<T>(object id) where T : class
{
var dbset = _context.Set<T>();
return dbset.Find(id);
}
public IQueryable<TDataContext.Foo> GetFiltered(Expression<Func<TDataContext.Foo, bool>> filter)
{
var dbset = _context.Set<TDataContext.Foo>();
return dbset.Where(filter);
}
// Final call
// Which fails..
public TDataContext.Foo Get(int id)
{
return this.GetById<TDataContext.Foo>(id);
}
// This works...
public TDataContext.Foo GetWhere(int id)
{
return this.GetFiltered(f => f.ParentId == id).FirstOrDefault();
}
}
Found something that solves my problem partially...
When adding another indicator to the tables, there is no more error, example:
public class FooConfiguration : EntityTypeConfiguration<Foo>
{
public FooConfiguration()
{
Map(m => {
m.Requires("FooIndicator").HasValue(true);
m.Requires("BarIndicator").HasValue<short>(1);
});
}
}
public class BarConfiguration : EntityTypeConfiguration<Bar>
{
public BarConfiguration()
{
Map(m => {
m.Requires("BarIndicator").HasValue(true);
m.Requires("FooIndicator").HasValue<short>(0);
});
}
}
Wouldn't be better
public class FooConfiguration : EntityTypeConfiguration<Foo>
{
public FooConfiguration()
{
Map(m => m.Requires("Type").HasValue("Foo"));
}
}
public class BarConfiguration : EntityTypeConfiguration<Bar>
{
public BarConfiguration()
{
Map(m => m.Requires("Type").HasValue("Bar");
}
}
In this way FooConfiguration doesn't need to know anything about BarConfiguration and visa versa. I had this issue when migrating from EF 4.3 to 5.0 and I think what has changed was the discriminator database columns are not nullable in EF 5.0. I think it makes much more sense for them to be not nullable and in general it might be better to have only one discrimanotor column for each derived type as opposed to one column per type (as it was in EF 4.3)
-Stan

MVC3 + Ninject + Entity framework 4

i have this Dependency resolver
public class NinjectDependencyResolvercs : IDependencyResolver
{
private readonly IResolutionRoot resolutionRoot;
public NinjectDependencyResolvercs(IResolutionRoot kernel)
{
resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
return resolutionRoot.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return resolutionRoot.GetAll(serviceType);
}
}
in global.asax.cs
// Ninject DI container ----------------------------------------------------------- |
public void SetupDependencyInjection()
{
// Create Ninject DI kernel
IKernel kernel = new StandardKernel();
#region Register services with Ninject DI Container
// DbContext to SqlDataContext
kernel.Bind<DbContext>()
.To<SqlDataContext>();
// IRepository to SqlRepository
kernel.Bind<IRepository>()
.To<SqlRepository>();
// IUsersServices to UsersServices
kernel.Bind<IUsersServices>()
.To<UsersServices>();
// IMessagesServices to MessagesServices
kernel.Bind<IMessagesServices>()
.To<MessagesServices>();
// IJobAdvertsServices to JobAdvertsServices
kernel.Bind<IJobAdvertsServices>()
.To<JobAdvertsServices>();
#endregion
// Tell ASP.NET MVC 3 to use Ninject DI Container
DependencyResolver.SetResolver(new NinjectDependencyResolvercs(kernel));
}
// --------------------------------------------------------------------------------- |
and class
public class SqlDataContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Profile> Profiles { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<JobAdvert> JobAdverts { get; set; }
public DbSet<Message> Messages { get; set; }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(x => x.Roles).WithMany(x => x.Users).Map(x =>
{
x.MapLeftKey(y => y.UserId, "UserId");
x.MapRightKey(y => y.RoleId, "RoleId");
x.ToTable("UsersInRoles");
});
base.OnModelCreating(modelBuilder);
}
}
all dependecies work fine but for DbContext to SqlDataContext is problem. If use this:
public class SqlRepository
{
private DbContext dataContext;
public SqlRepository(DbContext dataContext) {
this.dataContext = dataContext;
}
public DbSet<User> Users {
get {
return dataContext.Users;
}
}
}
then
dataContext.Users
and all others properties alert this error:
'System.Data.Entity.DbContext' does not contain a definition for 'JobAdverts' and no extension method 'JobAdverts' accepting a first argument of type 'System.Data.Entity.DbContext' could be found (are you missing a using directive or an assembly reference?)
Have anyone any idea why DI doent work for Class DbContext ?
If I understand correctly, You're injecting DbContext which doesn't have those methods/properties, as they're declared in the derived type SqlDataContext.
You need to inject the SqlDataContext. If you want to use an interface, you'll need to extract an interface from SqlDataContext.
EDIT:
Ninject binds at runtime while the errors you're getting (I presume) are at compile time. You could get around this by using the dynamic key word, but that's just working AROUND the problem.
public class SqlRepository
{
private dynamic dataContext;
public SqlRepository(DbContext dataContext) {
this.dataContext = dataContext;
}
...
}
What you need to do is change the signature to use your SqlDataContext:
public class SqlRepository
{
private SqlDataContextdata Context;
public SqlRepository(SqlDataContextdata Context) {
this.dataContext = dataContext;
}
...
}
because DbContext does not contain those methods, only your SqlContext does. and your sqlcontext is bound to DbContext at runtime.