Dynamic Grouping Using LINQ - c#

Please have a look at below example. The Group Clause has to be dynamic. Can you please guide me how this can be achieved. i.e. the row
{ r.Portfolio, r.DataType }
has to be constructed dynamically.
Not sure how I can tweak the solution as given at blog http://jonahacquah.blogspot.com/2012/02/groupby-multiple-columns-using-dynamic.html
public class DecisionSupportData
{
public string Portfolio { get; set; }
public string BucketName { get; set; }
public string DataType { get; set; }
public string ChildPortfolio { get; set; }
}
public void PopulateData()
{
List<DecisionSupportData> lstAllDecSupp = decisionSupportDataBindingSource.DataSource as List<DecisionSupportData>;
List<DecisionSupportData> lstRmgAmt
= (from r in lstAllDecSupp.AsEnumerable()
where r.DataType == "P"
group r by new { r.Portfolio, r.DataType } into gg
select new DecisionSupportData
{
DataType = gg.Key.DataType,
Portfolio = gg.Key.Portfolio,
}).ToList();
}

The DynamicLinq library would appear to solve your issue, as mentioned in Scott Gu's original blog. Just use the GroupBy extension method with a string value.
Or you could dig into their ExpressionParser class and see what it's doing.

The following should work with your example, but may not work/scale up very well if your real-life example is more complicated.
// bools to indicate which columns you want to group by
bool groupByPortfolio = true;
bool groupByDataType = true;
bool groupByBucketName = false;
bool groupByChildPortfolio = false;
List<DecisionSupportData> lstRmgAmt
= (from r in lstAllDecSupp.AsEnumerable()
where r.DataType == "P"
group r by new
{
Portfolio = groupByPortfolio ? r.Portfolio : null ,
DataType = groupByDataType ? r.DataType : null ,
BucketName = groupByBucketName ? r.BucketName : null ,
ChildPortfolio = groupByChildPortfolio ? r.ChildPortfolio : null
}
into gg
select new DecisionSupportData
{
Portfolio = gg.Key.Portfolio,
DataType = gg.Key.DataType,
BucketName = gg.Key.BucketName,
ChildPortfolio = gg.Key.ChildPortfolio
}
).ToList();

Related

How to convert this sql to efcore linq

Original data sheet:
SQL that can get the result you want:
select group_number,count(group_number) as group_count,receive_date
from hS_HZXX
where type = '1'
group by group_number,receive_date;
use ef core linq like this:
var hAATGroups = from p in dbContext.Set<HS_HZXX>()
where p.type == "1"
group p by new { Group_number = p.group_number, Sampling_date = p.sampling_date } into dto
select new DTO_HAATGroup
{
Group_number = dto.Key.Group_number,
HAAT_count = dto.Key.Group_number.Count(),
Sampling_date = dto.Key.Sampling_date.ToString("yyyy-MM-dd")
};
The DTO_HAATGroup:
public class DTO_HAATGroup
{
public string Group_number { get; set; }
public int HAAT_count { set; get; }
public string Sampling_date { get; set; }
}
The result of executing linq:
But I got the wrong result. . . Can you help me? How can I convert this SQL into a correct Linq statement.
I have solved this problem, the correct Linq writing is as follows:
var hAATGroups = from p in dbContext.Set<HS_HZXX>()
where p.type == "1"
group p by new { Group_number = p.group_number, Receive_date = p.receive_date } into dto
select new DTO_HAATGroup
{
Group_number = dto.Key.Group_number,
HAAT_count = dto.Count(),
Receive_date = dto.Key.Receive_date.ToString("yyyy-MM-dd")
};

Orderby on left join null value

