Error when trying to 'Seed' mock db into mock dbContext - c#

I've been searching stack overflow for a solution to this problem but I haven't been able to find that one that fixes my error yet. I'm attempting to write unit test for an API that I developed. I created a mock db and mock context for it but when I try to 'seed' my mock context I'm getting this error.
The call is ambiguous between the following methods or properties: 'AppointmentAPI.UnitTests.DbContextExtensions.Seed(AppointmentAPI.Appt_Models.ApptSystemContext)' and 'AppointmentAPI.UnitTests.DbContextExtensions.Seed(AppointmentAPI.Appt_Models.ApptSystemContext)' [AppointmentAPI.UnitTests, AppointmentAPI.UnitTests]
Not really sure what the problem is because it was working fine the other day with no error and then when I started working on it today the error appeared. I'm fairly new to C# and especially writing unit tests for a .net API so any help is greatly appreciated. I'll post my two files below.
DbContextExtensions.cs
namespace AppointmentAPI.UnitTests
{
using System;
using AppointmentAPI.Appt_Models;
public static class DbContextExtensions
{
public static void Seed(this ApptSystemContext dbContext)
{
// add entities for dbContext instance
dbContext.AppointmentSlots.Add(new AppointmentSlots
{
SlotId = 1,
Date = Convert.ToDateTime("2020-03-31 00:00:00.000"),
Time = TimeSpan.Parse("12:00:00.0000000"),
ApptJson = "{'fname':'Billy','lname':'Joel','age':70,'caseWorker':'Donna', 'appStatus':'finished'}",
Timestamp = Convert.ToDateTime("2020-02-24 12:00:00.000")
});
dbContext.AppointmentSlots.Add(new AppointmentSlots
{
SlotId = 6,
Date = Convert.ToDateTime("2020-07-24 00:00:00.000"),
Time = TimeSpan.Parse("10:00:00.0000000"),
ApptJson = "{'fname':'Michael','lname':'Smith','age':52,'caseWorker':'Donna', 'appStatus':'finished'}",
Timestamp = Convert.ToDateTime("2020-06-25 09:34:00.000")
});
dbContext.SaveChanges();
}
}
}
DbContextMocker.cs
namespace AppointmentAPI.UnitTests
{
using Microsoft.EntityFrameworkCore;
using AppointmentAPI.Appt_Models;
public static class DbContextMocker
{
public static ApptSystemContext GetApptSystemContext(string dbName)
{
// create option for DbContext instance
var options = new DbContextOptionsBuilder<ApptSystemContext>()
.UseInMemoryDatabase(databaseName: dbName)
.Options;
// create instance of DbContext
var dbContext = new ApptSystemContext(options);
// add entities in memory
dbContext.Seed(); <-- error happens here
return dbContext;
}
}
}

This exception is usually thrown when two or more methods are overloaded with same amount of arguments but in different types
eg:
static void Seed(this ApplicationDbContext dbContext, string word1, string[] array = null);
static void Seed(this ApplicationDbContext dbContext, string word1, string word2 = null);
dbContext.Seed("test word"); // will throw the exception or will show as an syntax error.
With the above two files i dont see any methods as such. A clean and build might work hopefully.

Related

C#: Testing Entity Framework FromSql to ensure proper syntax

