IQueryable Extension Behavior Differing For Automapper Polymorphic Collection - linq

Using Automapper 3.3.1.0 there is a different mapping behavior between the usage of Mapper.Map<IEnumerable<TDestination>>(someEnumerable) compared to someEnumerable.AsQueryable().Project().To<TDestination>()
This does not appear to be a limitation of a SQL LINQ provider or other as this is witnessed in an in-memory collection.
As with many things this is best explained by example:
Note: the following code can be found at https://gist.github.com/kmoormann/b3949d006f4083ab6ee4
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using FluentAssertions;
using NUnit.Framework;
namespace Automapper.PolymorphicList.Tests
{
[TestFixture]
class AutomapperQueryableExtensionPolymorphism
{
//taking the class structure from: https://github.com/AutoMapper/AutoMapper/wiki/Mapping-inheritance
public class Order { }
public class OnlineOrder : Order
{
public string Referrer { get; set; }
}
public class MailOrder : Order { }
//Dtos
public class OrderDto
{
public string Referrer { get; set; }
}
[Test(Description = "Does the same mapping behavior exist for a polymorphic list when doing the project querable extension as when doing the static mapper map method()")]
public void IsSameBehaviorForQueryableExtensionAndStaticMap()
{
Mapper.Reset();
//Mappings
Mapper.CreateMap<Order, OrderDto>()
.Include<OnlineOrder, OrderDto>()
.Include<MailOrder, OrderDto>()
.ForMember(o => o.Referrer, m => m.Ignore());
Mapper.CreateMap<OnlineOrder, OrderDto>();
Mapper.CreateMap<MailOrder, OrderDto>();
//build lists
var onlineOrders = new List<OnlineOrder>() { new OnlineOrder() { Referrer = "one" }, new OnlineOrder() { Referrer = "two" } };
var mailOrders = new List<MailOrder>() { new MailOrder() };
//single typed list mapping
var mappedOnlineOrderDtos = Mapper.Map<IEnumerable<OrderDto>>(onlineOrders);
var projectedOnlineOrderDtos = onlineOrders.AsQueryable().Project().To<OrderDto>();
//using FluentAssertions for collection assertions
projectedOnlineOrderDtos.ShouldBeEquivalentTo(mappedOnlineOrderDtos, "automapper can handle singly typed lists");
//other single typed list mapping
var mappedMailOrderDtos = Mapper.Map<IEnumerable<OrderDto>>(mailOrders);
var projectedMailOrderDtos = mailOrders.AsQueryable().Project().To<OrderDto>();
projectedMailOrderDtos.ShouldBeEquivalentTo(mappedMailOrderDtos, "automapper can handle singly typed lists");
//build a polymorphic list
var orders = new List<Order>();
orders.AddRange(onlineOrders);
orders.AddRange(mailOrders);
// Perform Mapping and Projection
var mappedPolymorhpicOrders = Mapper.Map<IEnumerable<OrderDto>>(orders);
var projectedPolymorphicOrders = orders.AsQueryable().Project().To<OrderDto>();
projectedPolymorphicOrders.ShouldBeEquivalentTo(mappedPolymorhpicOrders, "automapper can handle polymorphic typed lists?");
}
}
}
I understand there are limitations to the .Project().To<TDestination> IQueryable extensions but what I am unaware of is:
which limitation is causing this behavior?
is this a Automapper limitation or a LINQ limitation
is there a work around to still use the queryable extensions and not revert Mapper.Map<TDestination>(obj) exclusively?
for posterity: link discussion thread topic

This is a LINQ limitation. AutoMapper does not inherit base mappings for LINQ. What would be the Select expression to do polymorphic Select projections? Trying to do that leads you to where you can't do this. LINQ query providers don't support it.

Related

elasticsearch nest 7.1 aggregation Fields generic shorthand field expression

so reading this:
https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.x/field-inference.html
given a class of
public class MyData {
public Guid UserId { get; set; }
public decimal Value { get; set; }
}
I would expect to be able to do something like:
var fieldExpression = Field<MyData >(p => p.Value);
but I get an error of The non-generic type Field cannot be used with type arguments.
I am using Nest 7.1.0.
My goal was to be able to create a method that can have aggregations and queries passed in and then combined into running on a instance of a nest ElasticClient.
something like (I think)
var sr = new SearchRequest<MyData>
{
Aggregations = new ChildrenAggregation("name_of_child_agg", typeof(decimal?))
{
Aggregations =
new AverageAggregation("average_per_child", Field<MyData>(p => p.value))
&& new MaxAggregation("max_per_child", Field<MyData>(p => p.value))
&& new MinAggregation("min_per_child", Field<MyData>(p => p.value))
}
};
_client.Search<MyData>(sr);
As stated in the documentation you need to add a static import to be able to write the code using the same style
using static Nest.Infer;
Otherwise you need to use simple new Field(..) instantiation

Can you get strongly-type hits from InnerHits using NEST 2?

