Dynamic LINQ Query System.Linq.Dynamic.Core - c#

System.Linq.Dynamic.Core.Exceptions.ParseException: 'No property or field 'Name' exists in type 'IEnumerable`1''
How to use Complex object to do order by in Dynamic linq query! I am using System.Linq.Dynamic.Core for dynamic linq query.
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Student> Students { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Course Course { get; set; }
}
using (var context = new SchoolContext())
{
var list = context.Courses.Include("Students").AsQueryable();
var result = list.OrderBy("Students.Name").ToList();
foreach (var item in list)
{
Console.WriteLine("Hello World!" + item.Name );
}
}

The issue you are running into is trying to run an order by on a list.
If you want the students ordered by name in a course try the following
var list = context.Courses.Include(c => c.Students).AsQueryable();
var result = list.Select(course =>
new Course {
Id = course.Id,
Name = course.Name,
Students = course.Students.OrderBy(s => s.Name)
}).ToList();
foreach (var item in list)
{
foreach(var student in item.Students)
{
Console.WriteLine("Hello World!" + student.Name );
}
}

Related

Create query by dynamically pass the GroupBy() and create new class in Select() using Expression tree

I`m having simple method which builds IQueryable and returns it.
public IQueryable<ClassDTO> ReportByNestedProperty()
{
IQueryable<Class> query = this.dbSet;
IQueryable<ClassDTO> groupedQuery =
from opportunity in query
group new
{
ItemGroup = opportunity.OpportunityStage.Name,
EstimatedRevenue = opportunity.EstimatedRevenue,
CostOfLead = opportunity.CostOfLead
}
by new
{
opportunity.OpportunityStage.Name,
opportunity.OpportunityStage.Id
}
into item
select new ClassDTO()
{
ItemGroup = string.IsNullOrEmpty(item.Key.Name) ? "[Not Assigned]" : item.Key.Name,
Count = item.Select(z => z.ItemGroup.Name).Count(), // int
Commission = item.Sum(z => z.EstimatedRevenue), // decimal
Cost = item.Sum(z => z.CostOfLead), // decimal?
};
return groupedQuery;
}
This is fine. The thing i need is to create method with same return type, but groupby by different prperties dynamically. So from the above code I want to have 3 dynamic parts which will be passed as params:
ItemGroup = opportunity.OpportunityStage.Name
and
by new
{
opportunity.OpportunityStage.Name,
opportunity.OpportunityStage.Id
}
So the new method should be like this
public IQueryable<ClassDTO> ReportByNestedProperty(string firstNestedGroupByProperty, string secondNestedGroupByProperty)
{
// TODO: ExpressionTree
}
And call it like this:
ReportByNestedProperty("OpportunityStage.Name","OpportunityStage.Id")
ReportByNestedProperty("OtherNestedProperty.Name","OtherNestedProperty.Id")
ReportByNestedProperty("OpportunityStage.Name","OpportunityStage.Price")
So the main thing is to create expressions with these two selects:
opportunity.OpportunityStage.Name,
opportunity.OpportunityStage.Id
I have tried toe create the select expressions, groupby, the creation of Anonomoys classes and the DTO Class but I just cant get it right.
EDIT:
Here are the classes involved:
public class ClassDTO
{
public ClassDTO() { }
[Key]
public string ItemGroup { get; set; }
public decimal Commission { get; set; }
public decimal? Cost { get; set; }
public int Count { get; set; }
}
Class obj is a pretty big one so i`m posting just part of it
public partial class Class
{
public Class() { }
[Key]
public Guid Id { get; set; }
public Guid? OpportunityStageId { get; set; }
[ForeignKey(nameof(OpportunityStageId))]
[InverseProperty(nameof(Entities.OpportunityStage.Class))]
public virtual OpportunityStage OpportunityStage { get; set; }
}
public partial class OpportunityStage
{
public OpportunityStage()
{
this.Classes = new HashSet<Class>();
}
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
[InverseProperty(nameof(Class.OpportunityStage))]
public virtual ICollection<TruckingCompanyOpportunity> Classes{ get; set; }
}
I have simplified your Grouping query and introduced private class IdName which should replace anonymous class usage:
class IdName
{
public int Id { get; set; }
public string Name { get; set; } = null!;
}
static Expression MakePropPath(Expression objExpression, string path)
{
return path.Split('.').Aggregate(objExpression, Expression.PropertyOrField);
}
IQueryable<ClassDTO> ReportByNestedProperty(IQueryable<Class> query, string nameProperty, string idProperty)
{
// Let compiler to do half of the work
Expression<Func<Class, string, int, IdName>> keySelectorTemplate = (opportunity, name, id) =>
new IdName { Name = name, Id = id };
var param = keySelectorTemplate.Parameters[0];
// generating expressions from prop path
var nameExpr = MakePropPath(param, nameProperty);
var idExpr = MakePropPath(param, idProperty);
var body = keySelectorTemplate.Body;
// substitute parameters
body = ReplacingExpressionVisitor.Replace(keySelectorTemplate.Parameters[1], nameExpr, body);
body = ReplacingExpressionVisitor.Replace(keySelectorTemplate.Parameters[2], idExpr, body);
var keySelectorLambda = Expression.Lambda<Func<Class, IdName>>(body, param);
// finalize query
IQueryable<ClassDTO> groupedQuery = query
.GroupBy(keySelectorLambda)
.Select(item => new ClassDTO()
{
ItemGroup = string.IsNullOrEmpty(item.Key.Name) ? "[Not Assigned]" : item.Key.Name,
Count = item.Count(x => x.Name), // int
Commission = item.Sum(x => x.EstimatedRevenue), // decimal
Cost = item.Sum(x => x.CostOfLead), // decimal?
});
return groupedQuery;
}

How to change the following foreach code to Linq Query?

public class A
{
public List<Data> data { get; set; }
}
public class Data
{
public int ID { get; set; }
public int values { get; set; }
}
public class B
{
public List<DifferentData> Data { get; set; }
}
public class DifferentData
{
public int NewID { get; set; }
public int Differentvalues { get; set; }
public string AdditionalInfo { get; set; }
}
I have a code like below
var TopAClassData = new A();
var TopBClassData = new B();
var anotherItemD = new DifferentData();
foreach (var item in TopAClassData.data)
{
foreach (var anotherItem in TopBClassData.Data)
{
if (item.ID == anotherItem.NewID)
{
item.values = anotherItemD.Differentvalues;
}
}
}
I'm trying to set the Differentvalues from the List to values field of List if the NewId and ID are matching. Is there anyway to convert the foreach code to simpler linq query
You can join two lists and then enumerate:
var items = TopAClassData.data
.Join(TopBClassData.Data, x => x.ID, x => x.NewId,
(item, anotherItem) => item);
foreach(var item in items)
{
item.values = anotherItemD.Differentvalues;
}
Here's a fairly nice LINQ version:
IEnumerable<Data> items =
from item in TopAClassData.data
join anotherItem in TopBClassData.Data on item.ID equals anotherItem.NewID
select item;
foreach (Data item in items)
item.values = anotherItemD.Differentvalues;

Convert list of items to list of tree nodes that have children

I have the following class
public class Item
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Text { get; set; }
}
And the following class for tree view
public class TreeViewModel
{
public TreeViewModel()
{
this.Children = new List<TreeViewModel>();
}
public int Id { get; set; }
public int NodeId { get; set; }
public string Text { get; set; }
public bool Expanded { get; set; }
public bool Checked { get; set; }
public bool HasChildren
{
get { return Children.Any(); }
}
public IList<TreeViewModel> Children { get; private set; }
}
I will receive list of items and I would to convert it to tree.
the item that not have parent id it will the main node.
example: if i have the following items
item[0] = Id:0 Text:User ParentId:3
item[1] = Id:1 Text:Role ParentId:3
item[2] = Id:2 Text:SubUser ParentId:0
item[3] = Id:3 Text:Admin ParentId:null
item[4] = Id:4 Text:SuperAdmin ParentId:null
item[5] = Id:5 Text:Doha ParentId:4
the following item it will list of tree
I tried to make recursive function to do that , but i have no result
You don't need a recursive function to do this:
var models = items.Select(i => new TreeViewModel
{
Id = i.Id,
...
}).ToList();
foreach (var model in models){
model.Children.AddRange(models.Where(m => m.ParentId == model.Id));
}
If you then want to get the roots of your tree, you can use:
var roots = models.Where(m => !m.ParentId.HasValue);
Here is a fast O(N) time complexity method of doing that:
List<Item> list = ...;
// Pre create all nodes and build map by Id for fast lookup
var nodeById = list
.Select(item => new TreeViewModel { Id = item.Id, Text = item.Text })
.ToDictionary(item => item.Id);
// Build hierarchy
var tree = new List<TreeViewModel>();
foreach (var item in list)
{
var nodeList = item.ParentId == null ? tree, nodeById[item.ParentId.Value].Children;
nodeList.Add(nodeById[item.Id]);
}

LINQ selectors inheritance

It's not impossible that my question has already been answered here, but unfortunately when it comes to LINQ I can't always find the right search terms.
Given the underlying source code how do you think I can reuse Selectors.CustomerDTO selector within Selectors.CustomerExtendedDTO?
It should be possible since the input database entity is the same.
class Program
{
static void Main(string[] args)
{
using (NorthwindEntities ctx = new NorthwindEntities())
{
foreach (var item in ctx.Customers.Where(c => c.CompanyName.StartsWith("A")).Select(Selectors.CustomerDTO))
{
Console.WriteLine("CompanyName: {0}", item.CompanyName);
Console.WriteLine();
}
foreach (var item in ctx.Customers.Where(c => c.CompanyName.StartsWith("A")).Select(Selectors.CustomerExtendedDTO))
{
Console.WriteLine("CompanyName: {0}", item.CompanyName);
Console.WriteLine("ContactName: {0}", item.ContactName);
Console.WriteLine();
}
Console.ReadLine();
}
}
}
class CustomerDTO
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
}
class CustomerExtendedDTO : CustomerDTO
{
public string ContactName { get; set; }
public string Address { get; set; }
}
static class Selectors
{
internal static Expression<Func<Customers, CustomerDTO>> CustomerDTO
{
get
{
return c => new CustomerDTO
{
CustomerID = c.CustomerID,
CompanyName = c.CompanyName
};
}
}
internal static Expression<Func<Customers, CustomerExtendedDTO>> CustomerExtendedDTO
{
get
{
return c => new CustomerExtendedDTO
{
CustomerID = c.CustomerID,
CompanyName = c.CompanyName,
Address = c.Address,
ContactName = c.ContactName
};
}
}
}
Such thing would be very useful when the inherited projection entity does not only contain two properties but several dozens.
Thanks for any ideas.

RavenDb how do I reduce group values into collection in reduce final result?

I hope it's more clear what I want to do from the code than the title. Basically I am grouping by 2 fields and want to reduce the results into a collection all the ProductKey's constructed in the Map phase.
public class BlockResult
{
public Client.Names ClientName;
public string Block;
public IEnumerable<ProductKey> ProductKeys;
}
public Block()
{
Map = products =>
from product in products
where product.Details.Block != null
select new
{
product.ClientName,
product.Details.Block,
ProductKeys = new List<ProductKey>(new ProductKey[]{
new ProductKey{
Id = product.Id,
Url = product.Url
}
})
};
Reduce = results =>
from result in results
group result by new {result.ClientName, result.Block} into g
select new BlockResult
{
ClientName = g.Key.ClientName,
Block = g.Key.Block,
ProductKeys = g.SelectMany(x=> x.ProductKeys)
};
}
I get some weird System.InvalidOperationException and a source code dump where basically it is trying to initialize the list with an int (?).
If I try replacing the ProductKey with just IEnumerable ProductIds (and make appropriate changes in the code). Then the code runs but I don't get any results in the reduce.
You probably don't want to do this. Are you really going to need to query in this manner? If you know the context, then you should probably just do this:
var q = session.Query<Product>()
.Where(x => x.ClientName == "Joe" && x.Details.Block == "A");
But, to answer your original question, the following index will work:
public class Products_GroupedByClientNameAndBlock : AbstractIndexCreationTask<Product, Products_GroupedByClientNameAndBlock.Result>
{
public class Result
{
public string ClientName { get; set; }
public string Block { get; set; }
public IList<ProductKey> ProductKeys { get; set; }
}
public class ProductKey
{
public string Id { get; set; }
public string Url { get; set; }
}
public Products_GroupedByClientNameAndBlock()
{
Map = products =>
from product in products
where product.Details.Block != null
select new {
product.ClientName,
product.Details.Block,
ProductKeys = new[] { new { product.Id, product.Url } }
};
Reduce = results =>
from result in results
group result by new { result.ClientName, result.Block }
into g
select new {
g.Key.ClientName,
g.Key.Block,
ProductKeys = g.SelectMany(x => x.ProductKeys)
};
}
}
When replicating I get the same InvalidOperationException, stating that it doesn't understand the index definition (stack trace omitted for brevity).
Url: "/indexes/Keys/ByNameAndBlock"
System.InvalidOperationException: Could not understand query:
I'm still not entirely sure what you're attempting here, so this may not be quite what you're after, but I managed to get the following working. In short, Map/Reduce deals in anonymous objects, so strongly typing to your custom types makes no sense to Raven.
public class Keys_ByNameAndBlock : AbstractIndexCreationTask<Product, BlockResult>
{
public Keys_ByNameAndBlock()
{
Map = products =>
from product in products
where product.Block != null
select new
{
product.Name,
product.Block,
ProductIds = product.ProductKeys.Select(x => x.Id)
};
Reduce = results =>
from result in results
group result by new {result.Name, result.Block}
into g
select new
{
g.Key.Name,
g.Key.Block,
ProductIds = g.SelectMany(x => x.ProductIds)
};
}
}
public class Product
{
public Product()
{
ProductKeys = new List<ProductKey>();
}
public int ProductId { get; set; }
public string Url { get; set; }
public string Name { get; set; }
public string Block { get; set; }
public IEnumerable<ProductKey> ProductKeys { get; set; }
}
public class ProductKey
{
public int Id { get; set; }
public string Url { get; set; }
}
public class BlockResult
{
public string Name { get; set; }
public string Block { get; set; }
public int[] ProductIds { get; set; }
}

Categories

Resources