I am writing to test FromSql Statement with InMemory Database. We are attempting to utilize Sqlite.
Running the following Sql passes the unit test without error.
select * from dbo.Product
However, doing this also passes with incorrect sql syntax. Would like to make the test fail with improper sql syntax. How can we test FromSql properly?
No error came from result of bad syntax .
seledg24g5ct * frofhm dbo.Product
Full Code:
namespace Tests.Services
{
public class ProductTest
{
private const string InMemoryConnectionString = "DataSource=:memory:";
private SqliteConnection _connection;
protected TestContext testContext;
public ProductServiceTest()
{
_connection = new SqliteConnection(InMemoryConnectionString);
_connection.Open();
var options = new DbContextOptionsBuilder<TestContext>()
.UseSqlite(_connection)
.Options;
testContext= new TestContext(options);
testContext.Database.EnsureCreated();
}
[Fact]
public async Task GetProductByIdShouldReturnResult()
{
var productList = testContext.Product
.FromSql($"seledg24g5ct * frofhm dbo.Product");
Assert.Equal(1, 1);
}
Using Net Core 3.1
There are two things to be taken into consideration here.
First, FromSql method is just a tiny bridge for using raw SQL queries in EF Core. No any validation/parsing of the passed SQL string occurs when the method is called except finding the parameter placeholders and associating db parameters with them. In order to get validated, it has to be executed.
Second, in order to support query composition over the FromSql result set, the method returns IQueryable<T>. Which means it is not executed immediately, but only if/when the result is enumerated. Which could happen when you use foreach loop over it, or call methods like ToList, ToArray or EF Core specific Load extension method, which is similar to ToList, but without creating list - the equivalent of foreach loop w/o body, e.g.
foreach (var _ in query) { }
With that being said, the code snippet
var productList = testContext.Product
.FromSql($"seledg24g5ct * frofhm dbo.Product");
does basically nothing, hence does not produce exception for invalid SQL. You must execute it using one of the aforementioned methods, e.g.
productList.Load();
or
var productList = testContext.Product
.FromSql($"seledg24g5ct * frofhm dbo.Product")
.ToList();
and assert the expected exception.
For more info, see Raw SQL Queries and How Queries Work sections of EF Core documentation.
#ivan-stoev has answered your question as to why your '.FromSql' statement does nothing - i.e. the query is never actually materialized. But to try and add some additional value, i'll share my Unit Test setup as it works well for me. Of course, YMMV.
Create a reusable class to handle generic In-memory database creation and easy population of tables with test data. NB: this requires the Nuget packages:
ServiceStack.OrmLite.Core
ServiceStack.OrmLite.Sqlite
I am using OrmLite as it allows for mocking and unit testing by providing a non-disposing connection factory which I can neatly inject into the Test classes via Dependency Injection:
/// <summary>
/// It is not possible to directly mock the Dapper commands i'm using to query the underlying database. There is a Nuget package called Moq.Dapper, but this approach doesnt need it.
/// It is not possible to mock In-Memory properties of a .NET Core DbContext such as the IDbConnection - i.e. the bit we actually want for Dapper queries.
/// for this reason, we need to use a different In-Memory database and load entities into it to query. Approach as per: https://mikhail.io/2016/02/unit-testing-dapper-repositories/
/// </summary>
public class TestInMemoryDatabase
{
private readonly OrmLiteConnectionFactory dbFactory =
new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider);
public IDbConnection OpenConnection() => this.dbFactory.OpenDbConnection();
public void Insert<T>(IEnumerable<T> items)
{
using (var db = this.OpenConnection())
{
db.CreateTableIfNotExist<T>();
foreach (var item in items)
{
db.Insert(item);
}
}
}
}
A 'DbConnectionManager<EFContext>' class to provide the wrapper to the database connection using the EF Context you will already have created. This grabs the database connection from the EF Context and abstracts away the opening/closing operations:
public class DbConnectionManager<TContext> : IDbConnectionManager<TContext>, IDisposable
where TContext : DbContext
{
private TContext _context;
public DbConnectionManager(TContext context)
{
_context = context;
}
public async Task<IDbConnection> GetDbConnectionFromContextAsync()
{
var dbConnection = _context.Database.GetDbConnection();
if (dbConnection.State.Equals(ConnectionState.Closed))
{
await dbConnection.OpenAsync();
}
return dbConnection;
}
public void Dispose()
{
var dbConnection = _context.Database.GetDbConnection();
if (dbConnection.State.Equals(ConnectionState.Open))
{
dbConnection.Close();
}
}
}
Accompanying injectable Interface for the above:
public interface IDbConnectionManager<TContext>
where TContext : DbContext
{
Task<IDbConnection> GetDbConnectionFromContextAsync();
void Dispose();
}
In your .NET Project Startup class, register this interface with the inbuilt DI container (or whatever one you're using):
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(typeof(IDbConnectionManager<>), typeof(DbConnectionManager<>));
}
Now our Unit Test class looks like this:
/// <summary>
/// All tests to follow the naming convention: MethodName_StateUnderTest_ExpectedBehaviour
/// </summary>
[ExcludeFromCodeCoverage]
public class ProductTests
{
//private static Mock<ILoggerAdapter<Db2DbViewAccess>> _logger;
//private static Mock<IOptions<AppSettings>> _configuration;
private readonly Mock<IDbConnectionManager<Db2Context>> _dbConnection;
private readonly List<Product> _listProducts = new List<Product>
{
new Product
{
Id = 1,
Name = "Product1"
},
new Product
{
Id = 2,
Name = "Product2"
},
new Product
{
Id = 3,
Name = "Product3"
},
};
public ProductTests()
{
//_logger = new Mock<ILoggerAdapter<Db2DbViewAccess>>();
//_configuration = new Mock<IOptions<AppSettings>>();
_dbConnection = new Mock<IDbConnectionManager<Db2Context>>();
}
[Fact]
public async Task GetProductAsync_ResultsFound_ReturnListOfAllProducts()
{
// Arrange
// Using a SQL Lite in-memory database to test the DbContext.
var testInMemoryDatabase = new TestInMemoryDatabase();
testInMemoryDatabase.Insert(_listProducts);
_dbConnection.Setup(c => c.GetDbConnectionFromContextAsync())
.ReturnsAsync(testInMemoryDatabase.OpenConnection());
//_configuration.Setup(x => x.Value).Returns(appSettings);
var productAccess = new ProductAccess(_configuration.Object); //, _logger.Object, _dbConnection.Object);
// Act
var result = await productAccess.GetProductAsync("SELECT * FROM Product");
// Assert
result.Count.Should().Equals(_listProducts.Count);
}
}
Notes on the above:
You can see i'm testing a 'ProductAccess' Data Access class which wraps my database calls but that should be easy enough to change for your setup. My ProductAccess class is expecting other services such as logging and Configuration to be injected in, but i have commented these out for this minimal example.
Note the setup of the in-memory database and populating it with your test list of entities to query is now a simple 2 lines (you could even do this just once in the Test class constructor if you want the same test dataset to use across tests):
var testInMemoryDatabase = new TestInMemoryDatabase();
testInMemoryDatabase.Insert(_listProducts);

Accessing dbContext in a C# console application

I have tried to figure this out, but I am stuck.
I have a Net Core 2 application with Service/Repo/Api/Angular layers - but now I want to 'bolt on' a console application and access all the goodies I have already built up. I seem to be in a mess of static objects and DI and null parameters. Anyway, here is a simplified version of my code.
namespace SimpleExample
{
class Program
{
private static ApplicationDbContext _appDbContext;
public Program(ApplicationDbContext appDbContext)
{
_appDbContext = appDbContext;
}
static void Main(string[] args)
{
var instance = new Program(); // this doesn't work!
var instance = new Program(_appDbContext); // neither does this!
instance.GetData();
}
private void GetData()
{
Console.WriteLine("Let's read some data! Press a key to continue.");
Console.ReadLine();
var data = "my data";
var result = GetId(data);
}
private string GetId(string original)
{
var data = _appDbContext.Data
.Where(x => x.Name == original.Trim)
.FirstOrDefault();
return data;
}
}
}
I am getting the classic
'An object reference is required for the non-static field'
error. Then from investigating on here I changed things to static and then everything becomes null.
It's not just the DbContext I am trying to inject. I'm also trying to inject
private ManagerService _managerService;
but getting same errors.
Update
If I try
private static ApplicationDbContext _appDbContext = new
ApplicationDbContext();
as suggested a few times below, then I get the error
There is no argument given that corresponds to the required formal
parameter 'options' of
'ApplicationDbContext.ApplicationDbContext(DbContextOptions)'
OK, I have figured this out, and I'll post my answer for anyone else struggling in this situation.
When you launch the console app, your normal startup.cs doesn't execute, so you have to put a lot of that code in your console app.
private static SiteService _siteService;
private static ApplicationDbContext _appDbContext;
public static void Main()
{
var services = new ServiceCollection();
services.AddTransient<ISiteInterface, SiteRepo>();
services.AddTransient<SiteService>();
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer("blah-blah"));
var serviceProvider = services.BuildServiceProvider();
_siteService = serviceProvider.GetService<SiteService>();
_appDbContext = serviceProvider.GetService<ApplicationDbContext>();
GetData();
}
and now your _appDbContext will be available throughout the rest of your console app.
Hope that helps!
Basically, if you do not plan extensive usage of DbContext nor use DI, there is no need for ServiceProvider. Just remember to make DbContext instance short living and use it for single unit-of-work, not longer.
Your context may look like this:
using Microsoft.EntityFrameworkCore;
namespace YourNamespace;
public class ApplicationContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Your conn string");
}
public DbSet<YourType> YourEntity { get; set; }
}
You can pass conn string by ApplicationContext ctor as well. This is nicely explained here by Microsoft .
Then you can utilise your ApplicationContext like this:
// Unit-of-work closed in using statement
// Here you can query/update your DbContext
using (var dbContext = new ApplicationContext())
{
var queryResult = dbContext.YourEntity.Where(....);
}
You can prepare number of such units-of-work as separate methods for querying a database.
Your repository service can consist of these methods.
Then you can instantiate the service as needed.

