How to extract result of Linq Expression? - c#

My result Expression is
var result = dtFields.AsEnumerable().Join(dtCDGroup.AsEnumerable(),
fieldList=>fieldList.Field<string>("CDGroupID"),
cd=>cd.Field<string>("CDGroupID"),
(fieldList,cd) => new
{
FieldID = fieldList.Field<string>("FieldID"),
Name = cd.Field<string>("Name"),
CDCaption = fieldList.Field<string>("CDCaption"),
Priority = ((cd.Field<string>("Priority") == null) ? 99 : cd.Field<int>("Priority")),
fldIndex = fieldList.Field<string>("fldIndex")
}).OrderBy(result => result.Priority).ThenBy(result => result.fldIndex);
Casting above result to array or list throws an invalid cast exception.
How can extract result of above expression?

Add .ToArray() or .ToList() call respectively

Try to add a strongly typed type:
public class NewModule
{
public int FieldID { get; set; }
public string Name { get; set; }
public string CDCaption { get; set; }
public int Priority { get; set; }
public int fldIndex { get; set; }
}
instead of the anonymous type then you could use ToList<NewModule>() like this:
var result = dtFields.AsEnumerable().Join(dtCDGroup.AsEnumerable(),
fieldList=>fieldList.Field<string>("CDGroupID"),
cd=>cd.Field<string>("CDGroupID"),
(fieldList,cd) => new NewModule
{
FieldID = fieldList.Field<string>("FieldID"),
Name = cd.Field<string>("Name"),
CDCaption = fieldList.Field<string>("CDCaption"),
Priority = ((cd.Field<string>("Priority") == null) ? 99 : cd.Field<int>("Priority")),
fldIndex = fieldList.Field<string>("fldIndex")
}).OrderBy(result => result.Priority)
.ThenBy(result => result.fldIndex)
.ToList<NewModule>();

Related

cannot convert type decimal to double

I have a simple web service calling a sql view table through entity framework. I can bring all the columns in string fine but not the column in numeric like UID_NUM(numeric(38,8), null) in SQL. I have AddressALL class to set columns like below and error out at p.UID_NUM in LINQ.
public class GISAddressWebService : System.Web.Services.WebService
{
[WebMethod]
public AddressALL[] getAddress()
{
try
{
List<view_COBADDRESS> address = new List<view_COBADDRESS>();
using (GISAddressEntities database = new GISAddressEntities())
{
return database.view_COBADDRESS
.Where(p => p.UNIT_NUM == "103")
.Select(p => new AddressALL { UID_NUM = p.UID_NUM, ADD_FULL = p.ADD_FULL, POSTALCITY = p.POSTALCITY, ZIP5 = p.ZIP5}).ToArray();
}
}
catch (Exception)
{
return null;
}
}
}
public class AddressALL
{
public double UID_NUM { get; set; }
public string TLID { get; set; }
public string ADD_FULL { get; set; }
public string POSTALCITY { get; set; }
public string STATE { get; set; }
public string ZIP5 { get; set; }
public string IN_OUT { get; set; }
}
The decimal has more significant figures than the double, therefore it can be more precise and it also takes up slightly more memory. Because of this difference fou must explicitly program this change of type through (double)p.UID_NUM.
return database.view_COBADDRESS
.Where(p => p.UNIT_NUM == "103")
.Select(p => new AddressALL { UID_NUM = System.Convert.ToDouble(p.UID_NUM), ADD_FULL = p.ADD_FULL, POSTALCITY = p.POSTALCITY, ZIP5 = p.ZIP5}).ToArray();
MSDN
The obvious solution, instead of
.Select(p => new AddressALL
{
UID_NUM = p.UID_NUM,
ADD_FULL = p.ADD_FULL,
POSTALCITY = p.POSTALCITY,
ZIP5 = p.ZIP5
});
write
.Select(p => new AddressALL
{
UID_NUM = Convert.ToDouble(p.UID_NUM),
ADD_FULL = p.ADD_FULL,
POSTALCITY = p.POSTALCITY,
ZIP5 = p.ZIP5
});
In your select statement .Select(p => new AddressALL{ ... }) you are doing a projection that is trying to pick a new object of type AddressALL for each p, and you are using the object initializer syntax {...} to match the properties of your source objects p with the properties of your target type AddressALL.
Your error message however suggests your p.UID_NUM is of type decimal, while the UID_NUM property on your AddressALL is of type double. Therefore you have to convert the values to the necessary target type.

