Entity Framework Core 1.1 In Memory Database fails adding new entities - entity-framework-core

I am using the following code in a unit test for the test setup:
var simpleEntity = new SimpleEntity();
var complexEntity = new ComplexEntity
{
JoinEntity1List = new List<JoinEntity1>
{
new JoinEntity1
{
JoinEntity2List = new List<JoinEntity2>
{
new JoinEntity2
{
SimpleEntity = simpleEntity
}
}
}
}
};
var anotherEntity = new AnotherEntity
{
ComplexEntity = complexEntity1
};
using (var context = databaseFixture.GetContext())
{
context.Add(anotherEntity);
await context.SaveChangesAsync();
}
When SaveChangesAsync is reached EF throws an ArgumentException with the following message:
An item with the same key has already been added. Key: 1
I'm using a fixture as well for the unit test class which populates the database with objects of the same types, though for this test I want this particular setup so I want to add these new entities to the in memory database. I've tried adding the entities on the DbSet (not the DbContext) and adding all three entities separatly to no avail. I can however add "simpleEntity" separately (because it is not added in the fixture) but EF complains as soon as I try to add "complexEntity" or "anotherEntity".
It seems like EF in memory database cannot handle several Add's over different instances of the context. Is there any workaround for this or am I doing something wrong in my setup?
The databaseFixture in this case is an instance of this class:
namespace Test.Shared.Fixture
{
using Data.Access;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
public class InMemoryDatabaseFixture : IDatabaseFixture
{
private readonly DbContextOptions<MyContext> contextOptions;
public InMemoryDatabaseFixture()
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
var builder = new DbContextOptionsBuilder<MyContext>();
builder.UseInMemoryDatabase()
.UseInternalServiceProvider(serviceProvider);
contextOptions = builder.Options;
}
public MyContext GetContext()
{
return new MyContext(contextOptions);
}
}
}

You can solve this problem by using Collection Fixtures so you can share this fixture across several test classes. This way you don't build you context several times and thus you won't get this exception:
Some information about collection Fixture
My own example:
[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{ }
[Collection("Database collection")]
public class GetCitiesCmdHandlerTests : IClassFixture<MapperFixture>
{
private readonly TecCoreDbContext _context;
private readonly IMapper _mapper;
public GetCitiesCmdHandlerTests(DatabaseFixture dbFixture, MapperFixture mapFixture)
{
_context = dbFixture.Context;
_mapper = mapFixture.Mapper;
}
[Theory]
[MemberData(nameof(HandleTestData))]
public async void Handle_ShouldReturnCountries_AccordingToRequest(
GetCitiesCommand command,
int expectedCount)
{
(...)
}
public static readonly IEnumerable<object[]> HandleTestData
= new List<object[]>
{
(...)
};
}
}
Good luck,
Seb

Related

NBuilder and DbContext invalid cast issue