EF6 'Nested' DbContexts - could this cause problems

I have the following example which will illustrate my question:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
UserService.CreateUser("NewUser");
}
}
public static class UserService
{
public static void CreateUser(string userName)
{
// DB Context created to CREATE the new user
using (var db = new TestEntities())
{
User userToAdd = new User();
userToAdd.name = userName;
userToAdd.jobTitle = JobService.LookupJob(userName);
db.Users.Add(userToAdd);
db.SaveChanges();
}
}
}
public static class JobService
{
public static string LookupJob(string userName)
{
// 'nested' using, which opens up a new connection to 'read'
using (var db = new TestEntities())
{
return db.JobTitlesNames.Single(a => a.userName = userName).title;
}
}
}
public class TestEntities : DbContext
{
}
}
This is a very noddy example of a scenario I have on a bigger scale. I guess my question is: can this method of 'nested' DbContext creations cause application/sql problems in a large scale application. I know that this sort of structure is bad practice, and generally you should use the same DbContext per business transaction ( in this case, the business transaction being creating a new user ), but if the 'nested' DbContext is only used to read data, never modify, is it still dangerous?
The real-world system I have uses this sort of structure massively, occasionally going down to 4-5 levels of nesting. I'm having ( not going to cover it in detail here, this question is more around theory ) database read/commit problems and I'm wondering if this could be the route cause.
Thanks

