Entity Framework: Mixed predicates of mixed entities - linq

Sorry for my awful English.
I'm trying to extend my EF model (with LinqKit).
As long as I work with single entities expressions, I don't have any issue, but if I want to work with mixed expressions, I'm simply stuck.
For instance, a sort-of-post class like that...
partial class vit_post
{
//[...]
public static Expression<Func<vit_post, bool>> HasMetas(IEnumerable<MetaFilter> metaArgs)
{
var resultsInner = PredicateBuilder.True<vit_post>();
resultsInner.And(p=>p.vit_postmeta.Any(pm => (pm.hide == false)));
foreach (var metaArg in metaArgs)
{
var mf = metaArg;
resultsInner.And(p=>p.vit_postmeta.Any(pm => (pm.meta_key == mf.MetaKey && mf.Compare(pm.meta_value))));
if (mf.ChildrenMetaFilters != null)
{
Expression<Func<vit_post, bool>> resultsInner2;
switch (mf.LogicalOperator)
{
case LogicalOperators.AND:
resultsInner2 = PredicateBuilder.True<vit_post>();
resultsInner = resultsInner2.And(HasMetas(mf.ChildrenMetaFilters));
break;
case LogicalOperators.OR:
resultsInner2 = PredicateBuilder.False<vit_post>();
resultsInner = resultsInner2.Or(HasMetas(mf.ChildrenMetaFilters));
break;
}
}
}
return resultsInner;
}
//[...]
}
...extends an entity "vit_post" with an expression HasMetas.
Using this snippet, I get a subset of the entities, as expected:
public SearchResults GetResults()
{
var query = dbc.vit_posts.AsExpandable();
//[...]
if (SearchArgs.Metas != null)
{
var postsbycontent = vit_post.HasMetas(SearchArgs.Metas);
outer = Data.Utility.And(outer, postsbycontent);
}
//[...]
query = query.Where(outer);
var searchResults = new SearchResults
{
Items = searchResultsItems
};
return searchResults;
}
But if I try to move this this expression to the "vit_postmeta" entity like that:
partial class vit_postmeta
{
//[...]
var resultsInner = PredicateBuilder.True<vit_postmeta>();
resultsInner.And(pm => (pm.hide == false));
foreach (var metaArg in metaArgs)
{
var mf = metaArg;
resultsInner.And(pm => (pm.meta_key == mf.MetaKey && mf.Compare(pm.meta_value)));
if (mf.ChildrenMetaFilters != null)
{
Expression<Func<vit_postmeta, bool>> resultsInner2;
switch (mf.LogicalOperator)
{
case LogicalOperators.AND:
resultsInner2 = PredicateBuilder.True<vit_postmeta>();
resultsInner = resultsInner2.And(HasMetas(mf.ChildrenMetaFilters));
break;
case LogicalOperators.OR:
resultsInner2 = PredicateBuilder.False<vit_postmeta>();
resultsInner = resultsInner2.Or(HasMetas(mf.ChildrenMetaFilters));
break;
}
}
}
return resultsInner;
//[...]
}
My idea was to keep the original method in vit_post and change it like that:
partial class vit_post
{
//[...]
public static Expression<Func<vit_post, bool>> HasMetas(IEnumerable<MetaFilter> metaArgs)
{
var resultsInner = PredicateBuilder.True<vit_post>();
resultsInner.And(p=>p.vit_postmeta.HasMetas(metaArgs));
return resultsInner;
}
//[...]
}
But I can't do that, as "'vit_postmeta' does not contain a definition for 'HasMetas' and no extension method 'HasMetas' accepting a first argument of type 'vit_postmeta' could be found".
I'm missing something, I know, but I can't find what.
UPDATE
I've found an alternate solution (and perhaps the proper one too), implementing all my expression in respective entity partial classes.
For instance, vit_post had all expressions related to vit_posts's table, whilst vit_postmeta has all expressions related to vit_postmeta's table.
My Search class then has a private method for each entity. Something like:
private IQueryable<vit_post> QueryPosts()
{
IQueryable<vit_post> queryPosts = VITContext.vit_posts.AsExpandable();
Expression<Func<vit_post, bool>> outerPredicate = PredicateBuilder.True<vit_post>();
if (!_searchArgs.IncludeExpiredPosts)
{
Expression<Func<vit_post, bool>> postsByExpiration = vit_post.ExcludeExpired();
outerPredicate = Data.Utility.And(outerPredicate, postsByExpiration);
}
if (_searchArgs.LocaleCode != null)
{
Expression<Func<vit_post, bool>> postsByLanguage = vit_post.HasLocaleCode(_searchArgs.LocaleCode);
outerPredicate = Data.Utility.And(outerPredicate, postsByLanguage);
}
[...]
}
Then the GetResults() function calls all these methods and join them:
internal string GetResults()
{
IQueryable<vit_post> queryPosts = QueryPosts();
if (_searchArgs.Metas != null)
{
IEnumerable<vit_postmeta> queryMetas = QueryMetas();
queryPosts = from p in queryPosts
join m in queryMetas
on new {id = p.ID, loc = p.locale} equals new {id = m.post_id, loc = m.locale}
select p;
}
}
This way I can make the query works, but I'm still not sure this is the right way to do it.

