How to dynamically choose two fields from a Linq query as a result - linq

If you have a simple Linq query like:
var result = from record in db.Customer
select new { Text = record.Name,
Value = record.ID.ToString() };
which is returning an object that can be mapped to a Drop Down List, is it possible to dynamically specify which fields map to Text and Value?
Of course, you could do a big case (switch) statement, then code each Linq query separately but this isn't very elegant. What would be nice would be something like:
(pseudo code)
var myTextField = db.Customer["Name"]; // Could be an enumeration??
var myValueField = db.Customer["ID"]; // Idea: choose the field outside the query
var result = from record in db.Customer
select new { Text = myTextField,
Value = myValueField };

Right way to do this is with closures.
Func<Customer, string> myTextField = (Customer c) => c["Name"];
Func<Customer, int> myValueField = (Customer c) => c["ID"];
var result = from record in db.Customer
select new { Text = myTextField(record),
Value = myValueField(record) };
The one limitation is that your definition of myTextField always needs to return a string.

You could try something like
class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
var dict = new Dictionary<string, Func<Customer, string>>
{ { "ID", (Customer c) => c.ID.ToString() },
{ "Name", (Customer c) => c.Name},
{ "Surname", (Customer c) => c.Surname } };
List<Customer> rows = new List<Customer>();
rows.Add(new Customer { ID = 1, Name = "Foo", Surname = "Bar"});
var list = from r in rows
select new { Text = dict["ID"](r), Value = dict["Name"](r) };
To try to access the properties dynamically, you could try something like
var dict = new Dictionary<string, Func<Customer, string>>
{ { "ID", (Customer c) => c.GetType().GetProperty("ID").GetValue(c,null).ToString() },
{ "Name", (Customer c) => c.GetType().GetProperty("Name").GetValue(c,null).ToString()},
{ "Surname", (Customer c) => c.GetType().GetProperty("Surname").GetValue(c,null).ToString() } };

Related

How to write a Linq that can retrieve all parent table records and total of sub-table record, I mean 'separate' into two parts

Let's say I have two tables, parent table 'P' and sub-table 'S', I usually wrote the Linq like this to get what I want:
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select new{
Id = p.Id,
Title = p.Title
SubContentCount = subContent.Count()
}
It's very simple, but if for some reason I have to pass a parameter into this query when there has one (let's say 'key'), I have to do this (I guess :-):
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select p;
if(!string.IsNullOrEmpty(key)){ // I'm using C#
rows = rows.Where(q => q.Title.Contains(key))
}
And then:
var list = rows.Select(q => new ()
{
Id = q.Id,
Title = q.Title,
subCount = ???.Count()
});
Is that passable to do Linq like this? if so, how?
Thanks for any kind help!
You could create a method that receives a Func<Table, bool>as parameter and use it to filter your dataset:
public static void Main(string[] args)
{
var rows = new List<Table>
{
new Table { Id = 1, Title = "A", SubContent = new [] { "A1" } },
new Table { Id = 2, Title = "B", SubContent = new [] { "B1", "B2" } },
new Table { Id = 3, Title = "C", SubContent = new [] { "C1", "C2", "C3" } },
};
var title = "C";
foreach (var item in Filter(rows, table =>
String.IsNullOrEmpty(title) || table.Title == title))
{
Console.WriteLine(
"Title={0}, SubContent.Length={1}",
item.Title, item.SubContent.Length);
}
}
public static List<Table> Filter(List<Table> original, Func<Table, bool> filter)
{
return original.Where(filter).ToList();
}
public class Table
{
public int Id { get; set; }
public string Title { get; set; }
public string[] SubContent { get; set; }
}
Why not include the filter in the where clause?
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
Quick example in the interactive console:
public class Parent { public int Id {get; set;} public string Title {get; set;} }
public class SubTable { public int Id {get; set;} public int ParentId {get; set;} }
public class Result { public int Id {get; set;} public string Title {get; set;} public int SubContentCount {get; set;} }
var p1 = new Parent() { Id = 1, Title = "Parent_1" };
var p2 = new Parent() { Id = 2, Title = "Parent_2" };
var p3 = new Parent() { Id = 3, Title = "Parent_3" };
var s1_1 = new SubTable() { Id = 11, ParentId = 1 };
var s1_2 = new SubTable() { Id = 12, ParentId = 1 };
var s1_3 = new SubTable() { Id = 13, ParentId = 1 };
var s2_1 = new SubTable() { Id = 21, ParentId = 2 };
var s2_2 = new SubTable() { Id = 22, ParentId = 2 };
var s3_1 = new SubTable() { Id = 31, ParentId = 3 };
var db_P = new List<Parent>() { p1, p2, p3 };
var db_S = new List<SubTable>() { s1_1, s1_2, s1_3, s2_1, s2_2, s3_1 };
public IEnumerable<Result> GetResults(string key = null)
{
var rows = from p in db_P
join s in db_S on p.Id equals s.ParentId into subContent
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
select new Result() {
Id = p.Id,
Title = p.Title,
SubContentCount = subContent.Count()
};
return rows;
}
And example output (formatted onto multiple lines for readability)
> GetResults().ToList()
List<Submission#0.Result>(3) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" },
Submission#0.Result { Id=2, SubContentCount=2, Title="Parent_2" },
Submission#0.Result { Id=3, SubContentCount=1, Title="Parent_3" }
}
> GetResults("1").ToList()
List<Submission#0.Result>(1) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" }
}
>

