NullReferenceException using Where() in AutoMapper.MapFrom - entity-framework

I'm modelling a 3rd party database using Automapper 8 and EF 6.
One of my DTO classes needs to use a Where clause on an association to locate the correct record.
// Community table is mapped and working.
// Mapping breaks when mapping dest.Subjects
cfg.CreateMap<Person, PersonDto>()
// snip many mappings
.ForMember(dest => dest.Id, act => act.MapFrom(src => src.ID))
.ForMember(dest => dest.UserName, act => act.MapFrom(src => src.Community.NetworkLogin))
.ForMember(
dest => dest.Subjects,
act => act.MapFrom(
src => src.Community.StudentClasses.Where(
subject => subject.Year == CurrentSemester.Year && subject.Semester == CurrentSemester.Semester)))
.ForMember(
dest => dest.Contacts,
act => act.MapFrom(
src => src.Community.Contacts.Where(
contact => Contact.UseThis).Select(contact => contact.ContactDetails)));
This code works in production, but I'd really like to Unit Test this model. Running a simple test (get all records in the Mock) I'm hit with a NullReferenceException when it tries to access the Community.StudentClasses object.
I found this answer relating to NullReferenceExceptionexceptions and AutoMapper, which helped me fix the rest of the references in this config, but I'm still having issues with this one. The test works when I remove the Community.StudentClasses mapping.
I'm mocking objects using code similar to:
public static Person Person19788 =>
new SchoolContact
{
ID = 19788,
NetworkLogin = "username",
// Tried various creation methods
// StudentClasses = new List<StudentClass> {new StudentClass()},
// StudentClasses = new List<StudentClass> {new StudentClass {Year = 0, Semester = 0}},
StudentClasses = null,
StudentContacts = null,
Address = Address19788
};
CurrentSemester has been checked, and returns valid non-zero values for Year and Semester.
The strange thing is that the Contacts mapping works fine, even with null values. So I assume that I've broken my Subjects mapping somewhere along the line, but I'm unsure where else to look.

It turns out that the problem was with my Subjects mapping after all.
The top-level associations were mapped correctly, however the lower-level associations were not. (I didn't include these mappings because a) I didn't think about them, and b) there's only so much code that anyone would be willing to wade through).
After fixing up the w > x > y > z mappings everything is working as expected. I thought I'd exhausted all options before posting this question. Lesson learned: don't post to SO until you've slept on it and taken a fresh look at the code the next day.

Related

EF Core 3 GroupBy multiple columns Count Throws with extensions but linq works

Here is the one that throws full exception:
var duplicateCountOriginal = _db.TableName
.GroupBy(g => new {g.ColumnA, g.ColumnB, g.ColumnC})
.Count(g => g.Count() > 1);
Exception:
System.ArgumentException: Expression of type 'System.Func2[System.Linq.IGrouping2[Microsoft.EntityFrameworkCore.Storage.ValueBuffer,Microsoft.EntityFrameworkCore.Storage.ValueBuffer],Microsoft.EntityFrameworkCore.Storage.ValueBuffer]' cannot be used for parameter of type 'System.Func2[Microsoft.EntityFrameworkCore.Storage.ValueBuffer,Microsoft.EntityFrameworkCore.Storage.ValueBuffer]' of method 'System.Collections.Generic.IEnumerable1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer] Select[ValueBuffer,ValueBuffer](System.Collections.Generic.IEnumerable1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer], System.Func2[Microsoft.EntityFrameworkCore.Storage.ValueBuffer,Microsoft.EntityFrameworkCore.Storage.ValueBuffer])' (Parameter 'arg1')
But the same thing works when it is written as linq (I prefer extensions)
var duplicateCount =
from a in _db.TableName
group a by new {a.ColumnA, a.ColumnB, a.ColumnC}
into g
where g.Count() > 1
select g.Key;
duplicateCount.Count()
I am unable to understand why one works or the other doesn't.
Also if I change the first one a little bit based on EF Core 3 changes like the following
var duplicateCountOriginal = _db.TableName
.GroupBy(g => new {g.ColumnA, g.ColumnB, g.ColumnC})
.AsEnumerable()
.Count(g => g.Count() > 1);
I get the following exception:
System.InvalidOperationException: Client projection contains reference to constant expression of 'Microsoft.EntityFrameworkCore.Metadata.IPropertyBase' which is being passed as argument to method 'TryReadValue'. This could potentially cause memory leak. Consider assigning this constant to local variable and using the variable in the query instead. See https://go.microsoft.com/fwlink/?linkid=2103067 for more information.
According to me, the link given by ms has no meaning to the whatever problem here is.
Please LMK if there is any logical explanation.
There is no logical explanation. Just EF Core query translation is still far from perfect and have many defects/bugs/unhandled cases.
In this particular the problem is not the query syntax or method syntax (what you call extensions), but the lack of Select after GroupBy. If you rewrite the method syntax query similar to the one using query syntax, i.e. add .Where, .Select and then Count:
var duplicateCount = _db.TableName
.GroupBy(g => new {g.ColumnA, g.ColumnB, g.ColumnC})
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.Count();
then it will be translated and executed successfully.