You did not see/answer my question yet, but if vit_postmeta and vit_post are in any way related by inheritance you may be able to do something like:
public static Expression<Func<T, bool>> HasMetas<T>(IEnumerable<MetaFilter> metaArgs)
where T : vit_post
{
...
}
Assuming that vit_post is the super type.

Related

Mock IDocumentQuery with ability to use query expressions

I need to be able to mock IDocumentQuery, to be able to test piece of code, that queries document collection and might use predicate to filter them:
IQueryable<T> documentQuery = client
.CreateDocumentQuery<T>(collectionUri, options);
if (predicate != null)
{
documentQuery = documentQuery.Where(predicate);
}
var list = documentQuery.AsDocumentQuery();
var documents = new List<T>();
while (list.HasMoreResults)
{
documents.AddRange(await list.ExecuteNextAsync<T>());
}
I've used answer from https://stackoverflow.com/a/49911733/212121 to write following method:
public static IDocumentClient Create<T>(params T[] collectionDocuments)
{
var query = Substitute.For<IFakeDocumentQuery<T>>();
var provider = Substitute.For<IQueryProvider>();
provider
.CreateQuery<T>(Arg.Any<Expression>())
.Returns(x => query);
query.Provider.Returns(provider);
query.ElementType.Returns(collectionDocuments.AsQueryable().ElementType);
query.Expression.Returns(collectionDocuments.AsQueryable().Expression);
query.GetEnumerator().Returns(collectionDocuments.AsQueryable().GetEnumerator());
query.ExecuteNextAsync<T>().Returns(x => new FeedResponse<T>(collectionDocuments));
query.HasMoreResults.Returns(true, false);
var client = Substitute.For<IDocumentClient>();
client
.CreateDocumentQuery<T>(Arg.Any<Uri>(), Arg.Any<FeedOptions>())
.Returns(query);
return client;
}
Which works fine as long as there's no filtering using IQueryable.Where.
My question:
Is there any way to capture predicate, that was used to create documentQuery and apply that predicate on collectionDocuments parameter?
Access the expression from the query provider so that it will be passed on to the backing collection to apply the desired filter.
Review the following
public static IDocumentClient Create<T>(params T[] collectionDocuments) {
var query = Substitute.For<IFakeDocumentQuery<T>>();
var queryable = collectionDocuments.AsQueryable();
var provider = Substitute.For<IQueryProvider>();
provider.CreateQuery<T>(Arg.Any<Expression>())
.Returns(x => {
var expression = x.Arg<Expression>();
if (expression != null) {
queryable = queryable.Provider.CreateQuery<T>(expression);
}
return query;
});
query.Provider.Returns(_ => provider);
query.ElementType.Returns(_ => queryable.ElementType);
query.Expression.Returns(_ => queryable.Expression);
query.GetEnumerator().Returns(_ => queryable.GetEnumerator());
query.ExecuteNextAsync<T>().Returns(x => new FeedResponse<T>(query));
query.HasMoreResults.Returns(true, true, false);
var client = Substitute.For<IDocumentClient>();
client
.CreateDocumentQuery<T>(Arg.Any<Uri>(), Arg.Any<FeedOptions>())
.Returns(query);
return client;
}
The important part is where the expression passed to the query is used to create another query on the backing data source (the array).
Using the following example subject under test for demonstration purposes.
public class SubjectUnderTest {
private readonly IDocumentClient client;
public SubjectUnderTest(IDocumentClient client) {
this.client = client;
}
public async Task<List<T>> Query<T>(Expression<Func<T, bool>> predicate = null) {
FeedOptions options = null; //for dummy purposes only
Uri collectionUri = null; //for dummy purposes only
IQueryable<T> documentQuery = client.CreateDocumentQuery<T>(collectionUri, options);
if (predicate != null) {
documentQuery = documentQuery.Where(predicate);
}
var list = documentQuery.AsDocumentQuery();
var documents = new List<T>();
while (list.HasMoreResults) {
documents.AddRange(await list.ExecuteNextAsync<T>());
}
return documents;
}
}
The following sample tests when an expression is passed to the query
[TestMethod]
public async Task Should_Filter_DocumentQuery() {
//Arrange
var dataSource = Enumerable.Range(0, 3)
.Select(_ => new Document() { Key = _ }).ToArray();
var client = Create(dataSource);
var subject = new SubjectUnderTest(client);
Expression<Func<Document, bool>> predicate = _ => _.Key == 1;
var expected = dataSource.Where(predicate.Compile());
//Act
var actual = await subject.Query<Document>(predicate);
//Assert
actual.Should().BeEquivalentTo(expected);
}
public class Document {
public int Key { get; set; }
}