Sorting and Updating a Generic List of Object based on a Sub Object

I have the following objects:
public class TestResult
{
public string SectionName { get; set; }
public int Score { get; set; }
public int MaxSectionScore { get; set; }
public bool IsPartialScore { get; set; }
public string Name { get; set; }
public int NumberOfAttempts { get; set; }
}
public class TestResultGroup
{
public TestResultGroup()
{
Results = new List<TestResult>();
Sections = new List<string>();
}
public List<TestResult> Results { get; set; }
public List<string> Sections { get; set; }
public string Name { get; set; }
public int Rank { get; set; }
}
So, a TestResultGroup can have any number of results of type TestResult. These test results only differ by their SectionName.
I have a List<TestResultGroup> which I need to sort into descending order based on a score in the Results property, but only when Results has an item whos SectionName = "MeanScore" (if it doesnt have this section we can assume a score of -1). How would I go about ordering the list? Ideally I would also like to apply the result of this ordering to the Rank property.
Many Thanks
List<TestResultGroup> groups = ...
// group test result groups by the same score and sort
var sameScoreGroups = groups.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(gr => gr.Key);
int rank = 1;
foreach (var sameScoreGroup in sameScoreGroups)
{
foreach (var group in sameScoreGroup)
{
group.Rank = rank;
}
rank++;
}
// to obtain sorted groups:
var sortedGroups = groups.OrderByDescending(gr => gr.Rank).ToArray();
Or even write one expression with a side effect:
List<TestResultGroup> groups = ...
int rank = 1;
var sortedGroups = groups
.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(grouping => grouping.Key)
.SelectMany(grouping =>
{
int groupRank = rank++;
foreach (var group in grouping)
{
group.Rank = groupRank;
}
return grouping;
})
.ToArray(); // or ToList

LINQ to SQL select property name by string on projection

