I've been able to mock DbSet's from entity framework with Moq using this link.
However, I would now like to know how I could mock the call to SqlQuery. Not sure if this is possible or how as it relies on the mocked db context knowing what "query" is being called.
Below is what I am trying to mock.
var myObjects = DbContext.Database
.SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", "some_value")
.ToList();
I currently haven't tried anything as did not know how to start mocking this example.
The mocking of the DbSet is below and to re-iterate, I can correctly mock returning a DbSet of MyObject's but now am trying to mock a SqlQuery that returns a list of MyObject's.
var dbContext = new Mock<MyDbContext>();
dbContext.Setup(m => m.MyObjects).Returns(mockObjects.Object);
dbContext.Setup(m => m.Database.SqlQuery... something along these lines
Database.SqlQuery<T> is not marked as virtual, but Set<T>.SqlQuery is marked as virtual.
Based on Database.SqlQuery<T> documentation
The results of this query are never tracked by the context even if the
type of object returned is an entity type. Use the 'SqlQuery(String,
Object[])' method to return entities that are tracked by the
context.
and Set<T>.SqlQuery documentation
By default, the entities returned are tracked by the context; this can
be changed by calling AsNoTracking on the DbRawSqlQuery returned.
then the Database.SqlQuery<T>(String, Object[]) should be equivalent with Set<T>.SqlQuery(String, Object[]).AsNoTracking() (only if T is EF entity, not a DTO / VM).
So if you can replace the implementation into:
var myObjects = DbContext
.Set<MyObject>()
.SqlQuery("exec [dbo].[my_sproc] {0}", "some_value")
.AsNoTracking()
.ToList();
you can mock it as follow
var list = new[]
{
new MyObject { Property = "some_value" },
new MyObject { Property = "some_value" },
new MyObject { Property = "another_value" }
};
var setMock = new Mock<DbSet<MyObject>>();
setMock.Setup(m => m.SqlQuery(It.IsAny<string>(), It.IsAny<object[]>()))
.Returns<string, object[]>((sql, param) =>
{
// Filters by property.
var filteredList = param.Length == 1
? list.Where(x => x.Property == param[0] as string)
: list;
var sqlQueryMock = new Mock<DbSqlQuery<MyObject>>();
sqlQueryMock.Setup(m => m.AsNoTracking())
.Returns(sqlQueryMock.Object);
sqlQueryMock.Setup(m => m.GetEnumerator())
.Returns(filteredList.GetEnumerator());
return sqlQueryMock.Object;
});
var contextMock = new Mock<MyDbContext>();
contextMock.Setup(m => m.Set<MyObject>()).Returns(setMock.Object);
You can add a virtual method to your database context that you can override in unit tests:
public partial class MyDatabaseContext : DbContext
{
/// <summary>
/// Allows you to override queries that use the Database property
/// </summary>
public virtual List<T> SqlQueryVirtual<T>(string query)
{
return this.Database.SqlQuery<T>(query).ToList();
}
}
The Database property and SqlQuery method are not marked as virtual so they can't be mocked (using Moq; you could use a different library that can account for this but that may be more inertia than you'd like).
You'd need to use some sort of abstraction to get around this, such as by wrapping the entire query of the database in a helper class:
public interface IQueryHelper
{
IList<MyObject> DoYourQuery(string value);
}
public class QueryHelper : IQueryHelper
{
readonly MyDbContext myDbContext;
public QueryHelper(MyDbContext myDbContext)
{
this.myDbContext = myDbContext;
}
public IList<MyObject> DoYourQuery(string value)
{
return myDbContext.Database.SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", value).ToList();
}
}
Now the method you are testing becomes:
public void YourMethod()
{
var myObjects = queryHelper.DoYourQuery("some_value");
}
Then you'd inject the IQueryHelper in the constructor of the class you're testing and mock that.
You're going to be missing test coverage on DoYourQuery, but the now the query is so simple there are obviously no deficiencies.
Should anyone come across this. I solved this with a few approaches. Just another way to address this.
My context is abstracted through an interface. I only need a few of the methods:
public interface IDatabaseContext
{
DbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
int SaveChanges();
Task<int> SaveChangesAsync();
void AddOrUpdateEntity<TEntity>(params TEntity[] entities) where TEntity : class;
}
All of my database access is through async methods. Which brings up a whole new set of problems when trying to mock it. Fortunately - it has been answered here. The exception you get is related to the missing mock for IDbAsyncEnumerable. Using the solution provided - I just extended it a bit more so that I had a helper to return a Mock> object that mocked all of the properties expected.
public static Mock<DbSqlQuery<TEntity>> CreateDbSqlQuery<TEntity>(IList<TEntity> data)
where TEntity : class, new()
{
var source = data.AsQueryable();
var mock = new Mock<DbSqlQuery<TEntity>>() {CallBase = true};
mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression);
mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType);
mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider));
mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity());
mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; });
mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; });
return mock;
}
Finally - using the solution provided by #Yulium Chandra - my testing of raw SQL with mocked context looks like:
public Mock<DbSet<TestModel>> MockDbSet { get; }
....
MockDbSet.Setup(x => x.SqlQuery(It.IsAny<string>))
.Returns<string,object[]>
((sql, param) =>
{
var sqlQueryMock = MockHelper.CreateDbSqlQuery(Models);
sqlQueryMock.Setup(x => x.AsNoTracking())
.Returns(sqlQueryMock.Object);
return sqlQueryMock.Object;
});
Related
I am using MongoDB.Driver 2.10.4
I want to get all documents that have an id in a list of ids that I get from my controller
I am using this code :
var pending_processes = mongo_context.collectionName
.AsQueryable()
.Where(x => request.ids.Contains(x.Id))
.Select(x => new ViewModel()
{
process_id = x.Id,
// date = not important just accessing the x object
state = new States()
{
timeline = x.states.timeline,
part = x.states.part,
}
})
.ToList();
It works fine but if I make my function async and do an await and replace ToList() with ToListAsync() I get the following error:
The source IQueryable doesn't implement IAsyncEnumerable<Application.Process.Query.GetPendingProcesses.ViewModel>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
Clearly there is something I am not getting here my concern is I don't want my code to run synchronously this would be really bad. usually when dealing with postgresql context I always use the ToListAsync() but here in order to use linq with mongo I had to use AsQueryable() and AsQueryable() as I understand it does not get the data it's a normal query that I need to execute afterwards but when I use with it ToList() everything works but when I use ToListAsync() I get the error.
I just want to know what is behind all of this and is the code above synchronous or asynchronous?
I just changed my query using Find and ForEachAsync() and now all works well. I just did not use AsQueryable() because the mongodb driver as I understand it use these other functions but provides a way to use linq so I used the default methods without linq-
var result = new GetPendingProcessesViewModel();
var filter = Builders<Domain.MongoDocuments.Process>
.Filter.Where(x => own_processes.Contains(x.Id));
await _elba_mongo_context.process
.Find(filter)
.ForEachAsync(x =>
result.pending_processes_own.Add(
new GetPendingProcessesViewModelItem()
{
process_id = x.Id,
state = new States()
{
timeline = x.states.timeline,
part = x.states.part,
supplier = x.states.supplier
}
}
)
);
You can get the documentation and references for your MongoDB driver version from GitHub.
Sure you can keep it asynchronous, but first you have switch out AsQueryable to some other method which returns back and IQueryable.
In a nutshell ToListAsync() works on a IQueryable<T> only, when you turned it in to a IEnumerable via AsEnumerable() you lost the ability to call it. Its explained well here
You have a couple of choices, either implement IDbAsyncEnumerable see here or change the result list you have into an async list with Task.FromResult()
Option 1:
// try this in your controller
public async Task<List<PeopleStatesType>> GetAsyncStatesList()
{
//for e.g.
List<PeopleType> peopleList = new List<PeopleType>()
{
new PeopleType(){ Name = "Frank", Gender = "M" },
new PeopleType(){ Name = "Rose", Gender = "F" } //..
};
var result = from e in peopleList
where e.Gender == "M"
select e;
return await Task.FromResult(result.ToList());
}
Option 2:
Use this class
public class AsyncEnumerableQuery<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T> {
public AsyncEnumerableQuery(IEnumerable<T> enumerable) : base(enumerable) {
}
public AsyncEnumerableQuery(Expression expression) : base(expression) {
}
public IDbAsyncEnumerator<T> GetAsyncEnumerator() {
return new InMemoryDbAsyncEnumerator<T>(((IEnumerable<T>) this).GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() {
return GetAsyncEnumerator();
}
private class InMemoryDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> {
private readonly IEnumerator<T> _enumerator;
public InMemoryDbAsyncEnumerator(IEnumerator<T> enumerator) {
_enumerator = enumerator;
}
public void Dispose() {
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken) {
return Task.FromResult(_enumerator.MoveNext());
}
public T Current => _enumerator.Current;
object IDbAsyncEnumerator.Current => Current;
}
}
// FindAll: with a condition like .Find(x => x.user == "Jone Doe")
// see [here][3]
var task = collection.Find(p => true).ToListAsync();
Update Option 3: Simple Async Get
public async Task<IEnumerable<MyMongoEntity>> Where(Expression<Func<MyMongoEntity, bool>> expression = null)
{
return await context.GetCollection<MyMongoEntity>(typeof(MyMongoEntity).Name, expression).Result.ToListAsync();
}
Based on your comment, for a simple get documents collection, this helper should work.
From your error, it seems like the mongo_context.collectionName is returning something from Entity Framework?
Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
Make sure you are calling the AsQueryable extension method directly on the Mongo collection. (Your code just shows mongo_context.collectionName.AsQueryable() so I'm not sure you're doing that)
Hooking into the LINQ provider requires getting access to an IQueryable instance. The driver provides an AsQueryable extension method on IMongoCollection.
var collection = db.GetCollection<Person>("people");
var queryable = collection.AsQueryable();
Reference: https://mongodb.github.io/mongo-csharp-driver/2.10/reference/driver/crud/linq/#queryable
The AsQueryable extension above actually returns an IQueryable instance that implements IMongoQueryable and has all the same async extensions that other ORMs (Entity Framework, NHibernate, etc.) have - including ToListAsync.
Basically, I'm trying to follow Chris Pratt's "Truly Generic Repository" ( http://cpratt.co/truly-generic-repository/ ) but don't know how to handle the Many-to-Many relationships within that generic repository. I do not want to add Attach method to the interface and then calling it from my service layer (I'm calling Chris Pratt's IRepository as IService) and basically all my service functions will be calling Service : IService. In other words, I do not want to call Attach method from within PersonService, I wish the Create/Update method in Service.cs (which is inheriting IRepository/IService) to handle Many-to-Many automatically without the involvment of consumer (i.e PersonService.cs).
Any thoughts?
my current code is the following and I don't know how to achieve the same effect using Chris Pratt's repository.
public int AddPerson(Person person)
{
using (var dbContext = new MyContext("myConnString"))
{
// Attaching Hobbies
person.Hobbies.ToList().ForEach(x => dbContext.Hobbies.Attach(x));
dbContext.People.Add(person);
dbContext.SaveChanges();
return person.PersonId;
}
}
public void UpdatePerson(Person person)
{
using (var dbContext = new MyContext("myConnString"))
{
// Loading existing entity to compare
Person existingPerson = FindPersonBy(true, x => x.PersonId == person.PersonId, new string[] { "Hobbies", "Gender" });
dbContext.People.Attach(existingPerson);
dbContext.Entry(existingPerson).CurrentValues.SetValues(person);
// Updating Person.Hobbies
var deletedHobbies = existingPerson.Hobbies.ExceptBy(person.Hobbies, x => x.HobbyId).ToList();
deletedHobbies.ForEach(x => existingPerson.Hobbies.Remove(x));
var addedHobbies = person.Hobbies.ExceptBy(existingPerson.Hobbies, x => x.HobbyId).ToList();
addedHobbies.ForEach(x => { dbContext.Hobbies.Attach(x); existingPerson.Hobbies.Add(x); });
// Saving Transaction
dbContext.SaveChanges();
}
}
I am trying to use (POST/PUT) a DTO object with a collection of child objects from JavaScript to an ASP.NET Core (Web API) with an EF Core context as my data source.
The main DTO class is something like this (simplified of course):
public class CustomerDto {
public int Id { get;set }
...
public IList<PersonDto> SomePersons { get; set; }
...
}
What I don't really know is how to map this to the Customer entity class in a way that does not include a lot of code just for finding out which Persons had been added/updated/removed etc.
I have played around a bit with AutoMapper but it does not really seem to play nice with EF Core in this scenario (complex object structure) and collections.
After googling for some advice around this I haven't found any good resources around what a good approach would be. My questions is basically: should I redesign the JS-client to not use "complex" DTOs or is this something that "should" be handled by a mapping layer between my DTOs and Entity model or are there any other good solution that I am not aware of?
I have been able to solve it with both AutoMapper and and by manually mapping between the objects but none of the solutions feels right and quickly become pretty complex with much boilerplate code.
EDIT:
The following article describes what I am referring to regarding AutoMapper and EF Core. Its not complicated code but I just want to know if it's the "best" way to manage this.
(Code from the article is edited to fit the code example above)
http://cpratt.co/using-automapper-mapping-instances/
var updatedPersons = new List<Person>();
foreach (var personDto in customerDto.SomePersons)
{
var existingPerson = customer.SomePersons.SingleOrDefault(m => m.Id == pet.Id);
// No existing person with this id, so add a new one
if (existingPerson == null)
{
updatedPersons.Add(AutoMapper.Mapper.Map<Person>(personDto));
}
// Existing person found, so map to existing instance
else
{
AutoMapper.Mapper.Map(personDto, existingPerson);
updatedPersons.Add(existingPerson);
}
}
// Set SomePersons to updated list (any removed items drop out naturally)
customer.SomePersons = updatedPersons;
Code above written as a generic extension method.
public static void MapCollection<TSourceType, TTargetType>(this IMapper mapper, Func<ICollection<TSourceType>> getSourceCollection, Func<TSourceType, TTargetType> getFromTargetCollection, Action<List<TTargetType>> setTargetCollection)
{
var updatedTargetObjects = new List<TTargetType>();
foreach (var sourceObject in getSourceCollection())
{
TTargetType existingTargetObject = getFromTargetCollection(sourceObject);
updatedTargetObjects.Add(existingTargetObject == null
? mapper.Map<TTargetType>(sourceObject)
: mapper.Map(sourceObject, existingTargetObject));
}
setTargetCollection(updatedTargetObjects);
}
.....
_mapper.MapCollection(
() => customerDto.SomePersons,
dto => customer.SomePersons.SingleOrDefault(e => e.Id == dto.Id),
targetCollection => customer.SomePersons = targetCollection as IList<Person>);
Edit:
One thing I really want is to delcare the AutoMapper configuration in one place (Profile) not have to use the MapCollection() extension every time I use the mapper (or any other solution that requires complicating the mapping code).
So I created an extension method like this
public static class AutoMapperExtensions
{
public static ICollection<TTargetType> ResolveCollection<TSourceType, TTargetType>(this IMapper mapper,
ICollection<TSourceType> sourceCollection,
ICollection<TTargetType> targetCollection,
Func<ICollection<TTargetType>, TSourceType, TTargetType> getMappingTargetFromTargetCollectionOrNull)
{
var existing = targetCollection.ToList();
targetCollection.Clear();
return ResolveCollection(mapper, sourceCollection, s => getMappingTargetFromTargetCollectionOrNull(existing, s), t => t);
}
private static ICollection<TTargetType> ResolveCollection<TSourceType, TTargetType>(
IMapper mapper,
ICollection<TSourceType> sourceCollection,
Func<TSourceType, TTargetType> getMappingTargetFromTargetCollectionOrNull,
Func<IList<TTargetType>, ICollection<TTargetType>> updateTargetCollection)
{
var updatedTargetObjects = new List<TTargetType>();
foreach (var sourceObject in sourceCollection ?? Enumerable.Empty<TSourceType>())
{
TTargetType existingTargetObject = getMappingTargetFromTargetCollectionOrNull(sourceObject);
updatedTargetObjects.Add(existingTargetObject == null
? mapper.Map<TTargetType>(sourceObject)
: mapper.Map(sourceObject, existingTargetObject));
}
return updateTargetCollection(updatedTargetObjects);
}
}
Then when I create the mappings I us it like this:
CreateMap<CustomerDto, Customer>()
.ForMember(m => m.SomePersons, o =>
{
o.ResolveUsing((source, target, member, ctx) =>
{
return ctx.Mapper.ResolveCollection(
source.SomePersons,
target.SomePersons,
(targetCollection, sourceObject) => targetCollection.SingleOrDefault(t => t.Id == sourceObject.Id));
});
});
Which allow me to use it like this when mapping:
_mapper.Map(customerDto, customer);
And the resolver takes care of the mapping.
AutoMapper is the best solution.
You can do it very easily like this :
Mapper.CreateMap<Customer, CustomerDto>();
Mapper.CreateMap<CustomerDto, Customer>();
Mapper.CreateMap<Person, PersonDto>();
Mapper.CreateMap<PersonDto, Person>();
Note : Because AutoMapper will automatically map the List<Person> to List<PersonDto>.since they have same name, and there is already a mapping from Person to PersonDto.
If you need to know how to inject it to ASP.net core,you have to see this article : Integrating AutoMapper with ASP.NET Core DI
Auto mapping between DTOs and entities
Mapping using attributes and extension methods
First I would recommend using JsonPatchDocument for your update:
[HttpPatch("{id}")]
public IActionResult Patch(int id, [FromBody] JsonPatchDocument<CustomerDTO> patchDocument)
{
var customer = context.EntityWithRelationships.SingleOrDefault(e => e.Id == id);
var dto = mapper.Map<CustomerDTO>(customer);
patchDocument.ApplyTo(dto);
var updated = mapper.Map(dto, customer);
context.Entry(entity).CurrentValues.SetValues(updated);
context.SaveChanges();
return NoContent();
}
And secound you should take advantage of AutoMapper.Collections.EFCore. This is how I configured AutoMapper in Startup.cs with an extension method, so that I´m able to call services.AddAutoMapper() without the whole configuration-code:
public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddCollectionMappers();
cfg.UseEntityFrameworkCoreModel<MyContext>(services);
cfg.AddProfile(new YourProfile()); // <- you can do this however you like
});
IMapper mapper = config.CreateMapper();
return services.AddSingleton(mapper);
}
This is what YourProfile should look like:
public YourProfile()
{
CreateMap<Person, PersonDTO>(MemberList.Destination)
.EqualityComparison((p, dto) => p.Id == dto.Id)
.ReverseMap();
CreateMap<Customer, CustomerDTO>(MemberList.Destination)
.ReverseMap();
}
I have a similar object-graph an this works fine for me.
EDIT
I use LazyLoading, if you don´t you have to explicitly load navigationProperties/Collections.
I was struggling with the very same issue for quite some time. After digging through many articles I've came up with my own implementation which I'm sharing with you.
First of all I've created a custom IMemberValueResolver.
using System;
using System.Collections.Generic;
using System.Linq;
namespace AutoMapper
{
public class CollectionValueResolver<TDto, TItemDto, TModel, TItemModel> : IMemberValueResolver<TDto, TModel, IEnumerable<TItemDto>, IEnumerable<TItemModel>>
where TDto : class
where TModel : class
{
private readonly Func<TItemDto, TItemModel, bool> _keyMatch;
private readonly Func<TItemDto, bool> _saveOnlyIf;
public CollectionValueResolver(Func<TItemDto, TItemModel, bool> keyMatch, Func<TItemDto, bool> saveOnlyIf = null)
{
_keyMatch = keyMatch;
_saveOnlyIf = saveOnlyIf;
}
public IEnumerable<TItemModel> Resolve(TDto sourceDto, TModel destinationModel, IEnumerable<TItemDto> sourceDtos, IEnumerable<TItemModel> destinationModels, ResolutionContext context)
{
var mapper = context.Mapper;
var models = new List<TItemModel>();
foreach (var dto in sourceDtos)
{
if (_saveOnlyIf == null || _saveOnlyIf(dto))
{
var existingModel = destinationModels.SingleOrDefault(model => _keyMatch(dto, model));
if (EqualityComparer<TItemModel>.Default.Equals(existingModel, default(TItemModel)))
{
models.Add(mapper.Map<TItemModel>(dto));
}
else
{
mapper.Map(dto, existingModel);
models.Add(existingModel);
}
}
}
return models;
}
}
}
Then I configure AutoMapper and add my specific mapping:
cfg.CreateMap<TDto, TModel>()
.ForMember(dst => dst.DestinationCollection, opts =>
opts.ResolveUsing(new CollectionValueResolver<TDto, TItemDto, TModel, TItemModel>((src, dst) => src.Id == dst.SomeOtherId, src => !string.IsNullOrEmpty(src.ThisValueShouldntBeEmpty)), src => src.SourceCollection));
This implementation allows me to fully customize my object matching logic due to keyMatch function that is passed in constructor. You can also pass an additional saveOnlyIf function if you for some reason need to verify passed objects if they are suitable for mapping (in my case there were some objects that shouldn't be mapped and added to collection if they didn't pass an extra validation).
Then e.g. in your controller if you want to update your disconnected graph you should do the following:
var model = await Service.GetAsync(dto.Id); // obtain existing object from db
Mapper.Map(dto, model);
await Service.UpdateAsync(model);
This works for me. It's up to you if this implementation suits you better than what author of this question proposed in his edited post:)
I have some code that selects a property using reflection. It works fine but I can't unit test it. Apparently, the mock that it creates does not have any properties.
Can anyone tell me how to get the property from a Mocked object using reflection? (Or explain why it would not be possible?)
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
I'm using this for a copy class which allows to copy data for any entity type and expects a UnitOfWork that contains a IRepository where T is the type that you want to copy.
public class TableCopier<T> : ICopier where T : EntityBase
{
public IUnitOfWork From { get; set; }
public IUnitOfWork To { get; set; }
public virtual int Copy(IProgressReporter progres = null)
{
lock (this)
{
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
//...
}
}
}
The Unit of Work looks like this:
public interface IUnitOfWork
{
IRepository<Parameter> ParameterRepository { get; }
IRepository<Company> CompanyRepository { get; }
IRepository<CompanyAccountancynumber> CompanyAccountancynumberRepository { get; }
//...
And finally the unit test:
[Test]
public void DefaultCopy()
{
// (removed some code)
var to = MockRepository.GenerateStrictMock<IUnitOfWork>();
var target = MockRepository.GenerateStrictMock<IRepository<CompanyDataset>>();
to.Stub(x => x.CompanyDatasetRepository).Return(target);
var inserted = new List<CompanyDataset>();
target.Stub(x => x.BulkInsert(null))
.IgnoreArguments()
.WhenCalled(x => inserted.AddRange(x.Arguments[0] as IEnumerable<CompanyDataset>));
//ACT
var copier = new TableCopier<CompanyDataset>() as ICopier;
copier.Copy(from, to, map, log, null, CancellationToken.None );
I found the problem:
The properties are available as explicit properties.
And to access these kind of properties you need the interface map.
This means I should change this:
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
To this:
var target = To.GetType().GetInterfaceMap(typeof(IUnitOfWork))
.InterfaceMethods
.Single(x => (typeof(IRepository<T>).IsAssignableFrom(x.ReturnType)))
.Invoke(To, null) as IRepository<T>;
My own implementations of IUnitOfWork used 'implicit' properties, so that explains why that worked and the unit test failed. Also using the map is generally better (/more correct) (-> no chance on problems if I would ever make another implementation of the interface with explicit props). So I suppose it was a good unit test :)
Regards
Is there a way I can get a list of EntityA without its related navigations, even when DbContext has them. I need this for serialization purposes.
I tried to turn Lazy loading off and explicitly .Include any related entities. But if DbContext has already loaded them, it will be included anyway.
The senario is like this:
public class LookupRepository : ILookupRepository
{
private readonly CustomDbContext _dbContext;
public LookupRepository(CustomDbContext dbContext) {
if(dbContext == null)
throw new ArgumentNullException("dbContext");
_dbContext = dbContext;
}
public IEnumerable<Country> GetCountriesFull() {
return _dbContext.Set<Country>()
.Include(c => c.Areas)
.Include(c => c.Continent)
.ToList();
}
public IEnumerable<Country> GetCountries() {
return _dbContext.Set<Country>()
.ToList();
}
public IEnumerable<Continent> GetContinents() {
return _dbContext.Set<Continent>()
.ToList();
}
public IEnumerable<Area> GetAreas() {
return _dbContext.Set<Area>()
.ToList();
}
}
And the DbContext I inject in there is initialized like this:
public CustomDbContext CreateDbContext(){
var dbContext = new CustomDbContext();
dbContext.Configuration.ProxyCreationEnabled = false;
return dbContext;
}
So this test that uses a clean DbContext passes:
[Test]
public void GetCountries_CalledOnce_ReturnsCountriesWithoutNavigations() {
var sut = CreateLookupRepository();
var countries = sut.GetCountries();
CollectionAssert.IsNotEmpty(countries);
Assert.That(countries.Select(c => c.Continent), Is.All.Null);
Assert.That(countries.Select(c => c.Areas), Is.All.Null);
}
but this one that includes all a call to GetCountriesFull fails:
[Test]
public void GetCountries_AfterCallingGetCountriesFull_StillReturnsNoNavigations() {
var sut = CreateLookupRepository();
var fullCountries = sut.GetCountriesFull();
var countries = sut.GetCountries();
CollectionAssert.IsNotEmpty(countries);
Assert.That(countries.Select(c => c.Continent), Is.All.Null);
Assert.That(countries.Select(c => c.Areas), Is.All.Null);
}
Any advice on how to do this? I thought of using a factory to create a new dbContext for each method (anyway this code is only run on my application startup and data remain in memory as Singleton), but I thought there must me a better solution.
The most simple way to do this is by getting the entities with AsNoTracking(). By doing this, you tell EF not to add the entities to its internal cache and entity relationships will not be resolved.
But here (again) the extra repository layer works against you, because you can't just do a call like
var fullCountries = sut.GetCountriesFull().AsNoTracking();
You have to make overloads, or add parameters like bool withTracking, or initialize the repository with an option to always (or never) use AsNoTracking().
If you don't care that they are being loaded unnecessarily (or they were already filled in for another reason), just set all navigational properties to null if you don't want them to be passed.
You can also add the [XmlIgnore] attribute to your navigational properties, so the serializer will not include them. That also prevents the same issue.
Dear there are many ways to doing it
1)if you are using code 1st then follow this
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.Configuration.LazyLoadingEnabled = false;
}
2)The edmx file has in the and definition an attribute for lazy loading where you can set lazy loading generally to false:
public MyEntitiesContext() : base("name=MyEntitiesContext", "MyEntitiesContext")
{
this.ContextOptions.LazyLoadingEnabled = false;
OnContextCreated();
}
or simply do this
public BlogContext() : base()
{
this.Configuration.LazyLoadingEnabled = false;
}