Following situation:
Table of users
Table of addresses
The user has a single optional reference to the address table (=left join)
The query to fetch the data is:
IQueryable<User> query =
from u in _dbContext.Users
join a in _dbContext.Address on u.AddressId equals a.Id into address
from addresses in address.DefaultIfEmpty()
select new User(u, a);
Now I want to do a sorting on the query based on the municipality of the address
query = query.OrderBy(u => u.Address.Municipality);
The problem is that the Address can be a null value (as the address is optional) and for that reason it is throwing following exception.
NullReferenceException: Object reference not set to an instance of an object.
Is there a way to order on the municipality knowing that it can be null?
Already tried following things with same outcome:
query = query.OrderBy(u => u.Address.Municipality ?? "");
query = query.OrderBy(u => u.Address == null).ThenBy(u => u.Address.Municipality);
You can use
query = query.OrderBy(u => u.Address.Municipality.HasValue);
You can write your comparer like this:
public class One
{
public int A { get; set; }
}
public class Two
{
public string S { get; set; }
}
public class T
{
public One One { get; set; }
public Two Two { get; set; }
}
public class OrderComparer : IComparer<Two>
{
public int Compare(Two x, Two y)
{
if (((x == null) || (x.S == null)) && ((y == null) || (y.S == null)))
return 0;
if ((x == null) || (x.S == null))
return -1;
if ((y == null) || (y.S == null))
return 1;
return x.S.CompareTo(y.S);
}
}
static void Main(string[] args)
{
var collection = new List<T> {
new T{ One = new One{A=1}, Two = new Two{ S="dd" } },
new T{ One = new One{A=5}, Two = null },
new T{ One = new One{A=0}, Two = new Two{ S=null } },
new T{ One = new One{A=6}, Two = new Two{ S="aa" } },
};
var comparer = new OrderComparer();
collection = collection.OrderBy(e => e.Two, comparer).ToList();
}
But in your case you have to write var collection = query.AsEnumerable().OrderBy(x=>x.Address).
Also there is other method:
var result = query.Where(x=>x.Address==null || x.Address.Municipality==null)
.Union(query.Where(x.Address!=null && x.Address.Municipality!=null).OrderBy(x=>x.Address.Municipality));
I create a simple demo and it works well when I add nullable foreign key to the two tables.
Besides, I do not understabd what is select new User(u, a); in your code.
Below is my sample code:
Models:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Address")]
public int? AddressId { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public string AddressName { get; set; }
public string Municipality { get; set; }
}
Action:
IQueryable<User> query =
from u in _context.Users
join a in _context.Address on u.AddressId equals a.Id into address
from addresses in address.DefaultIfEmpty()
select new User
{
Id = u.Id,
Name = u.Name,
AddressId = u.AddressId,
Address = addresses
};
query = query.OrderBy(u => u.Address.Municipality);

Cannot implicitly convert type IQueryable to class

I am trying to get multiple results into a list to send back to JS to populate a grid. The first query (_mappedQuery) is getting data. I then want to end up putting the values into the _udfList object. I keep getting variances on the error 'cannot convert queryable to class'
I have tried setting as lists, creating query objects, single class objects. All no luck so far
MemberMNCFormsList _udfList = new MemberMNCFormsList();
foreach (var _row in _udfTables) {
System.Diagnostics.Debugger.Break();
System.Diagnostics.Debugger.Launch();
var _mappedQuery = (from res in Query<UdfColumnMapping>().AsNoTracking()
join udf in Query<UserDefinedForms>().AsNoTracking() on res.Func_Area equals udf.Func_Area
join ds in Query<Data_Set>().AsNoTracking() on res.Data_ID equals ds.DATA_ID
join df in Query<DEFITEM>().AsNoTracking() on ds.DEF_ID equals df.DEF_ID
where udf.UserDefinedForms_ID == _row.UserDefinedForms_ID &&
(res.FieldName.ToLower().StartsWith("reviewname") ||
res.FieldName.ToLower().StartsWith("disposition") ||
res.FieldName.ToLower().StartsWith("reviewdate"))
select (new MemberMNCForms {
UserDefinedFormData_ID = _row.UserDefinedFormData_ID,
FormId = udf.UserDefinedForms_ID,
MappedColumnName = res.MappedColumnName,
FieldName = res.FieldName,
MappedTableName = res.MappedTableName,
Reviewed_Name = _row.LAST_NAME.Trim() + ", " + _row.FIRST_NAME.Trim(),
Reviewed_Date = _row.CreateDate.GetShortDateorEmpty().ToString()
}));
var _formRow = _mappedQuery.Select(t => new MemberMNCForms {
UserDefinedFormData_ID = t.UserDefinedFormData_ID,
FormId = t.FormId,
MappedColumnName = t.MappedColumnName,
FieldName = t.FieldName,
MappedTableName = t.MappedTableName,
Reviewed_Name = t.Reviewed_Name,
Reviewed_Date = t.Reviewed_Date
})));
_udfList.list.Add(_formRow);
public sealed class MemberMNCForms {
public Guid? UserDefinedFormData_ID { get; set; }
public int FormId { get; set; }
public string Reviewed_Name { get; set; }
public string MappedColumnName { get; set; }
public string FieldName { get; set; }
public string MappedTableName { get; set; }
public int? MNCDetermination_ID { get; set; }
public string Reviewed_By { get; set; }
public string Reviewed_Date { get; set; }
}
public sealed class MemberMNCFormsList : ErrorInfo
{
public List<MemberMNCForms> list = new List<MemberMNCForms>();
public int Count { get; set; }
}
I am trying to get the _udfList object populated with the values coming from _mappedQuery. The only thing I thought would work was to create a MemberMNCForms object for each record in _mappedQuery to then add to _udfList.list
_formRow is an IEnumerable<MemberMNCForms>
var _formRow = _mappedQuery.Select(t => new MemberMNCForms {
UserDefinedFormData_ID = t.UserDefinedFormData_ID,
FormId = t.FormId,
MappedColumnName = t.MappedColumnName,
FieldName = t.FieldName,
MappedTableName = t.MappedTableName,
Reviewed_Name = t.Reviewed_Name,
Reviewed_Date = t.Reviewed_Date
})));
Here you are trying to add an IEnumerable<MemberMNCForms> to a List<MemberMNCForms>
_udfList.list.Add(_formRow);
You can't do this with .Add. You have to use .AddRange
Try this:
_udfList.list.AddRange(_formRow);
When you use linq like that, even if there is a single item in the list that you are Selecting on, it is just an expression tree until it is iterated on.
I assume that you are expecting a collection of MemberMNCForms back so you would need use AddRange instead of Add
_udfList.list.AddRange(_formRow);
To make sure that it has been executed, you can use ToList
_udfList.list.AddRange(_formRow.ToList());
If you are just expecting a single result, you can use SingleOrDefault.
var result = _formRow.SingleOrDefault();
if (result != null) {
_udfList.list.Add(result);
}