Group by an array inside an array using Linq

I can use the below code to group by a ID property in an array which works.
var docArray = MyArray;
var docGroup = docArray.GroupBy(x => x.ID)
.Select(grp => new
{
Id = grp.Key,
Results = grp.ToList(),
}).ToList();
If MyArray has another array inside it which has a property say Data can some please tell me how to do the grouping based on the Data property.
class MyArray
{
SecondArray[] arr = new SecondArray[2];
public int ID{get;set;}
}
class SecondArray
{
public string Data{ get; set; }
}
var query = from a in docArray
from b in a.arr
group new { a, b } by b.Data into g
select new
{
g.Key,
Results = g.ToList()
};

Performance Issue with NHibernate Query

I am currently having a performance problem with the following query written in NHibernate. I am trying to transform the data I queried into DTO's. With this complex structure I cannot use QueryOver to transform the entities. On the other side Linq provider is so useful but it takes ~10 seconds to load and transform ~6000 entities with each 30 child items. It creates an SQL query with left outer join. Are there any other ways to write this query with a better approach?
var Entities = session.Query<crmEntity>()
.Where(x => x.EntityType.ID == EntityType)
.Select(entity => new EntityDTO()
{
ID = entity.ID,
EntityType = entity.EntityType.ID,
InstanceID = entity.Instance.ID,
Values = entity.Values.Select(
value => new CustomFieldValueDTO()
{
ID = value.ID,
FieldID = value.Field.ID,
Value = value.Value
}).ToList<CustomFieldValueDTO>()
}).ToList();
Here is my solution. if there is any other better way, I am completely open to it:
session.CreateQuery(#"select vals.ID,
vals.Field.ID,
vals.Value,
ent.ID
from crmEntity ent inner join ent.Values vals
with vals.Value IS NOT NULL
where ent.EntityType.ID=:eID and ent.Instance.ID=:instanceID order by ent.ID")
.SetGuid("instanceID", InstanceID)
.SetGuid("eID", EntityType)
.SetResultTransformer(new EntityListTransformer()).Future<ReadOnlyEntityDTO>();
And this is my custom result transformer to get the same hierarchy like my linq query
public class EntityListTransformer : IResultTransformer
{
private List<ReadOnlyEntityDTO> list;
private ReadOnlyEntityDTO lastEntity;
private Guid instanceID;
public EntityListTransformer()
{
list = new List<ReadOnlyEntityDTO>();
lastEntity = new ReadOnlyEntityDTO();
}
public System.Collections.IList TransformList(System.Collections.IList collection)
{
return list;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
string ValueID = tuple[0].ToString();
string FieldID = tuple[1].ToString();
string Value = (string)tuple[2];
string EntityID = tuple[3].ToString();
if (lastEntity.ID != EntityID)
{
if (lastEntity.ID != null)
{
list.Add(lastEntity);
}
lastEntity = new ReadOnlyEntityDTO()
{
ID = EntityID
};
}
lastEntity.Values.Add(new ReadOnlyCustomFieldValueDTO()
{
FieldID = FieldID,
ID = ValueID,
Value = Value
});
return tuple;
}
}

Linq Unique Values

i have a list of generic class which consists of 2 string property and 1 List as a property
code snipnets is as follows:
public Class abc
{
public int ID { get; set; }
public String Name { get; set; }
List<String> myList;
public List<String> Subjects
{
get
{
if (myList == null)
{
myList = new List<string>();
}
return myList;
}
}
public abc()
{
}
public abc(int id, String name, params string[] subjects)
{
Subjects.AddRange(subjects.AsEnumerable<String>());
ID = id;
Name = name;
}
}
List<abc> myList = new List<abc>();
myList.Add(new abc(1, "p1", "Maths", "Science"));
myList.Add(new abc(2, "p2", "Maths", "Art"));
myList.Add(new abc(3, "p3", "Art", "Science"));
myList.Add(new abc(4, "p4", "Geometry", "Maths"));
I need the output as
Subject Count Person
Maths 3 p1,p2,p4
Science 2 p1,p3
Art 2 p2,p3
Geometry 1 p4
Looks like you want something like:
var query = from item in myList
from subject in item.Subjects
group item.Name by subject into g
select new { Subject = g.Key,
Count = g.Count(),
Person = string.Join(",", g) };
(Change g into g.ToArray() in the string.Join call if you're using .NET 3.5.)
var result =myList.SelectMany(p => p.Subjects
.Select(q => new{Person = p.Name, Subject = q, ID = p.ID}))
.GroupBy(p => p.Subject)
.Select(p => new {Name = p.Key, Count = p.Count(), Persons = p
.Aggregate("", (a, b) => a + b.Person
+ ",").TrimEnd(',')}).OrderBy( p => p.Count);
Iterate over this collection, and print result as needed - properties of a result are Name, Count, Persons

How do I add a where filter using the original Linq-to-SQL object in the following scenario

I am performing a select query using the following Linq expression:
Table<Tbl_Movement> movements = context.Tbl_Movement;
var query = from m in movements
select new MovementSummary
{
Id = m.DocketId,
Created = m.DateTimeStamp,
CreatedBy = m.Tbl_User.FullName,
DocketNumber = m.DocketNumber,
DocketTypeDescription = m.Ref_DocketType.DocketType,
DocketTypeId = m.DocketTypeId,
Site = new Site()
{
Id = m.Tbl_Site.SiteId,
FirstLine = m.Tbl_Site.FirstLine,
Postcode = m.Tbl_Site.Postcode,
SiteName = m.Tbl_Site.SiteName,
TownCity = m.Tbl_Site.TownCity,
Brewery = new Brewery()
{
Id = m.Tbl_Site.Ref_Brewery.BreweryId,
BreweryName = m.Tbl_Site.Ref_Brewery.BreweryName
},
Region = new Region()
{
Description = m.Tbl_Site.Ref_Region.Description,
Id = m.Tbl_Site.Ref_Region.RegionId
}
}
};
I am also passing in an IFilter class into the method where this select is performed.
public interface IJobFilter
{
int? PersonId { get; set; }
int? RegionId { get; set; }
int? SiteId { get; set; }
int? AssetId { get; set; }
}
How do I add these where parameters into my SQL expression? Preferably I'd like this done in another method as the filtering will be re-used across multiple repositories.
Unfortunately when I do query.Where it has become an IQueryable<MovementSummary>. I'm assuming it has become this as I'm returning an IEnumerable<MovementSummary>. I've only just started learning LINQ, so be gentle.
Answer:
private IQueryable<Tbl_Docket> BuildQuery(IQueryable<Tbl_Docket> movements, IMovementFilter filter)
{
if (filter != null)
{
if (filter.PersonId.HasValue) movements = movements.Where(m => m.UserId == filter.PersonId);
if (filter.SiteId.HasValue) ...
}
return movements;
}
Which is called like follows:
var query = from m in this.BuildQuery(movements, filter)
select new... {}
You have to call the where statement before you fire your select statement, e.g.:
IQueryable<Tbl_Movement> movements = context.Tbl_Movement;
if (filter != null)
{
if (filter.PersonId != null) movements = movements.Where(m => m....PersonId == filter.PersonId);
if (filter.RegionId != null) movements = movements.Where(m => m....RegionId == filter.RegionId);
if (filter.SiteId != null) movements = movements.Where(m => m...SiteId == filter.SiteId);
if (filter.AssetId != null) movements = movements.Where(m => m...AssetId == filter.AssetId);
}
var query = m from movements...
As opposed to using this IFilter class, you might want to consider a Fluent Pipe-based Repository structure, e.g.:
var movements = new MovementsPipe()
.FindSiteId(1)
.FindAssetIds(1, 2, 3)
.FindRegionId(m => m > 10)
.ToMovementSummaryList();
Hope this helps. Let me know if you have any questions.

Resources