Why is Automapper.ProjectTo() throwing a null reference exception? - entity-framework

I have a mapping:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Foo, FooDto>()
.ForMember(dest => dest.Id,
opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Name,
opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.PhoneNumber,
opt => opt.MapFrom(src => src.PhoneNumbers.Number)) //etc.
});
and I'm trying to use it in a unit test calling into some mocked up EF objects:
var ids = new List<string>()
{
"123";
"456";
"789";
};
var data = new List<Foo>();
foreach(var id in ids)
{
data.Add(new Foo() { Id = id });
}
this.mockContext = new Mock<entities>();
this.mockSet = new Mock<DbSet<Foo>>();
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.Provider).Returns(data.Provider);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.Expression).Returns(data.Expression);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.ElementType).Returns(data.ElementType);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.GetEnumerator()).Returns(data.GetEnumerator());
When I query the entities directly:
var id = "123";
var bar = this.mockContext.Object.Foo.Where(p => p.id == id);
I get back an IQueryable() with a single result, as expected. But when I try to project my object into a DTO:
var id = "123";
var buzz = this.mockContext.Object.Foo.Where(p => p.id == id).ProjectTo<FooDto>(this.config);
The IQueryable I get back throws a Null Reference Exception if I try to access the results in any way. So for example:
buzz.ToList();
buzz.SingleOrDefault(); // This mirrors the actual call in my code since this is a GetById query.
both fail. What am I missing here?

The problem lies is that Foo uses EF navigation properties to refer to other objects. Specifically a PhoneNumber in this instance. Since the test data is created without the linked PhoneNumber object, it breaks inside of the ProjectTo method. This isn't a problem when grabbing the top level Queryable directly, but Automapper needs the objects to exist (even if they're empty) in order to complete the mapping. Changing that line to:
data.Add(new Foo() { Id = id, PhoneNumber = new PhoneNumber() });
allows the ProjectTo method to complete, albeit with null values.

Related

Get all the roles after include with main entity in Entity framework Core

I have three entities which are Account, Role and AccountRole. Through repository pattern I have fetched all the accounts, roles and accountRoles like below:
var accounts = _accountRepository.Query()
.Include(x => x.AccountRoles)
.ThenInclude(x => x.Role);
Now I would like to get a single account information with all it's roles. So I have written the below query. But the problem is only first role comes out.
var userAccount = accounts.Where(x => x.Id == id)
.Select(x => new AccountDto
{
Id = x.Id,
Name = x.UserFullName,
FirstName = x.FirstName,
LastName = x.LastName,
Email = x.Email,
Mobile = x.Mobile,
UserName = x.UserName,
PhotoUrl = x.PhotoUrl,
IsActive = x.IsActive,
Roles = accounts.Where(x => x.Id == id).Select(x => new RoleDto
{
Title = x.AccountRoles
.Where(x => x.IsActive == true)
.Select(x => x.Role.Title).FirstOrDefault()
}).ToList()
}).FirstOrDefault();
Can anyone help me to write the proper query?
First, this part
Roles = accounts.Where(x => x.Id == id).Select(x => new RoleDto
{
Title = x.AccountRoles
.Where(x => x.IsActive == true)
.Select(x => x.Role.Title).FirstOrDefault()
}).ToList()
is quite strange mixture, you should simply use the navigation property (yes, it is not only for includes as some people think).
Second, don't apply FirstOrDefault() when you want to get all items rather than the first only
// here x variable type is Account (coming from the outer Select)
Roles = x.AccountRoles
.Where(ar => ar.IsActive)
.Select(ar => new RoleDto
{
Title = ar.Role.Title,
}).ToList()

How to EF.Property<T> method works with included queries