Dynamically selecting fields in a linq query

I have a linq query where I’d like to dynamically select only the fields requested by my user.
Currently I’m mapping my Jobs to a data transformation object like this:
var jobs = (from p in jobsDB
select new JobReportDTO()
{
JobID = p.JobID,
EventType = p.EventType,
DateApproved = p.ApprovedDate,
DateEntered = p.EnteredDate,
DateClosed = p.ClosedDate,
StartDate = p.StartDate,
FinishDate = p.FinishDate,
InsuredName = p.InsuredName,
StreetAddress = p.StreetAddress,
Suburb = p.Suburb,
State = p.State,
Postcode = p.Postcode,
.... etc
Within this function I have a number of boolean variables that identify whether that field should be sent to the view, i.e.:
public bool ShowInsuredName { get; set; }
public bool ShowSuburb { get; set; }
public bool ShowICLA { get; set; }
public bool ShowClaimNumber { get; set; }
public bool ShowFileMananger { get; set; }
public bool ShowSupervisor { get; set; }
public bool ShowStatus { get; set; }
... etc
How can I modify my linq query to show selected fields only?
I’ve tried
var jobs = (from p in jobsDB
select new JobReportDTO()
{
JobID = p.JobID,
jobReport.ShowEventType == true ? EventType = p.EventType : "",
... etc
But am getting “invalid initialiser member declarator”
If you can afford LINQ method syntax and use strong naming convention for options like public bool Show{DTOPropertyName} { get; set; }, then you can make your life much easier with the help of the System.Linq.Expressions and the following little helper method
public static class MyExtensions
{
public static IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector, object options)
{
var memberInit = (MemberInitExpression)selector.Body;
var bindings = new List<MemberBinding>();
foreach (var binding in memberInit.Bindings)
{
var option = options.GetType().GetProperty("Show" + binding.Member.Name);
if (option == null || (bool)option.GetValue(options)) bindings.Add(binding);
}
var newSelector = Expression.Lambda<Func<TSource, TResult>>(
Expression.MemberInit(memberInit.NewExpression, bindings), selector.Parameters);
return source.Select(newSelector);
}
}
What it does is to remove the assignments which has associated ShowProperty with value set to false.
The usage is simple
var jobs = jobsDB.Select(p => new JobReportDTO
{
JobID = p.JobID,
EventType = p.EventType,
DateApproved = p.ApprovedDate,
DateEntered = p.EnteredDate,
DateClosed = p.ClosedDate,
StartDate = p.StartDate,
FinishDate = p.FinishDate,
InsuredName = p.InsuredName,
StreetAddress = p.StreetAddress,
Suburb = p.Suburb,
State = p.State,
Postcode = p.Postcode,
.... etc
}, jobReport);
If you set a breakpoint in the debugger and examine the newSelector variable, you'll see that only properties that do not have ShowProperty (like JobID) or have ShowProperty = true are included.
Try this way:
EventType = jobReport.ShowEventType == true ? p.EventType : string.Empty,

Linq : copy a value to another

I have a collection of this object :
public Class MyObject()
{
public int Id { get; set; }
public int Code { get; set; }
public string NL { get; set; }
public string FR { get; set; }
public string Value { get; set; }
}
I have this
IList<MyObject> listObject = new List<Object>();
bool res = MyMethod();
I'd like depending of the res result copy all the value of FR to Value and from NL to Value if res is false :
I try this :
var res = (from p in listObject select new { Id = p.Id, Value = ?????? });
the ?????? reprsent the code I don't find :(
Any idea ?
Thanks,
UPDATE1
I made a more generic method :
public void MyTest<T>(IList<T> list) where T : ILookup
{
bool res = MyMethod();
var result = (from p in list select new { Id = p.Id, Value = res? p.FR : p.NL });
return result;
}
What is the return type ?
A conditional or ternary operator will help here. An example of how it work is:
var result = booleanExpression ? valueIfTrue : valueIfFalse;
applied to your code it would look something like this:
var copy = (from p in listObject select new { Id = p.Id, Value = res ? p.FR : p.NL});
If you wish the result to be returnable from the method you will have to use a class with your select statement rather than createing an anonymous type:
var copy = (from p in listObject select new MyObject { Id = p.Id, Value = res ? p.FR : p.NL});
where MyObject has properties defined as Id and Value. You could then return it as
IEnumerable<MyObject >.
Use the ternary operator
var result = (from p in listObject select new { Id = p.Id, Value = res ? p.FR : p.NL });

Categories