I'm using the NEST 2 client to perform queries against our Elasticsearch.
We've got a query that queries against an array of Nested documents and use .InnerHits() to include the matches of the nested documents in the result.
Is there a way to strongly type the results of the InnerHits to a particular POCO class instead of dynamically accessing the resulting fields?
Yes there is; Here's an example from the integration tests for inner hits; the principles are the same for any search that can return inner hits
public interface IRoyal
{
string Name { get; set; }
}
[ElasticsearchType(IdProperty = "Name")]
public abstract class RoyalBase<TRoyal> : IRoyal
where TRoyal : class, IRoyal
{
public string Name { get; set; }
}
public class King : RoyalBase<King>
{
public List<King> Foes { get; set; }
}
public class Prince : RoyalBase<Prince> { }
public class Duke : RoyalBase<Duke> { }
public class Earl : RoyalBase<Earl> { }
public class Baron : RoyalBase<Baron> { }
Each King has a list of other Kings who are the foes of the king.
A King has n Prince children and
Each Prince has n Duke children
Each Duke has n Earl children
Each Earl has n Baron children
So, we have four descending Parent/Child relationships. The id to use for each document is the Name property on the document.
An example of a strongly typed inner hit search with such a setup would be
var response = client.Search<Duke>(s => s
.Index(index)
.InnerHits(ih => ih
.Type<Earl>("earls", g => g
.Size(5)
.InnerHits(iih => iih
.Type<Baron>("barons")
)
.FielddataFields(p => p.Name)
)
)
);
And then handling the response, demonstrating a couple of ways for strongly typed access
foreach (var hit in response.Hits)
{
// each hit here is a Hit<ILazyDocument> i.e.
// no strongly typed access
var earlHits = hit.InnerHits["earls"].Hits;
// strongly typed access to documents using
// .Documents<T>()
var earls = earlHits.Documents<Earl>();
foreach (var earlHit in earlHits.Hits)
{
// use Source.As<T> to access source strongly typed
var earl = earlHit.Source.As<Earl>().Name;
var baronHits = earlHit.InnerHits["barons"];
// strongly typed access to documents
var baron = baronHits.Documents<Baron>();
// do something with baron documents
}
}
Types need to be known when accessing response because the client does not store or persist the relationship of name of the inner hit to the C# type.
Inner hits are internally deserialized to Json.Net JObject types, with .Source.As<T>() and .Documents<T>() using JToken.ToObject<T>() within Json.Net to perform the conversion.

How do you manually construct a lambda expression?

I'm designing an application using ASP.NET Web API and Entity Framework 5 and LINQ to Entities. The Web API doesn't serve up the entities directly, it converts them to a set of data transfer objects that are similar but not identical to my entities. The API will be used by a Silverlight application initially but I will have to support non-.NET clients (e.g. iOS apps) down the road. I'd also like to give the client the ability to run a robust set of queries against the API.
These requirements have lead me to consider the query object pattern. Essentially, I want to create a homegrown query object client-side, post it to the Web API, and convert the query object to a lambda expression that I can use in LINQ to Entities. This last part is what's tripping me up.
Starting with a simple comparison query, I want to be able to convert an object that looks like the following into a lambda expression at runtime.
public enum QueryOperator
{
None = 0,
GreaterThan,
GreaterThanOrEqualTo,
EqualTo,
NotEqualTo,
LessThanOrEqualTo,
LessThan
}
public class SimpleQuery<T>
{
public SimpleQuery()
{
this.Field = null;
this.Operator = QueryOperator.None;
this.Value = null;
}
public string Field { get; set; }
public QueryOperator Operator { get; set; }
public object Value { get; set; }
public IEnumerable<T> Execute(IQueryable<T> queryTarget)
{
// ????
}
}
How can I do this?
I've had to do things like this in the past. Here's what I came up with:
public IEnumerable<T> Execute(IQueryable<T> queryTarget)
{
return queryTarget.Where(this.GetWhereExpression<T>());
}
private Expression<Func<T, bool>> GetWhereExpression<T>()
{
var param = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(param, this.Field);
var value = Expression.Constant(this.Value, prop.Type);
Expression compare = null;
switch(this.Operator)
{
case QueryOperator.EqualTo:
compare = Expression.Equal(prop, value);
break;
...
}
return Expression.Lambda(compare, param);
}

How can i load collection with include

How can I load collection with include, I have tried these:
Type.Where(t => t.Entity.Where(e => e.Parent == false).Count() > 0).Include(e => e.Entity)
But the filter is not supported here (Entities recovered not fulfill a condition).
With LinQ To Entities :
var v = from type in ObjectContext.Type
from entity in Type.Entities
where entity.Parent == false
select type;
But type do not contains it's associated Entities.
I can't write select new Type { type.Code, type.Entities } and I can't use anonymous type as it's not adapted for my repository layer.
Have you any ideas on how I can get a list of Type objects with its property Entities satisfying a condition?
Update
Thank you for your response
I think it's not a good idea to use the CTP , no ?
I am sorry, I did not explain my object model, I have :
class Type {
public string Code { get; set; }
public IList<Entity> Entities { get; set; }
...
}
class Entity {
public string Code { get; set; }
...
}
And I want to use your second filtering proposal : Filter both entity sets
Do you have any suggestions without using the CTP ?
Thanks
Rad
as you stated you want to apply filters to the related entities, best to look at the new CTP5 of the EF4 http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
Look under "Applying filters when explicitly loading related entities"

NHibernate: HasMany components and Where/Contains clause

I'm trying to work out how to create a query using Linq to NHibernate.
I have two classes like this:
public class Foo
{
private ISet<Bar> _bars = new HashedSet<Bar>();
public virtual ISet<Bar> Bars
{
get { return _bars; }
set { _bars = value; }
}
}
public class Bar
{
public string Name { get; set; }
public string Color { get; set; }
}
Foo's Bar collection is mapped as a one-to-many component collection.
Now I want to run a query that should look something like this:
var myBar = new Bar { Name = "test", Color = "testColor" };
var matchingFoos = Session.Linq<Foo>
.Where(foo => foo.Bars.Contains(myBar),
new BarEqualityComparer())
.ToList();
I am not sure if this is correct, but whenever I run this query I get a NullReferenceException from inside a method called NHibernate.Linq.Visitors.WhereArgumentsVisitor.GetCollectionContainsCriteria method.
Could anyone help me out with an alternative means of running this query?
The BarEqualityComparer will be the point of failure for sure. There is no simple way for the provider to translate custom class to an SQL statement.

Resources