How do I apply a default IComparable<T> in a Linq OrderBy clause

I have a type which has a default sort order as it implements IComparable<T> and IComparable. I'm not getting the results I expect from LINQ , basically it looks as if the IComparable<T> which the type implements is not being applied.
I thought I would get the result I want with an expression in the form:
var result = MyEnumerable<T>.OrderBy(r => r);
where T itself implements IComparable<T>. It's not happening.
I can see related questions where specific IComparable<T> classes are specified for the sort, but I can't find one which uses the default IComparable<T> implemented by T itself.
My syntax is clearly incorrect. What is the correct syntax please?
Thanks in advance.
OrderBy uses the default comparer Comparer<T>.Default which in turn will default to use the IComparable<T> implementation for T, or the non-generic IComparable if the former does not exist.
This code works:
public class Program
{
static void Main(string[] args)
{
var list = new List<Stuff>
{
new Stuff("one"),
new Stuff("two"),
new Stuff("three"),
new Stuff("four")
};
var sorted = list.OrderBy(x => x);
foreach (var stuff in sorted)
{
Console.Out.WriteLine(stuff.Name);
}
}
}
public class Stuff : IComparable<Stuff>
{
public string Name { get; set; }
public Stuff(string name)
{
Name = name;
}
public int CompareTo(Stuff other)
{
return String.CompareOrdinal(Name, other.Name);
}
}
public static class GenericSorter
{
public static IOrderedEnumerable<T> Sort<T>(IEnumerable<T> toSort, Dictionary<string, SortingOrder> sortOptions)
{
IOrderedEnumerable<T> orderedList = null;
foreach (KeyValuePair<string, SortingOrder> entry in sortOptions)
{
if (orderedList != null)
{
if (entry.Value == SortingOrder.Ascending)
{
orderedList = orderedList.ApplyOrder<T>(entry.Key, "ThenBy");
}
else
{
orderedList = orderedList.ApplyOrder<T>(entry.Key, "ThenByDescending");
}
}
else
{
if (entry.Value == SortingOrder.Ascending)
{
orderedList = toSort.ApplyOrder<T>(entry.Key, "OrderBy");
}
else
{
orderedList = toSort.ApplyOrder<T>(entry.Key, "OrderByDescending");
}
}
}
return orderedList;
}
private static IOrderedEnumerable<T> ApplyOrder<T>(this IEnumerable<T> source, string property, string methodName)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x");
Expression expr = param;
foreach (string prop in property.Split('.'))
{
expr = Expression.PropertyOrField(expr, prop);
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), expr.Type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, param);
MethodInfo mi = typeof(Enumerable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), expr.Type);
return (IOrderedEnumerable<T>)mi.Invoke(null, new object[] { source, lambda.Compile() });
}
}