I am really new to NBuilder, but it looks awesome so I thought I would have a go.
I have a DatabaseContext which just inherits from DbContext like this:
public class DatabaseContext : DbContext
Now, I have created a service that queries the DatabaseContext like this:
public async Task<List<Strategy>> Handle(StrategyList query, CancellationToken cancellationToken)
{
return _databaseContext.Strategies.ToList();
}
And now I want to make a test.
I set this Context up like this:
public class StrategyListContext
{
public readonly DatabaseContext DatabaseContext;
private StrategyListContext()
{
DatabaseContext = CreateDatabaseContext();
}
private DatabaseContext CreateDatabaseContext()
{
var dbContext = Substitute.For<DatabaseContext>();
var items = Builder<Strategy>.CreateListOfSize(10).Build();
dbContext.Strategies.ToList().Returns(items);
return dbContext;
}
public static StrategyListContext GivenServices() => new StrategyListContext();
public StrategyListHandler WhenCreateHandler() => new StrategyListHandler(DatabaseContext);
}
The most important part is the CreateDatabaseContext method.
It is like this:
private DatabaseContext CreateDatabaseContext()
{
var dbContext = Substitute.For<DatabaseContext>();
var items = Builder<Strategy>.CreateListOfSize(10).Build();
dbContext.Strategies.ToList().Returns(items);
return dbContext;
}
But when I run the test, I get this error:
System.InvalidCastException : Unable to cast object of type 'Castle.Proxies.ObjectProxy' to type 'Microsoft.EntityFrameworkCore.Metadata.Internal.Model'.
Does anyone know what I can do to get this working?
So I found this question:
How do I mock DbContext using NSubstitute and then add/remove data
And I have changed my method to this:
private DatabaseContext CreateDatabaseContext()
{
var dbContext = Substitute.For<DatabaseContext>();
var items = Builder<Strategy>.CreateListOfSize(10).Build().AsQueryable();
var dbSet = Substitute.For<DbSet<Strategy>, IQueryable<Strategy>>();
((IQueryable<Strategy>)dbSet).Provider.Returns(items.Provider);
((IQueryable<Strategy>)dbSet).Expression.Returns(items.Expression);
((IQueryable<Strategy>)dbSet).ElementType.Returns(items.ElementType);
((IQueryable<Strategy>)dbSet).GetEnumerator().Returns(items.GetEnumerator());
dbContext.Set<Strategy>().Returns(dbSet);
return dbContext;
}
But I still get the same error....
The second solution was soooo close, it was simply this:
private static DatabaseContext CreateDatabaseContext()
{
var dbContext = Substitute.For<DatabaseContext>();
var items = Builder<Hall>.CreateListOfSize(10).Build().AsQueryable();
var dbSet = Substitute.For<DbSet<Hall>, IQueryable<Hall>>();
((IQueryable<Hall>)dbSet).Provider.Returns(items.Provider);
((IQueryable<Hall>)dbSet).Expression.Returns(items.Expression);
((IQueryable<Hall>)dbSet).ElementType.Returns(items.ElementType);
((IQueryable<Hall>)dbSet).GetEnumerator().Returns(items.GetEnumerator());
dbContext.Halls = dbSet;
return dbContext;
}
NB: the line that states: dbContext.Halls = dbSet instead of dbContext.Set<Strategy>().Returns(dbSet);

one transaction for multiple contexts in integration tests on TestInitialize

I am writing integration tests and I want to use transaction scope.
We use EF and Repositories with Contexts.
If I have one Repository and once Context then it would look like this:
[TestInitialize]
public void RuleEngineTestsStart() {
customContext = new CustomContext();
transaction = customContext.Database.BeginTransaction();
repo = new CustomRepository(customContext);
// I need to make this context to work in the same transaction as above
anotherContext = new AnotherContext();
anotherRepo = new AnotherRepository(anotherContext);
}
At the end of tests (TestCleanup) I would like to transaction.Rollback(); everything.
I want to have the same transaction for all repositories that work with different contexts, is it possible? How to create transaction and 'send' it to all three contexts?
Please, to do not to use one Context for all repositories, it is not possible due to reasons (we want to have each context with its own DbSets later to be used within microservices).
Edit
In comments I was asked to include more code, however, I think is not necessary to answer my question.
customContext = new CustomContext();
repo = new CustomRepository(customContext);
customContext2 = new CustomContext2();
otherRepository = new CustomRepository2(customContext2);
// class to be tested needs both repositories
ToBeTestedClass cl = new ToBeTestedClass(customRepository, otherRepository);
// "BASE" interface
public interface IRepository<TEntity> where TEntity : class
{
TEntity GetById(long id);
IEnumerable<TEntity> GetByFilter(Expression<Func<TEntity, bool>> predicate);
TEntity GetSingleByFilter(Expression<Func<TEntity, bool>> filter);
void Insert(TEntity entity);
void Delete(long id);
void Update(TEntity entity);
...
}
// BASE CLASS
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext _context;
protected readonly DbSet<TEntity> _dbSet;
public Repository(ColldeskDbContext context)
{
_context = context;
_dbSet = context.Set<TEntity>();
}
// GetSingle, GetAll, Insert, Update etc.
}
// CustomRepository (other Repositories are similar, with custom methods)
public interface ICustomRepository : IRepository<CusotmData>
{
// some specific methods that are not in Base class
}
public class CustomRepository: Repository<CustomData>, ICustomRepository
{
public CustomRepository(CustomContext context) : base(context)
{
}
// custom methods that are specific for given context
}
// Contexts - each context consists of its one DbSets
Don't use dbContext.SaveChanges() in your repositories. Use ONE dbContext when creating repositories. Sample:
using ( var db = new YourDbContext() )
{
// Create and begin transaction
using ( var transaction = db.Database.BeginTransaction() )
{
try
{
// ONE dbContext for all repositories
var firstRepo = new Custom1Repository(db);
var secondRepo = new Custom2Repository(db);
City city = new City { Description = "My city" };
Street street = new Street { Description = "My street", City = city};
firstRepo.Insert(city);
secondRepo.Insert(street);
// Save all your changes and after that commit transaction
db.SaveChanges();
transaction.Commit();
}
catch ( Exception ec)
{
transaction.Rollback();
}
}
}
Doing like this your repositories becomes just wrappers over DbSet<TEntity>
I have figured out that I can simply use TransactionScope like this:
private TransactionScope _scope;
[TestInitialize]
public void TestInitialize()
{
_scope = new TransactionScope();
}
[TestCleanup]
public void TestCleanup()
{
_scope.Dispose();
}
And then each Context would be running within this TransactionScope.