How can I achieve the projection on the last select? I need the property defined by the string prop.Name to be selected into the SeriesProjection object.
public override IQueryable<SeriesProjection> FilterOn(string column)
{
//Get metadata class property by defined Attributes and parameter column
var prop = typeof(CommunicationMetaData)
.GetProperties()
.Single(p => p.GetCustomAttribute<FilterableAttribute>().ReferenceProperty == column);
var attr = ((FilterableAttribute)prop.GetCustomAttribute(typeof(FilterableAttribute)));
var param = Expression.Parameter(typeof(Communication));
Expression conversion = Expression.Convert(Expression.Property(param, attr.ReferenceProperty), typeof(int));
var condition = Expression.Lambda<Func<Communication, int>>(conversion, param); // for LINQ to SQl/Entities skip Compile() call
var result = DbQuery.Include(prop.Name)
//.GroupBy(c => c.GetType().GetProperty(attr.ReferenceProperty))
.GroupBy(condition)
.OrderByDescending(g => g.Count())
.Select(group => new SeriesProjection()
{
Count = group.Count(),
Id = group.Key,
//set this navigation property dynamically
Name = group.FirstOrDefault().GetType().GetProperty(prop.Name)
});
return result;
}
For the GroupBy I used the fk property name that's always an int on the Communication entity, but for the select I can't figure out the expression.
[EDIT]
System.Data.Entity.Infrastructure.DbQuery<Communication> DbQuery;
---
[MetadataType(typeof(CommunicationMetaData))]
public partial class Communication
{
public int CommunicationId { get; set; }
public Nullable<int> TopicId { get; set; }
public int CreateById { get; set; }
public virtual Employee CreateByEmployee { get; set; }
public virtual Topic Topic { get; set; }
}
---
public class CommunicationMetaData
{
[Filterable("By Employee", nameof(Communication.CreateById))]
public Employee CreateByEmployee { get; set; }
[Filterable("By Topic", nameof(Communication.TopicId))]
public Topic Topic { get; set; }
}
---
[AttributeUsage(AttributeTargets.Property)]
public class FilterableAttribute : System.Attribute
{
public FilterableAttribute(string friendlyName, string referenceProperty)
{
FriendlyName = friendlyName;
ReferenceProperty = referenceProperty;
}
public string FriendlyName { get; set; }
public string ReferenceProperty { get; set; }
}
---
public class SeriesProjection
{
public int Count { get; set; }
public int Id { get; set; }
public object Name { get; set; }
}
Without some expression helper library, you have to build the whole selector expression manually.
The input of the selector will be a parameter of type IGrouping<int, Communication>, the result type - SeriesProjection, and the body will be MemberInit expression:
var projectionParameter = Expression.Parameter(typeof(IGrouping<int, Communication>), "group");
var projectionType = typeof(SeriesProjection);
var projectionBody = Expression.MemberInit(
// new SeriesProjection
Expression.New(projectionType),
// {
// Count = group.Count(),
Expression.Bind(
projectionType.GetProperty(nameof(SeriesProjection.Count)),
Expression.Call(typeof(Enumerable), "Count", new[] { typeof(Communication) }, projectionParameter)),
// Id = group.Key
Expression.Bind(
projectionType.GetProperty(nameof(SeriesProjection.Id)),
Expression.Property(projectionParameter, "Key")),
// Name = group.FirstOrDefault().Property
Expression.Bind(
projectionType.GetProperty(nameof(SeriesProjection.Name)),
Expression.Property(
Expression.Call(typeof(Enumerable), "FirstOrDefault", new[] { typeof(Communication) }, projectionParameter),
prop.Name))
// }
);
var projectionSelector = Expression.Lambda<Func<IGrouping<int, Communication>, SeriesProjection>>(projectionBody, projectionParameter);
and then of course use simply:
var result = DbQuery.Include(prop.Name)
.GroupBy(condition)
.OrderByDescending(g => g.Count())
.Select(projectionSelector);

How to find value from a Dictionary<int, IEnumerable<Struct>>