binaryexpression contains method

I created a helper class which is able to build lambda expression from string parameters an I can filter a query result using this.
But I have little problem that the LINQ.Expressions.Expression does not have a Contains method.
this is my code:
string member = d.Member;
object value = d.Value;
System.Linq.Expressions.Expression expression = System.Linq.Expressions.Expression.Parameter(typeof(T), "e");
foreach (var property in member.Split('.'))
{
expression = System.Linq.Expressions.Expression.PropertyOrField(expression, property);
}
ConstantExpression c = System.Linq.Expressions.Expression.Constant(value, typeof(string));
BinaryExpression b = null;
switch (d.Operator)
{
case FilterOperator.IsEqualTo:
b = System.Linq.Expressions.Expression.Equal(expression, c);
break;
case FilterOperator.Contains:
b = GetExpression<T>(expression.ToString(), value.ToString()).Body as BinaryExpression;
break;
case FilterOperator.IsGreaterThanOrEqualTo:
b = System.Linq.Expressions.Expression.GreaterThanOrEqual(expression, c);
break;
case FilterOperator.IsLessThanOrEqualTo:
b = System.Linq.Expressions.Expression.LessThanOrEqual(expression, c);
break;
}
CriteriaCollection.Add(b);
static Expression<Func<T, bool>> GetExpression<T>(string propertyName, string propertyValue)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return BinaryExpression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}
It should be work but the how I can convert Expression to BinaryExpression?
Anybody knows this or knows an other solution which is working?
i know it's been a long time since the question was asked but, well, i've think i've found an answer, which, basically, lies on MakeBinary.
First of all, i had to create a method alike Contains, but not a self referencing extension method, like this:
public static bool Containss(string text, string text2)
{
return text.Contains(text2, StringComparison.OrdinalIgnoreCase);
}
Then you use it like this:
MethodInfo method = typeof(StackOverflowAnswer).GetMethod("Containss", new[] { typeof(string), typeof(string) });
var constainsExp = Expression.MakeBinary(ExpressionType.Equal, Expression.Constant("FULL TEXT"), Expression.Constant("TEXT"), false, method);
Something that may also help, considering your propable objective, is to receive the member, parameter and value to compose your final expression. Here's an example:
class StackOverflowAnswer
{
static void Main()
{
ParameterExpression parameter = Expression.Parameter(typeof(Student), typeof(Student).Name);
var exp1 = GetBinaryExpression(parameter, "Name", "Foo");
var exp2 = GetBinaryExpression(parameter, "Name", "Bar");
BinaryExpression[] expressions = new BinaryExpression[] { exp1, exp2 };
var bin = CombineExpressions(expressions, parameter);
var func = Expression.Lambda<Func<Student, bool>>(bin, parameter);
var exp = func.Compile();
Student student = new Student { Name = "Foo Bar" };
var x = exp(student);
Console.WriteLine(x);
}
public static BinaryExpression GetBinaryExpression(ParameterExpression parameter, string property, object comparissonValue)
{
MemberExpression member = Expression.Property(parameter, property);
ConstantExpression constant = Expression.Constant(comparissonValue, comparissonValue.GetType());
MethodInfo method = typeof(StackOverflowAnswer).GetMethod("Containss", new[] { typeof(string), typeof(string) });
var containsExp = Expression.MakeBinary(ExpressionType.Equal, member, constant, false, method);
return containsExp ;
}
public static BinaryExpression CombineExpressions(BinaryExpression[] expressions, ParameterExpression parameter)
{
bool first = true;
BinaryExpression expFull = expressions[0];
foreach (BinaryExpression item in expressions)
{
if (first)
first = false;
else
{
expFull = Expression.AndAlso(expFull, item);
}
}
return expFull;
}
internal class Student
{
public string Name { get; set; }
}
}
public class FilterExpressionHelper<T> where T : class
{
public FilterExpressionHelper()
{
CriteriaCollection = new List<BinaryExpression>();
}
public List<BinaryExpression> CriteriaCollection { get; set; }
public Expression<Func<T, bool>> NoFilterExpression { get; set; }
public void RemoveFilterCriteriaFilterDescriptor(Telerik.Windows.Data.FilterDescriptor d)
{
string member = d.Member;
object value = d.Value;
System.Linq.Expressions.Expression expression = System.Linq.Expressions.Expression.Parameter(typeof(T), "e");
foreach (var property in member.Split('.'))
{
expression = System.Linq.Expressions.Expression.PropertyOrField(expression, property);
}
ConstantExpression c = System.Linq.Expressions.Expression.Constant(value, typeof(string));
BinaryExpression b = System.Linq.Expressions.Expression.Equal(expression, c);
BinaryExpression expr = CriteriaCollection.Where(cr => cr.Right.ToString() == b.Right.ToString()).FirstOrDefault();
CriteriaCollection.Remove(expr);
}
public void AddFilterCriteriaFilterDescriptor(Telerik.Windows.Data.FilterDescriptor d)
{
string member = d.Member;
object value = d.Value;
System.Linq.Expressions.Expression expression = System.Linq.Expressions.Expression.Parameter(typeof(T), "e");
foreach (var property in member.Split('.'))
{
expression = System.Linq.Expressions.Expression.PropertyOrField(expression, property);
}
ConstantExpression c = System.Linq.Expressions.Expression.Constant(value, value.GetType());
BinaryExpression b = null;
switch (d.Operator)
{
case FilterOperator.IsEqualTo:
b = System.Linq.Expressions.Expression.Equal(expression, c);
break;
case FilterOperator.Contains:
//b = GetExpression<T>(expression.ToString(), value.ToString()).Body as BinaryExpression;
break;
case FilterOperator.IsGreaterThanOrEqualTo:
b = System.Linq.Expressions.Expression.GreaterThanOrEqual(expression, c);
break;
case FilterOperator.IsLessThanOrEqualTo:
b = System.Linq.Expressions.Expression.LessThanOrEqual(expression, c);
break;
}
CriteriaCollection.Add(b);
}
public Expression<Func<T, bool>> GetLambdaExpression()
{
ParameterExpression e = System.Linq.Expressions.Expression.Parameter(typeof(T), "e");
var orderedList = CriteriaCollection.OrderBy(cr => cr.Left.ToString()).ToList();
var disctinctValues = CriteriaCollection.Distinct(new BinaryExpressionComparer()).ToList();
List<BinaryExpression> orElseExpressionList = new List<BinaryExpression>();
foreach (var value in disctinctValues)
{
System.Linq.Expressions.BinaryExpression expression = null;
foreach (var criteria in orderedList.Where(cr => cr.Left.ToString().Equals(value.Left.ToString())))
{
if (expression == null)
{
expression = criteria;
}
else
{
if (expression.Left.ToString() == criteria.Left.ToString())
expression = System.Linq.Expressions.BinaryExpression.OrElse(expression, criteria);
else
expression = System.Linq.Expressions.BinaryExpression.AndAlso(expression, criteria);
}
}
orElseExpressionList.Add(expression);
}
System.Linq.Expressions.BinaryExpression expressionAnd = null;
foreach (var ex in orElseExpressionList)
{
if (expressionAnd == null)
{
expressionAnd = ex;
}
else
{
expressionAnd = System.Linq.Expressions.BinaryExpression.AndAlso(expressionAnd, ex);
}
}
if (expressionAnd != null)
{
return System.Linq.Expressions.Expression.Lambda<Func<T, bool>>(expressionAnd, e);
}
else
{
return NoFilterExpression;
}
}
static Expression<Func<T, bool>> GetExpression<T>(string propertyName, string propertyValue)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return BinaryExpression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}
private static System.Linq.Expressions.BinaryExpression Like(Expression lhs, Expression rhs)
{
//typeof(string).GetMethod("Contains", new Type[] { typeof(string) }, null);
Expression expression = Expression.Call(
typeof(FileInfoHelper).GetMethod("Like",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
, lhs, rhs);
return expression as BinaryExpression;
}
class BinaryExpressionComparer : IEqualityComparer<BinaryExpression>
{
#region IEqualityComparer<Contact> Members
public bool Equals(BinaryExpression x, BinaryExpression y)
{
return x.Left.ToString().Equals(y.Left.ToString());
}
public int GetHashCode(BinaryExpression obj)
{
return obj.Left.ToString().GetHashCode();
}
#endregion
}
}