FluentAssertions graph comparison, ExcludingNestedObjects ignored?

I've got a unit/integration test as follows.
using (var repository = _factory.Get())
{
applicationBefore = repository.Applications
.Include(a => a.AcceptedAgreements)
.Single(a => a.AggregateId == applicationId);
}
// Perform an operation that deletes an Application and all
// related data and then rebuilds it from the event store.
Repo.Application applicationAfter = null;
using (var repository = _factory.Get())
{
applicationAfter = repository.Applications
.Include(a => a.AcceptedAgreements)
.Single(a => a.AggregateId == applicationId);
}
applicationAfter.AcceptedAgreements.ShouldAllBeEquivalentTo(applicationBefore.AcceptedAgreements, options => options
.ExcludingNestedObjects());
The repository reference is a DbContext and AcceptedAgreements is a navigation property.
This test fails with the following message.
Result Message:
FluentAssertions.Execution.AssertionFailedException :
Expected item[0].Application.Id to be 1, but found 2.
<more failures where stuff inside Application is different>
With configuration:
- Use declared types and members
- Compare enums by value
- Include all non-private properties
- Include all non-private fields
- Match member by name (or throw)
- Be strict about the order of items in byte arrays
- FluentAssertions.Equivalency.ShouldAllBeEquivalentToHelper+CollectionMemberOrderingRuleDecorator
If I modify the assertion as follows:
applicationAfter.AcceptedAgreements.ShouldAllBeEquivalentTo(applicationBefore.AcceptedAgreements, options => options
.Excluding(o => o.Application));
Now the test passes.
Please help me understand why ExcludingNestedObjects() doesn't exclude the Application property, which is in fact a nested object, and I have to resort to excluding each navigation property individually. The above code is slightly simplified as I actually have multiple navigation properties and have to exclude each of them individually.

EF6 error when using in-memory testing

Mocked setup below (the GenerateTrades() and other methods in ContextFactory are just returning a List AsQueryable with sample data):
_trades = ContextFactory.GenerateTrades();
_hedges = ContextFactory.GenerateHedges();
_exposures = ContextFactory.GenerateExposures();
_ctx = new Mock<FxContext>();
var fakeTrades = new Mock<DbSet<Trade>>();
fakeTrades.As<IQueryable<Trade>>().Setup(m => m.Provider).Returns(_trades.Provider);
fakeTrades.As<IQueryable<Trade>>().Setup(m => m.Expression).Returns(_trades.Expression);
fakeTrades.As<IQueryable<Trade>>().Setup(m => m.ElementType).Returns(_trades.ElementType);
fakeTrades.As<IQueryable<Trade>>().Setup(m => m.GetEnumerator()).Returns(_trades.GetEnumerator());
var fakeHedges = new Mock<DbSet<Hedge>>();
fakeHedges.As<IQueryable<Hedge>>().Setup(m => m.Provider).Returns(_hedges.Provider);
fakeHedges.As<IQueryable<Hedge>>().Setup(m => m.Expression).Returns(_hedges.Expression);
fakeHedges.As<IQueryable<Hedge>>().Setup(m => m.ElementType).Returns(_hedges.ElementType);
fakeHedges.As<IQueryable<Hedge>>().Setup(m => m.GetEnumerator()).Returns(_hedges.GetEnumerator());
var fakeExposures = new Mock<DbSet<Exposure>>();
fakeExposures.As<IQueryable<Exposure>>().Setup(m => m.Provider).Returns(_exposures.Provider);
fakeExposures.As<IQueryable<Exposure>>().Setup(m => m.Expression).Returns(_exposures.Expression);
fakeExposures.As<IQueryable<Exposure>>().Setup(m => m.ElementType).Returns(_exposures.ElementType);
fakeExposures.As<IQueryable<Exposure>>().Setup(m => m.GetEnumerator()).Returns(_exposures.GetEnumerator());
_ctx.Setup(c => c.Trades).Returns(fakeTrades.Object);
_ctx.Setup(c => c.Hedges).Returns(fakeHedges.Object);
_ctx.Setup(c => c.Exposures).Returns(fakeExposures.Object);
Part of test code looks like:
_sut = (from x in _ctx.Object.Hedges
where x.Id == ContextFactory.s_hedge01Id
select x).FirstOrDefault();
_ctx.Object.Hedges.Attach(_sut);
_ctx.Object.Entry(_sut).Collection(x => x.HedgedTrades).Load();
On the last line I get an exception:
{"Member 'Load' cannot be called for property 'HedgedTrades' because the entity of type 'Hedge' does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet<Hedge>."}
I have verified that in fact the _sut Hedge is actually an instance of a Hedge (not null or a stub) and as the code seems to indicate, I have attached the Hedge to the context explicitly (though I would think the query should bring the object into the context automagically). Am I missing something?
It seems like here in your test you are mixing two separate approaches. Error you received states that you are trying to access DbContext inherited logic, but it is mocked. If you want to perform your unit tests against database with use of FxContext then do not mock it. If you want test other part of software then you should mock interface that FxContext should implement. This will enable you to fullfil dependency injection principle in your code which will help you then organize your test in more predictable way.

