Includes doesn't work with LinqKit AsExpandable - entity-framework

I'm trying to use LinqKit AsExpandable in my EfCore2.0 project and I am running into this problem where the Includes doesn't work.
In attempting to debug this I have downloaded the LinqKit source from github and have replaced the Nuget references in my project with Project references.
When debugging with the LinqKit project, I noticed that the my call to Include wasn't hitting a breakpoint I set on ExpandableQueryOfClass<T>.Include.
I did some further testing and noticed the breakpoint is hit if I first cast to ExpandableQueryOfClass (this is an internal class in LinqKit which I made public, so I can't do the cast if I'm referencing the Nuget package).
Is this a bug in LinqKit or am I doing something wrong?
Here is my test code.
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Internal.DAL.Db;
using Internal.Models.Customer;
using LinqKit; // Referencing LinqKit.Microsoft.EntityFrameworkCore
using Xunit;
namespace Internal.EntityFramework.Tests
{
public class UnitTest1
{
private DbContextOptionsBuilder<DataContext> _ctxBuilder =>
new DbContextOptionsBuilder<DataContext>().UseSqlServer(Connection.String);
[Fact]
public async Task SuccessTest()
{
using (var ctx = new DataContext(_ctxBuilder.Options))
{
var query =
(
// this cast is the only difference between the methods
(ExpandableQueryOfClass<Order>)
ctx.Orders
.AsExpandable()
)
.Include(r => r.Customer)
.Take(500);
var responses = await query.ToListAsync();
// this succeeds
Assert.All(responses, r => Assert.NotNull(r.Customer));
}
}
[Fact]
public async Task FailTest()
{
using (var ctx = new DataContext(_ctxBuilder.Options))
{
var query = ctx.Orders
.AsExpandable()
.Include(r => r.Customer)
.Take(500);
var responses = await query.ToListAsync();
// this fails
Assert.All(responses, r => Assert.NotNull(r.Customer));
}
}
}
}
Edit 2018-05-15: There is an open issue on the LinqKit github repository.

I'm not sure whether this is LINQKit or EF Core fault (definitely it's not yours).
For sure it's caused by the EF Core Include / ThenInclude implementations which all include a check for source.Provider is EntityQueryProvider and do nothing in case it's false.
I'm not sure what's the idea of EF Core designers - probably custom query providers to inherit from EntityQueryProvider, but at the same time the class is part of the infrastructure and marked as not supposed to be used.
I also have no idea how LINQKit is planning to address it, but as you noticed the current implementation definitely is broken/not working. At the moment it looks to me more like a WIP.
The only workaround I see at this time is to apply AsExpandable() after includes.

Related

How do I filter a result set in Entity Framework Core?

I have a dinky little web service written in Entity Framework Core that returns a list of coils that have been, are being or will be annealed. I don't care about the ones that have been annealed. How do I put filter out the finished ones?
The Inventory table includes a column archived. I want to query for those rows for which the archived column contains zero. How do I do that?
The query is being performed in an extension method on the IEndpointRouteBuilder interface:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Text.Json;
using CAPSWebServer.CapsDataModels;
namespace Microsoft.AspNetCore.Builder
{
public static class CAPSServiceEndpoint
{
public static void MapWebService(this IEndpointRouteBuilder app)
{
app.MapGet("caps/coils", async context =>
{
CapsDataContext data = context.RequestServices.GetService<CapsDataContext>();
var coils = data.Inventories;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize<IEnumerable<Inventory>>(data.Inventories));
});
}
}
}
Assuming archived is a bool, you should be able to do:
await data.Inventories.Where(inventory => !inventory.Archived).ToListAsync();.
You'll most likely need a using for System.Linq and Microsoft.EntityFrameworkCore

Entity Framework Core 1.0 CurrentValues.SetValues() does not exist