I trying write a generic method for create response to datatables ajax request;
public static Response<T> CreateResponse<T>(IQueryable<T> query, Request request) where T : class
{
query = query.AsNoTracking();
var filtered = query;
if (!string.IsNullOrEmpty(request.Search.Value))
{
var keywords = Regex.Split(request.Search.Value, #"\s+").ToList();
request
.Columns
.Where(p => p.Searchable)
.ToList()
.ForEach(c =>
keywords.ForEach(k =>
{
filtered = filtered.Intersect(query.Where(p => EF.Functions.Like(EF.Property<string>(p, c.Name), $"%{k}%")));
})
);
}
var ordered = filtered;
request.Order.ForEach(p => ordered = p.Dir == "asc" ? ordered.OrderBy(q => EF.Property<T>(q, request.Columns[p.Column].Name)) : ordered.OrderByDescending(q => EF.Property<T>(q, request.Columns[p.Column].Name)));
var paged = ordered.Skip(request.Start).Take(request.Length);
return new Response<T> { draw = request.Draw, recordsTotal = query.Count(), recordsFiltered = filtered.Count(), data = paged.ToList() };
}
My problem is, when query parameter is IIncludableQueryable EF.Property method can't locate sub properties. For example;
DataTables.CreateResponse<Rayon>(context.Rayons.Include(p=>p.User), parameters);
EF.Property<T>.Property<string>(p, "Name") is working but, EF.Property<T>.Property<string>(p, "User.Name") is not working, exception message is "EF.Property called with wrong property name."
Sorry for bad English.

Prevent sort result of union in entity framework

In SQL server union, result is sorted based on primary key column. I want to prevent this behavior in entity framework.
In this post, #praveen has explained how to do this in pure sql. But I want to do this in entity framework.
My code:
public virtual ActionResult Search(string keyword)
{
var products = _db.Products
.Where(x => x.IsActive)
.AsQueryable();
var productExactlyTitle = products.Where(x => x.Title == keyword);
var productStartTitle = products.Where(x => x.Title.StartsWith(keyword));
var productContainsTitle = products.Where(x => x.Title.Contains(keyword)
|| x.Title.Contains(keyword)
|| x.SubTitle.Contains(keyword)
|| x.OtherName.Contains(keyword));
var productList = productExactlyTitle.Union(productStartTitle)
.Union(productContainsTitle)
.Take(10)
.AsEnumerable()
.Select(x => new ProductItemViewModel()
{
Id = x.Id,
Title = x.Title,
Price = x.Price.ToPrice(),
Image = x.Images.FirstOrDefault(y => y.IsCoverPhoto)?.ImageUrl
});
// some code ...
}
I want to show records with below order:
First: records of productExactlyTitle
Second: records of productStartTitle
Third: records of productContainsTitle
But result is sorted with Id column! and I don't want this.
Is there a way for do this?
In SQL all queries without an order by explicitly set is considered unordered. (and EF queries a translated into SQL). So if you want a specific order after your union just specify it.
var result = q1.Union(q2).OrderBy(x => x.?);
For your specific case:
var p1 = productExactlyTitle.Select(x => new { Item = x, Order = 1 });
var p2 = productStartTitle.Select(x => new { Item = x, Order = 2 });
var p3 = productContainsTitle.Select(x => new { Item = x, Order = 3 });
var productList = p1.Union(p2)
.Union(p3)
.OrderBy(x => x.Order)
.Select(x => x.Item)
.Take(10);

Register multiple dbcontext using autofac

We have application in asp.net MVC with DDD architecture with autofac IOC container. We are trying to register two dbcontext with different database connect. But the only last one is came in to effect. We are using Entity Framework 4.4.0. Here is the code.
var masterDataSettingManager = new SaasDataSettingManager();
if (masterDataSettingManager.LoadSettings() != null)
{
var masterProviderSettings = masterDataSettingManager.LoadSettings();
builder.Register(c => masterDataSettingManager.LoadSettings()).As<DataSettings>();
builder.Register(x => new EfDataProviderManager(x.Resolve<DataSettings>())).As<BaseDataProviderManager>().InstancePerDependency();
builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IDataProvider>().InstancePerDependency();
builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IEfDataProvider>().InstancePerDependency();
if (masterDataSettingManager != null && masterProviderSettings.IsValid())
{
var efDataProviderManager = new EfDataProviderManager(masterDataSettingManager.LoadSettings());
var dataProvider = (IEfDataProvider)efDataProviderManager.LoadDataProvider();
dataProvider.InitConnectionFactory();
var dbProviderFactory = efDataProviderManager.LoadDbProviderFactories();
builder.Register<IDbContext>(c => new MyDbContext1(masterProviderSettings.DataConnectionString, dbProviderFactory)).InstancePerHttpRequest();
}
else
{
builder.Register<IDbContext>(c => new MyDbContext1(masterDataSettingManager.LoadSettings().DataConnectionString)).InstancePerHttpRequest();
}
}
//data layer
var dataSettingsManager = new DataSettingsManager();
var dataProviderSettings = dataSettingsManager.LoadSettings();
builder.Register(c => dataSettingsManager.LoadSettings()).As<DataSettings>();
builder.Register(x => new EfDataProviderManager(x.Resolve<DataSettings>())).As<BaseDataProviderManager>().InstancePerDependency();
builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IDataProvider>().InstancePerDependency();
builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IEfDataProvider>().InstancePerDependency();
if (dataProviderSettings != null && dataProviderSettings.IsValid())
{
var efDataProviderManager = new EfDataProviderManager(dataSettingsManager.LoadSettings());
var dataProvider = (IEfDataProvider)efDataProviderManager.LoadDataProvider();
dataProvider.InitConnectionFactory();
var dbProviderFactory = efDataProviderManager.LoadDbProviderFactories();
builder.Register<IDbContext>(c => new MyDbContext2(dataProviderSettings.DataConnectionString, dbProviderFactory)).InstancePerHttpRequest();
}
else
{
builder.Register<IDbContext>(c => new MyDbContext2(dataSettingsManager.LoadSettings().DataConnectionString)).InstancePerHttpRequest();
}
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerHttpRequest();
Register them like this:
builder
.Register(c =>
new MyDbContext1(dataProviderSettings.DataConnectionString, dbProviderFactory))
.Named<IDbContext>("dbContext1").InstancePerHttpRequest();
builder
.Register(c =>
new MyDbContext2(dataProviderSettings.DataConnectionString, dbProviderFactory))
.Named<IDbContext>("dbContext2").InstancePerHttpRequest();
And then resolve them:
IDbContext dbContext = ctx.ResolveNamed<IDbContext>("dbContext1");
If you would like to inject it in a constructor you may do it like this:
builder
.RegisterType<SomeService>()
.WithParameter(
(p, c) => p.Name == "dbContext",
(p, c) => c.ResolveNamed<IDbContext>("dbContext1")
);
Apart from having named registrations pf dbContexts you still can have unnamed defined in parallel with them - it may be treated as default one. Iy then in your application you would like rather to use a named registration then you specify this desire in the above way.

Entity Framework. Where clause with parameter from query as variable

Task: use different where clause in one query
Here is example (it is not real query, just to illustrate the problem)
var events = ctx.Events; // ctx - EntityFramework context
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// how to get this clause from variable
.Where(ee => ee.State == gg.Key)
.Take(2)
})
.ToList();
Next code did not work, the problem is that where expression use parameter from query gg.Key
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// 1
// how to get this clause from variable
//.Where(ee => ee.State == gg.Key)
// 2
// try to take out where expression from query
.Where(_buildExpression(gg.Key))
.Take(2)
})
.ToList();
// method
static Expression<Func<Event, bool>> _buildExpression(string state)
{
return ee => ee.State == state;
}
// exeption
An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.SqlServer.dll
Additional information: variable 'gg' of type 'System.Linq.IGrouping`2[System.String,Entities.Event]' referenced from scope '', but it is not defined
Example of getting where expression from variable, but does not depend on gg.Key (wrong)
Expression<Func<Event, bool>> whereClause = (ee) => (ee.State == "test");
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// 1
// how to get this clause from variable
//.Where(ee => ee.State == gg.Key)
// 2
// try to take out where expression from query
//.Where(_buildExpression(gg.Key))
// 3
// whereClause from variable, but does not depend on gg.Key
.Where(whereClause)
.Take(2)
})
.ToList();
How to take where сlause from variable with depend on gg.Key?
p.s. the query is just example of the problem. The code below does not solve the problem of real query:
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = gg.Take(2)
})
.ToList();
Solution by OP.
Thanks to Ivan Stoev comment.
Expression<Func<Event, string, bool>> whereClause = (ee, state) => (ee.State == state);
var events = ctx.Events;
var res = events
.AsExpandable() // <= add this
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
.Where(ee => whereClause.Invoke(ee, gg.Key)) // <= Invoke expression
.Take(2)
})
.ToList();
This was made possible by LinqKit