factory girl - how can we access overrides passed to a method?

This question is related to when/how overrides work in FactoryGirl
For my specific problem, I have a organization with 'limits' attribute. The after(:create) section sets some other values and finally does a save. The save resets the 'limits' based on logic in organization.after_save method. So I had to do update_attributes so 'limits' is set to whatever is defined in the factory girl.
This all works OK except for cases when limits is overridden by a spec.
Here is the code:
factory :organization do
sequence(:name) {|n| "org#{n + $offset}" }
.....
limits_attributes :projectsLimit => 1, :svnEnabled => true, :deployEnabled => true
...
after(:create) do | org |
sub = FactoryGirl.create(:subscription, :organization => org)
sp = FactoryGirl.create(:subscription_plan, :subscription => sub)
sub.subscription_plans << sp
org.subscription = sub
org.status = :active
org.save
# Since entitlements are messed up after setting sub-plan set them here.
org.limits.update_attributes(:projectsLimit => 1, :svnEnabled => true, :deployEnabled => true)
end
end
This fails when 'limits' is overridden, clearly because I am not getting the overridden attributes:
FactoryGirl.create(:organization, :limits_attributes => {:gitEnabled => true, :projectsLimit => 3})
I tried getting attributes using attributes_for, I misinterpreted attributes_for to get the values passed to 'create the organization'
org_attrs = FactoryGirl.attributes_for(:organization)
puts "atts #{org_attrs}"
org.save
# Since entitlements are messed up after setting sub-plan set them here.
org.limits.update_attributes(org_attrs[:limits_attributes])
Given that I cannot change the logic in Organization's after_save, how can I update limits if user overrides them? Where in the flow of things are overrides applied to the models defined in FactoryGirl.
I think there might be a flaw in how I interpret what the code is doing OR the scenario I m looking to solve does not fit how FG is used.
Thanks.
I was incorrect about the overridden values. org has the values set before the save (overriden or default, whatever the case). So just saving a copy before and reusing it did it. Not sure if this is a good solution though
org_limits = org.limits.dup
org.save
# Since entitlements are messed up after setting sub-plan set them here.
org.limits.update_attributes(org_limits.attributes)

Eager Loading aggregate roots with Entity Framework

I would like to create a more structured approach to loading the needed entity-tree:
I need a serious amount of data, so I'm doing this using type-safe Includes (just a normal Include but with Lambda's) as shown here.
As I said, I need a lot of data, basically a whole entity tree under 1 parent item.
Now, I could do this doing something like:
context.House
.Include(x => x.Doors)
.Include(x => x.Doors.FirstOrDefault().Joint)
.Include(x => x.Doors.FirstOrDefault().Joint.FirstOrDefault().JointCategory)
.Include(x => x.Doors.FirstOrDefault().Joint.FirstOrDefault().JointCategory.JointType)
.Include(x => x.Windows)
// ... same thing
.Include(x => x.Roof)
// ... same thing
As you can see, this line filled with includes can get quite huge. This is in fact a very simplified sample of the actual code (which doesn't include houses btw)
So what I would like to do is creating methods, responsible for its branch in the tree. Where the method can accept the object query and include the child, and in its turn, call the "child-loader methods". Also, the parent shouldn't matter, as long as it has a property with the type of the child.
This probably does not make much sense so:
public void LoadHouse(int id)
{
// ...
ObjectQuery<House> query = context.House;
// and now?
LoadDoors(query, x => x.Door);
}
public void LoadDoors<T>(ObjectQuery<T> query, ..?..)
{
// ... ?
LoadJoints(...)
}
And so on. But I can't really get my head around it... There's a missing link between the incoming query and calling the child methods.
Has anyone done something like this? Or could anyone give me some pointers?
Try something like this instead:
query = LoadDoors(query, x => x.Door);
Where LoadX returns the result of calling Include.