I'm attempting to update an entity and its related child entities using Entity Framework Core 1.0 RC 1, where the entities are detached from DbContext. I've done this previously using a solution similar to the one described in this answer.
However, it seems that we are no longer able to do the following using Entity Framework 7:
DbContext.Entry(existingPhoneNumber).CurrentValues.SetValues();
Visual Studio complains that:
EntityEntry does not contain a definition for 'CurrentValues'
etc...
I presume this means that this has not (yet?) been implemented for EF Core 1.0? Apart from manually updating the properties, is there any other solution?
As you have noticed, this API is not implemented yet in EF Core. See this work item: https://github.com/aspnet/EntityFramework/issues/1200
I know this is an old question but I ran into this issue today, and it appears it still isn't implemented in EF Core. So I wrote an extension method to use in the meantime that will update any object's properties with the matching values of any other object.
public static class EFUpdateProperties
{
public static TOrig UpdateProperties<TOrig, TDTO>(this TOrig original, TDTO dto)
{
var origProps = typeof(TOrig).GetProperties();
var dtoProps = typeof(TDTO).GetProperties();
foreach(PropertyInfo dtoProp in dtoProps)
{
origProps
.Where(origProp => origProp.Name == dtoProp.Name)
.Single()
.SetMethod.Invoke(original, new Object[]
{
dtoProp.GetMethod.Invoke(dto, null) });
}
);
return original;
}
}
Usage:
public async Task UpdateEntity(EditViewModel editDto)
{
// Get entry from context
var entry = await _context.Items.Where(p => p.ID == editDto.Id).FirstOrDefaultAsync();
// Update properties
entry.UpdateProperties(editDto);
// Save Changes
await _context.SaveChangesAsync();
}

Asp.Net Web API Error: The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'

Simplest example of this, I get a collection and try to output it via Web API:
// GET api/items
public IEnumerable<Item> Get()
{
return MyContext.Items.ToList();
}
And I get the error:
Object of type
'System.Data.Objects.ObjectQuery`1[Dcip.Ams.BO.EquipmentWarranty]'
cannot be converted to type
'System.Data.Entity.DbSet`1[Dcip.Ams.BO.EquipmentWarranty]'
This is a pretty common error to do with the new proxies, and I know that I can fix it by setting:
MyContext.Configuration.ProxyCreationEnabled = false;
But that defeats the purpose of a lot of what I am trying to do. Is there a better way?
I would suggest Disable Proxy Creation only in the place where you don't need or is causing you trouble. You don't have to disable it globally you can just disable the current DB context via code...
[HttpGet]
[WithDbContextApi]
public HttpResponseMessage Get(int take = 10, int skip = 0)
{
CurrentDbContext.Configuration.ProxyCreationEnabled = false;
var lista = CurrentDbContext.PaymentTypes
.OrderByDescending(x => x.Id)
.Skip(skip)
.Take(take)
.ToList();
var count = CurrentDbContext.PaymentTypes.Count();
return Request.CreateResponse(HttpStatusCode.OK, new { PaymentTypes = lista, TotalCount = count });
}
Here I only disabled the ProxyCreation in this method, because for every request there is a new DBContext created and therefore I only disabled the ProxyCreation for this case .
Hope it helps
if you have navigation properties and you do not want make them non virtual, you should using JSON.NET and change configuration in App_Start to using JSON not XML!
after install JSON.NET From NuGet, insert this code in WebApiConfig.cs in Register method
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
If you have navigation properties make them non virtual. Mapping will still work but it prevents the creation of Dynamic Proxy entities which cannot be serialized.]
Not having lazy loading is fine in a WebApi as you don't have a persistent connection and you ran a .ToList() anyway.
I just disabled proxy classes on a per needed basis:
// GET: ALL Employee
public IEnumerable<DimEmployee> Get()
{
using (AdventureWorks_MBDEV_DW2008Entities entities = new AdventureWorks_MBDEV_DW2008Entities())
{
entities.Configuration.ProxyCreationEnabled = false;
return entities.DimEmployees.ToList();
}
}
Add the following code in Application_Start function of Global.asax.cs:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters
.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
This instruct the API to serialize every response into JSON and remove XML responses.
In my case the object being returned had a property within it with a type that did not have an argumentless/default constructor. By adding a zero-argument constructor to that type the object could be serialized successfully.
I had the same problem and my DTO was missing an parameter less constructor.
public UserVM() { }
public UserVM(User U)
{
LoginId = U.LoginId;
GroupName = U.GroupName;
}
First constructor was missing.
I got this error message and it turns out the problem was that I had accidentally set my class to use the same serialized property name for two properties:
public class ResultDto
{
//...
[JsonProperty(PropertyName="DataCheckedBy")]
public string ActualAssociations { get; set; }
[JsonProperty(PropertyName="DataCheckedBy")]
public string ExpectedAssociations { get; set; }
//...
}
If you're getting this error and you aren't sending entities directly through your API, copy the class that's failing to serialize to LINQPad and just call JsonConvert.SerializeObject() on it and it should give you a better error message than this crap. As soon as I tried this it gave me the following error message: A member with the name 'DataCheckedBy' already exists on 'UserQuery+ResultDto'. Use the JsonPropertyAttribute to specify another name.
After disable Proxy Creation, use eager loading (Include()) to load the proxy object.
In my Project EntityCollection returned from the WebApi action method.
Configuration.ProxyCreationEnabled = false not applicable. I have tried the below approach it is working fine for me.
Control Panel.
2.Turn on Windows Features on or off
Choose Internet Information Service
Check all the World Wide Web Components it would be better to check all the components in IIS.
Install the components.
Go to (IIS) type inetmgr in command prompt.
select the published code in the Virtual directory.
Convert into application
Browse it the application.
The answer by #Mahdi perfectly fixes the issue for me, however what I noticed is that if my Newtonsoft.JSON is 11.0 version then it doesn't fix the issue, but the moment I update Newtonsoft.JSON to latest 13.0 it starts working.