EmitMapper and List

It's the first time that I use EmitMapper.
I have a list of object ex: Customer and I would like to map this list in a ienumerable of CustomerDTO how can I do that?
Tnx
It's straightforward if you have a list and want to convert it to list of DTOs:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Customer, CustomerDTO>();
IEnumerable<CustomerDTO> dtos = listOfCustomer.Select(mapper.map);
The preblem is when the list is in another object, for example User and UserDTO:
class User {
public List<Customer> Customers { get; set; }
}
class UserDTO {
public IEnumerable<CustomerDTO> Customers { get; set; }
}
It seems that EmitMapper does not support conversion from List to Enumerable. A way to support it would be:
var customerMapper = ObjectMapperManager
.DefaultInstance.GetMapper<Customer, CustomerDTO>();
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<User, UserDTO>(
new DefaultMapConfig()
.ConvertUsing<List<Customer>, IEnumerable<CustomerDTO>>(
a => a.Select(customerMapper.Map))
);
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

C# Entity Framework 4.1: include paths in query for loading related objects

When I run this line of code
queryCompanies = (DbSet)queryCompanies.Include(path);
from this method:
public Company GetCompanyById(int companyId)
{
List<string> includePaths = new List<string>();
includePaths.Add("Addresses");
includePaths.Add("Users");
Company company = null;
using (Entities dbContext = new Entities())
{
var queryCompanies = dbContext.Companies;
if (includePaths != null)
{
foreach (string path in includePaths)
queryCompanies = (DbSet<Company>)queryCompanies.Include(path);
}
company = (from c in queryCompanies
where c.Id.Equals(companyId)
select c).FirstOrDefault<Company>();
}
return company;
}
I get this error:
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery1[ClassLibrary1.Company]' to type 'System.Data.Entity.DbSet1[ClassLibrary1.Company]'.
At compilation I have no error. In EF 4.0 this code runs correct using instead of DbSet<>, ObjectQuery<>.
I am a beginner in EF 4.1 so any suggestion will be useful.
Thanks.
Try this
public Company GetCompanyById(int companyId)
{
List<string> includePaths = new List<string>();
includePaths.Add("Addresses");
includePaths.Add("Users");
Company company = null;
using (Entities dbContext = new Entities())
{
var queryCompanies = dbContext.Companies.AsQueryable();
if (includePaths != null)
{
foreach (string path in includePaths)
queryCompanies = queryCompanies.Include(path);
}
company = (from c in queryCompanies
where c.Id.Equals(companyId)
select c).FirstOrDefault<Company>();
}
return company;
}
DbSet inherits from DbQuery, so the compiler doesn't complain since the cast could be valid. Apparently, what DbSet<T>.Include returns is not a DbSet<T> and the cast fails at runtime.
However, you do not need to cast; calling FirstOrDefault will work on DbQuery<T>.
I write a generic include checker method. This is not good now but it is work. I will refactor this. Maybe it meets your needs.
List<TEntityType> GetEntityListTemplate(Expression<Func<TEntityType, bool>> expression = null)
{
List<TEntityType> entityList;
DbQuery<TEntityType> query = null;
Type entityType = typeof(TEntityType);
PropertyInfo[] properties = entityType.GetProperties();
using (DatabaseContext database = new DatabaseContext())
{
database.Database.Connection.Open();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType.FullName.Contains("Your.Identifier.Namespace"))
{
if (query == null)
{
query = database.Set<TEntityType>().Include(property.Name);
}
else
{
query = query.Include(property.Name);
}
}
}
if (query == null)
{
if (expression == null)
{
entityList = database.Set<TEntityType>().ToList();
}
else
{
entityList = database.Set<TEntityType>().Where(expression).ToList();
}
}
else //(query!=null)
{
if (expression == null)
{
entityList = query.ToList();
}
else
{
entityList = query.Where(expression).ToList();
}
}
}
return entityList;
}

Resources