Shim DbContext ctor for Effort unit testing

I'd like to intercept var context = new MyDbContext() to return a different constructor call instead.
The great thing about EFfort is that it let's you set up an easy in-memory database for unit testing.
var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);
But then you'd have to inject that context into your repository.
public FooRepository(MyDbContext context) { _context = context; }
Is it possible to just intercept var context = new MyDbContext() , so that it returns the testContext?
using (var context = new MyDbContext()) {
// this way, my code isn't polluted with a ctor just for testing
}
You have two possible options. Using factories or via Aspect oriented programming (like PostSharp)
referencing this article: http://www.progware.org/Blog/post/Interception-and-Interceptors-in-C-(Aspect-oriented-programming).aspx
Using PostSharp (AOP)
PostSharp is a great tool and can achieve the most clean interception
possible (meaning no changes in your classes and object generation at
all even if you do not your factories for object creation and/or
interfaces) but it is not a free library. Rather than creating proxies
at runtime, it injects code at compile time and therefore changes your
initial program in a seamless way to add method interception.
.....
The cool thing in this is that you do not change anything else in your
code, so your object can be still generated using the new keyword.
Using DI and Factory-pattern
I personally prefer the factory-pattern approach, but you seem apposed to having to inject any dependencies into your classes.
public interface IDbContextFactory<T> where T : DbContext {
T Create();
}
public class TestDbContextFactory : IDbContextFactory<MyDbContext> {
public MyDbContext Create() {
var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);
return testContext;
}
}
public class FooRepository {
MyDbContext _context;
public FooRepository(IDbContextFactory<MyDbContext> factory) {
_context = factory.Create();
}
}
(edit: I just realized this isn't actually returning the other ctor call. working on it.)
Figured it out. Simple enough if you know how to do it:
[TestMethod]
public void Should_have_a_name_like_this()
{
// Arrange
var connection = Effort.DbConnectionFactory.CreateTransient();
ShimSolrDbContext.Constructor = context => new SolrDbContext(connection);
// Act
// Assert
}
And as usual, EFfort requires this constructor in the DbContext class:
public class SomeDbContext
{
public SomeDbContext() : base("name=Prod")
{
}
// EFfort unit testing ctor
public SomeDbContext(DbConnection connection) : base(connection, contextOwnsConnection: true) {
Database.SetInitializer<SolrDbContext>(null);
}
}
But it means the repo is blissfully unaware of the special Transient connection:
public class SomeRepository
{
public void SomeMethodName()
{
using (var context = new SomeDbContext())
{
// self-contained in repository, no special params
// and still calls the special test constructor
}
}
}

Retrieve Entity Framework DbContext from a proxy object

I'm using Entity framework 6 , DBcontext Database First.
I have some situations when I have a proxy object and I want to get the DBContext.
I'm using this code :
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
namespace Sample
{
public class MyDbContext : DbContext
{
private static readonly Dictionary<ObjectContext, MyDbContext> contexts =
new Dictionary<ObjectContext, MyDbContext>();
public static MyDbContext FromObjectContext(ObjectContext context)
{
lock (contexts)
{
if (contexts.ContainsKey(context))
return contexts[context];
return null;
}
}
public static MyDbContext FromObject(object obj)
{
var field = obj.GetType().GetField("_entityWrapper");
var wrapper = field.GetValue(obj);
var property = wrapper.GetType().GetProperty("Context");
var context = (ObjectContext)property.GetValue(wrapper, null);
return FromObjectContext(context);
}
public MyDbContext()
{
lock (contexts)
contexts[((IObjectContextAdapter)this).ObjectContext] = this;
}
protected override void Dispose(bool disposing)
{
lock (contexts)
contexts.Remove(((IObjectContextAdapter)this).ObjectContext);
base.Dispose(disposing);
}
}
}
Now , with this code I can get DBContext using this :
var ctx = MyDataContext.FromObject(MyObj1);
This code is working except one case :
If I add a new object and call SaveChanges , and after for this object try to get the DbContext , I get an error . on the line :
Dim wrapper = field.GetValue(obj)
Error :
An unhandled exception of type 'System.NullReferenceException' occurred in myprog.exe
Additional information: Object reference not set to an instance of an object.
I've also found that in this case , this line return nothing :
var field = obj.GetType().GetField("_entityWrapper");
What can I do ?
Thank you !
When you add a new object, the new object is still the original object (not an object with the proxy). You can use DbSet.Create instead of new so you have already an entity with the proxy.

Preparing for multiple EF contexts on a unit of work - TransactionScope

I'm thinking of the options in regards to implementing a single unit of work for dealing with multiple datasources - Entity framework. I came up with a tentative approach - for now dealing with a single context - but it apparently isn't a good idea.
If we were to analyze the code below, would you consider it a bad implementation? Is the lifetime of the transaction scope a potential problem?
Of course if we wrap the transaction scope with different contexts we'd be covered if the second context.SaveChanges() failed...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
using(UnitOfWork unitOfWork = new UnitOfWork())
{
var repository = new EmployeeRepository(unitOfWork);
var employee = repository.CreateOrGetEmployee("Whatever Name");
Console.Write(employee.Id);
unitOfWork.SaveChanges();
}
}
}
class UnitOfWork : IDisposable
{
TestEntities _context;
TransactionScope _scope;
public UnitOfWork()
{
_scope = new TransactionScope();
_context = new TestEntities();
}
public void SaveChanges()
{
_context.SaveChanges();
_scope.Complete();
}
public TestEntities Context
{
get
{
return _context;
}
}
public void Dispose()
{
_scope.Dispose();
_context.Dispose();
}
}
class EmployeeRepository
{
UnitOfWork _unitOfWork;
public EmployeeRepository(UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public Employee GetEmployeeById(int employeeId)
{
return _unitOfWork.Context.Employees.SingleOrDefault(e => e.Id == employeeId);
}
public Employee CreateEmployee(string fullName)
{
Employee employee = new Employee();
employee.FullName = fullName;
_unitOfWork.Context.SaveChanges();
return employee;
}
public Employee CreateOrGetEmployee(string fullName)
{
var employee = _unitOfWork.Context.Employees.FirstOrDefault(e => e.FullName == fullName);
if (employee == null)
{
employee = new Employee();
employee.FullName = fullName;
this.AddEmployee(employee);
}
return employee;
}
public Employee AddEmployee(Employee employee)
{
_unitOfWork.Context.Employees.AddObject(employee);
_unitOfWork.Context.SaveChanges();
return employee;
}
}
}
Why do you start TransactionScope in constructor? You need it only for saving changes.
public void SaveChanges()
{
// SaveChanges also uses transaction which uses by default ReadCommitted isolation
// level but TransactionScope uses by default more restrictive Serializable isolation
// level
using (var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
_context.SaveChanges();
scope.Complete();
}
}
If you want to have unit of work with more contexts you will simply wrap all those context in the same unit of work class. Your SaveChanges will become little bit more complicated:
public void SaveChanges()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
_contextA.SaveChanges(SaveOptions.DetectChangesBeforeSave);
_contextB.SaveChanges(SaveOptions.DetectChangesBeforeSave);
scope.Complete();
_contextA.AcceptAllChanges();
_contextB.AcceptAllChanges();
}
}
This version separate saving operation from reseting inner state of the context. The reason is that if the first context successfully saves changes but the second fires exception the transaction will be rolled back. Because of that we don't want the first context to have already cleared all changes as accepted (we would lose information about performed changes and we will not be able to save them again).