Unit testing with EF Code First DataContext

This is more a solution / work around than an actual question. I'm posting it here since I couldn't find this solution on stack overflow or indeed after a lot of Googling.
The Problem:
I have an MVC 3 webapp using EF 4 code first that I want to write unit tests for. I'm also using NCrunch to run the unit tests on the fly as I code, so I'd like to avoid backing onto an actual database here.
Other Solutions:
IDataContext
I've found this the most accepted way to create an in memory datacontext. It effectively involves writing an interface IMyDataContext for your MyDataContext and then using the interface in all your controllers. An example of doing this is here.
This is the route I went with initially and I even went as far as writing a T4 template to extract IMyDataContext from MyDataContext since I don't like having to maintain duplicate dependent code.
However I quickly discovered that some Linq statements fail in production when using IMyDataContext instead of MyDataContext. Specifically queries like this throw a NotSupportedException
var siteList = from iSite in MyDataContext.Sites
let iMaxPageImpression = (from iPage in MyDataContext.Pages where iSite.SiteId == iPage.SiteId select iPage.AvgMonthlyImpressions).Max()
select new { Site = iSite, MaxImpressions = iMaxPageImpression };
My Solution
This was actually quite simple. I simply created a MyInMemoryDataContext subclass to MyDataContext and overrode all the IDbSet<..> properties as below:
public class InMemoryDataContext : MyDataContext, IObjectContextAdapter
{
/// <summary>Whether SaveChanges() was called on the DataContext</summary>
public bool SaveChangesWasCalled { get; private set; }
public InMemoryDataContext()
{
InitializeDataContextProperties();
SaveChangesWasCalled = false;
}
/// <summary>
/// Initialize all MyDataContext properties with appropriate container types
/// </summary>
private void InitializeDataContextProperties()
{
Type myType = GetType().BaseType; // We have to do this since private Property.Set methods are not accessible through GetType()
// ** Initialize all IDbSet<T> properties with CollectionDbSet<T> instances
var DbSets = myType.GetProperties().Where(x => x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>)).ToList();
foreach (var iDbSetProperty in DbSets)
{
var concreteCollectionType = typeof(CollectionDbSet<>).MakeGenericType(iDbSetProperty.PropertyType.GetGenericArguments());
var collectionInstance = Activator.CreateInstance(concreteCollectionType);
iDbSetProperty.SetValue(this, collectionInstance,null);
}
}
ObjectContext IObjectContextAdapter.ObjectContext
{
get { return null; }
}
public override int SaveChanges()
{
SaveChangesWasCalled = true;
return -1;
}
}
In this case my CollectionDbSet<> is a slightly modified version of FakeDbSet<> here (which simply implements IDbSet with an underlying ObservableCollection and ObservableCollection.AsQueryable()).
This solution works nicely with all my unit tests and specifically with NCrunch running these tests on the fly.
Full Integration Tests
These Unit tests test all the business logic but one major downside is that none of your LINQ statements are guaranteed to work with your actual MyDataContext. This is because testing against an in memory data context means you're replacing the Linq-To-Entity provider but a Linq-To-Objects provider (as pointed out very well in the answer to this SO question).
To fix this I use Ninject within my unit tests and setup InMemoryDataContext to bind instead of MyDataContext within my unit tests. You can then use Ninject to bind to an actual MyDataContext when running the integration tests (via a setting in the app.config).
if(Global.RunIntegrationTest)
DependencyInjector.Bind<MyDataContext>().To<MyDataContext>().InSingletonScope();
else
DependencyInjector.Bind<MyDataContext>().To<InMemoryDataContext>().InSingletonScope();
Let me know if you have any feedback on this however, there are always improvements to be made.
As per my comment in the question, this was more to help others searching for this problem on SO. But as pointed out in the comments underneath the question there are quite a few other design approaches that would fix this problem.