Entity Framework initializer in console/library app

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

Two different instances of my dbcontext in EF are colliding with "The context cannot be used while the model is being created."

I have a WebAPI solution with an endpoint "DeleteFolder". That looks like this:
public FolderController()
{
_service = new DtoService();
}
[HttpDelete]
public HttpResponseMessage DeleteFolder(int id)
{
_service.DeleteFolder(id);
return Request.CreateResponse(HttpStatusCode.OK, "Deleted");
}
My _service is an instance of DtoService.cs which contains this:
public DtoService()
{
_repository = new RepositoryService();
}
public void DeleteFolder(int folderId)
{
_repository.DeleteFolder(folderId);
}
Finally, in my repository I have this:
public RepositoryService()
{
_db = new AppDbContext();
}
public void DeleteFolder(int folderId)
{
var folder = GetFolder(folderId);
_db.Folders.Remove(folder);
SaveChanges();
}
Where _db is an instance of my project's DbContext, defined once in the constructor of the Repository class.
When I send a bunch of asynchronous AJAX calls to the delete method, I get "The context cannot be used while the model is being created.". I can see a new instance of RepositoryService spinning up for each one, but if I set a breakpoint at
var folder = GetFolder(folderId);
and then step over, it's hit again, so it seems the other instance is trying to hit the same code before the first one completes, which is somehow causing this error to be thrown.
I don't have any references to my dbContext in my WebAPI layer or DTO service layer - and I'd prefer to keep it that way if possible. I tried wrapping the DeleteFolder code in a using (_db = new AppDbContext) but this didn't work either - and I feel like peppering all of my repository methods with a new dbcontext can't possibly be a recommended solution.
What's going on here? Any tips would be awesome, I'm totally at a loss here.
One thread is initializing your context in response to a request (it's a lengthy process), and another comes in attempting to use the context. The second request thinks the context is ready for use and you get this exception: “The context cannot be used while the model is being created.”
The following code shows how to force database initialization in EF Code First at start up:
protected void Application_Start() {
// ... 
     // Initializes and seeds the database.
     Database.SetInitializer(new MyDBInitializer());
 
     // Forces initialization of database on model changes.
     using (var context = new ApplicationDB()) {
          context.Database.Initialize(force: true);
     }
  // ...
}

Categories