I have a dictionary, salaryFitmentDictionary which I would like to query (linq or lambda) based on example: where employeedId = 1 and EarningDeductionId = 145 and get the value of the balance, EDBalance.
How would I achieve this?
var balance = salaryFitmentDictionary.Where...
Dictionary<int, IEnumerable<SalaryFitmentInfoMonth>> salaryFitmentDictionary = new Dictionary<int, IEnumerable<SalaryFitmentInfoMonth>>();
employeeIdList.ToList().ForEach(employeedId =>
{
var perEmployeeFitments = from pf in _db.PayFitments.AsEnumerable()
join ed in _db.EarningDeductions.AsEnumerable()
on pf.EarningDeductionId equals ed.EarningDeductionId
where pf.EmployeeId == employeedId
select new SalaryFitmentInfoMonth
{
EDId = pf.EarningDeductionId,
EDAmount = pf.Amount,
EDBalance = pf.Balance.GetValueOrDefault(),
EDType = ed.EDType,
IsTaxable = ed.IsTaxable,
IsBenefit = ed.IsBenefit,
IsLoan = ed.IsLoan,
IsAdvance = ed.IsAdvance,
Limit = ed.TaxIfMoreThan.GetValueOrDefault()
};
salaryFitmentDictionary.Add(employeedId, perEmployeeFitments);
});
public struct SalaryFitmentInfoMonth
{
public int EDId { get; set; }
public decimal EDAmount { get; set; }
public decimal? EDBalance { get; set; }
public EarnDeduct EDType { get; set; }
public bool IsTaxable { get; set; }
public bool IsBenefit { get; set; }
public bool IsLoan { get; set; }
public bool IsAdvance { get; set; }
public decimal? Limit { get; set; }
}
IEnumerable<SalaryFitmentInfoMonth> salaries = salaryFitmentDictionary[1];
SalaryFitmentInfoMonth salary = salaries.FirstOrDefault(s => s.EDId == 45);
You should handle the case that salaryFitmentDictionary doesn't contain one with this ID. So you could use TryGetValue instead. If no salary has this EDId FirstOrDefault returns null.
So here's the safer version:
IEnumerable<SalaryFitmentInfoMonth> salaries;
if(salaryFitmentDictionary.TryGetValue(1, out salaries))
{
SalaryFitmentInfoMonth salary = salaries.FirstOrDefault(s => s.EDId == 45);
if(salary != null)
{
// do something ...
}
}
If you expect more than one match you could use Enumerable.Where instead of FirstOrDefault.
You could use SelectMany method in LINQ method syntax:
Int32 id = 1;
Int32 edId = 147;
var result = salaryFitmentDictionary.
Where((pair) => pair.Key == id ).
SelectMany((pair) =>
pair.Value.Where((perEmployeeFitment) => perEmployeeFitment.EDId == edId)).
Select(perEmployeeFitment => perEmployeeFitment.EDBalance).
Single();
Or in query syntax:
Int32 id = 1;
Int32 edId = 147;
var result = (from pair in salaryFitmentDictionary
from perEmployeeFitment in pair.Value
where pair.Key == id
where perEmployeeFitment.EDId == edId
select perEmployeeFitment.EDBalance).Single();

what is wrong with my GroupBy Query

Hello all what is wrong with my GroupBy query ?
I have following class:
public class AssembledPartsDTO
{
public int PID { get; set; }
public McPosition Posiotion { get; set; }
public string Partnumber { get; set; }
public string ReelID { get; set; }
public int BlockId { get; set; }
public List<string> References { get; set; }
}
I am trying to perform following query:
assembledPcb.AssembledParts.GroupBy(entry => new
{
entry.PID,
entry.Posiotion.Station,
entry.Posiotion.Slot,
entry.Posiotion.Subslot,
entry.Partnumber,
entry.ReelID,
entry.BlockId
}).
Select( (key , val )=> new AssembledPartsDTO
{
BlockId = key.Key.BlockId,
PID = key.Key.PID,
Partnumber = key.Key.Partnumber,
ReelID = key.Key.ReelID,
Posiotion = new McPosition(key.Key.Station, key.Key.Slot, key.Key.Subslot),
References = val <-- ????
})
But the val that I have there is of type int and not the val of grouping that I can do there val.SelectMany(v => v).ToList(); any idea what is wrong in my code ?
The second parameter of Enumerable.Select is the index of the item in the sequence. So in this case it is the (zero based) number of the group. You just want to select the group, you don't need it's index:
var result = assembledPcb.AssembledParts.GroupBy(entry => new
{
entry.PID,
entry.Posiotion.Station,
entry.Posiotion.Slot,
entry.Posiotion.Subslot,
entry.Partnumber,
entry.ReelID,
entry.BlockId
})
.Select(g => new AssembledPartsDTO
{
BlockId = g.Key.BlockId,
PID = g.Key.PID,
Partnumber = g.Key.Partnumber,
ReelID = g.Key.ReelID,
Posiotion = new McPosition(g.Key.Station, g.Key.Slot, g.Key.Subslot),
References = g.SelectMany(entry => entry.References)
.Distinct()
.ToList()
});
(assuming that you want a list of distinct references)
Side-Note: you have a typo at the property-name: Posiotion

Categories

Resources