autofac registration issue in release v2.4.5.724

I have the following registration
builder.Register<Func<Type, IRequestHandler>>(
c => request => (IRequestHandler)c.Resolve(request));
Basically I am trying to register a factory method that resolves an instance of IRequestHandler from a given type.
This works fine until the version 2.4.3.700. But now I am getting a the following error..
Cannot access a disposed object.
Object name: 'This resolve operation has already ended. When
registering components using lambdas,
the IComponentContext 'c' parameter to
the lambda cannot be stored. Instead,
either resolve IComponentContext again
from 'c', or resolve a Func<> based
factory to create subsequent
components from.'.
UPDATE
I was trying to limit autofac's exposure to the rest of the projects in the solution. Nick, thanks for the hint, now my registration looks like this...
builder.Register<Func<Type,IRequestHandler>>(c =>
{
var handlers = c.Resolve<IIndex<Type,RequestHandler>>();
return request => handlers[request];
});
The c in this expression is a temporary, so this code while previously functional, is broken. Autofac 2.4.5 detects this problem while earlier versions silently ignored it.
To fix the issue, explicitly resolve IComponentContext:
builder.Register<Func<Type, IRequestHandler>>(c => {
var ctx = c.Resolve<IComponentContext>();
return request => (IRequestHandler)ctx.Resolve(request));
});
The functionality you're emulating here might be better represented using keys and indexes, e.g. see Interrupted chain of IoC or http://code.google.com/p/autofac/wiki/TypedNamedAndKeyedServices.
I had a similar problem as the user6130. I wanted to avoid using IIndex in my class implementation and pass in a service resolver into my constructor instead.
So now I have my service implementation with the following constructor:
public MvcMailer(Converter<string, MailerBase> mailerResolver)
{
_resolver = mailerResolver;
}
I wanted to used keyed services without directly relying on the Autofac namespace. I was getting the same error until I restructured the configuration as such.
1) Scan for all my mailer implementations and index via class name (could be improved)
builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly())
.Where(t => t.Name.EndsWith("Mailer")).Keyed<Mvc.Mailer.MailerBase>(t => t.Name.Replace("Mailer", "").ToLower());
2) Register the converter in Autofac config
builder.Register<System.Converter<string,Mvc.Mailer.MailerBase>>(c => {
var all = c.Resolve<Autofac.Features.Indexed.IIndex<string,Mvc.Mailer.MailerBase>>();
return delegate(string key)
{
return all[key];
};
});
3) Register like other types of components and let Autofac handle the Converter injection
builder.RegisterType<Mailers.MvcMailer>().As<Mailers.IMailer>();