How merge two sequences into one? - c#

I have some working code that retrieves data from data base. It is interesting for me to get some better code for my solution. Are there some ways to combine two queries into one or something like this?
Dim customerTitlesAndIDs = contex.CustomerTable.Select(Function(row) New
With {.ID = row.ID, .CustomerTitle = row.Title}).ToList()
Dim cutomerIdPayment = contex.CustomerPayments.Select(Function(table) New
With
{
.ID = table.CustomerID,
.Range = table.PaymentsRange,
.Values = table.Values
}).ToList()
Dim customerInfos As New List(Of SCustomerInfo)
For Each customer In customerTitlesAndIDs
Dim cID As Integer = customer.ID
customerInfo.Add(New SCustomerInfo(CreateCustomerTable(), cID, customer.CustomerTitle))
For Each cutomerPayments In cutomerIdPayment
If cutomerPayments.ID = cID Then
Dim rangeValue(1) As Object
rangeValue(0) = cutomerPayments.Range
rangeValue(1) = cutomerPayments.Values
Dim dtRow As DataRow = customerInfos.Last().PaymentTable.NewRow()
dtRow.ItemArray = rangeValue
customerInfos.Last().PaymentTable.Rows.Add(dtRow)
End If
Next
Next
Return customerInfos
Same code with C# (hope no syntax errors occurred):
var customerTitlesAndIDs = contex.CustomerTable.Select(row => new
{ .ID = row.ID, .CustomerTitle = row.Title }).ToList();
var cutomerIdPayment = contex.CustomerPayments.Select(table => new
{
.ID = table.CustomerID,
.Range = table.PaymentsRange,
.Values = table.Values
}).ToList();
List<SCustomerInfo> customerInfos = new List<SCustomerInfo>;
foreach (var customer in customerTitlesAndIDs)
{
int cID = customer.ID;
customerInfos.Add(new SCustomerInfo(CreateCustomerTable(), cID, customer.CustomerTitle));
foreach (var cutomerPayments in cutomerIdPayment)
{
if (cutomerPayments.ID = cID)
{
object[] rangeValue = new object[1] {cutomerPayments.Range, cutomerPayments.Values};
DataRow dtRow = customerInfos.Last().PaymentTable.NewRow();
dtRow.ItemArray = rangeValue;
customerInfos.Last().PaymentTable.Rows.Add(dtRow);
}
}
}
SCustomerInfo represented by folowing Structure (code is simplified):
Public Structure SWindAltitude
Public PaymentTableAs DataTable
Public Title As String
Public ID As Integer
End Structure
Both C# and VB.NET solutions will be helpful.

Try something like this, utilizing navigation properties (you'll probably have to massage it as I don't know the exact makeup of your data structures):
var customerQuery = context.CustomerTable.Select( ct =>
new {
ct.ID,
ct.CustomerTitle,
// use nav property to get customer payments
CustomerPayments = ct.CustomerPayments.Select( cp =>
new {
Range = cp.Range,
Values = cp.Values } ) } );
return customerQuery.ToArray()
.Select( cq =>
{
var retVal = new SCustomerInfo( CreateCustomerTable(), cq.ID, cq.CustomerTitle );
foreach( var customerPayment in cq.CustomerPayments )
{
var dtRow = cq.PaymentTable.NewRow();
dtRow.ItemArray = new object[] { customerPayment.Range, customerPayment.Values };
retVal.PaymentTable.Rows.Add( dtRow );
}
return retVal;
} );

if i understand right in c# with linq it will be something like this
var customerInfos = customerTitlesAndIDs.Select((c)=>{
var ci = new SCustomerInfo(CreateCustomerTable(), c.ID, customer.CustomerTitle);
ci.PaymentTable = ci.PaymentTable.AsEnumerable().Union(
cutomerIdPayment.Where(j=>j.ID == c.ID)
.Select(j=>{
var dtRow = ci.PaymentTable.NewRow();
dtRow.ItemArray = new object[] {
customerPayment.Range,
customerPayment.Values
};
return dtRow;
})).CopyToDataTable();
return ci;
}).ToList();

I think you can use the Linq provided function Sequence.concat() as described here: http://msdn.microsoft.com/en-us/library/vstudio/bb386979(v=vs.100).aspx

Related

LINQ to Entities does not recognize the method 'StaticMethod' method

I have a code like this:
using (var ws = new WebService())
using (var db = new EntityFrameworkModel())
{
var originalFolders = ws.GetFolders();
foo.folders = originalFolders.Select(c => new FolderType()
{
Id = c.Description,
Items = ws.ListDocs(c.Id)
.Select((d, i) =>
new DocType()
{
Id = StaticMethod(d, c),
Order = i,
SomeValue = db.docs.Single(doc => doc.Id == StaticMethod(d, c)).SomeValue
}
).ToArray()
}).ToArray();
}
But I get a "LINQ to Entities does not recognize the method 'StaticMethod' method, and this method cannot be translated into a store expression" exception. Does exist any way to pass a static value as a parameter? Something like this:
using (var ws = new WebService())
using (var db = new EntityFrameworkModel())
{
var originalFolders = ws.GetFolders();
foo.folders = originalFolders.Select(c => new FolderType()
{
Id = c.Description,
Items = ws.ListDocs(c.Id)
.Select((d, i, string myValue = StaticMethod(d, c)) =>
new DocType()
{
Id = myValue,
Order = i,
SomeValue = db.docs.Single(doc => doc.Id == myValue).SomeValue
}
).ToArray()
}).ToArray();
}
I can't modify DocType class constructor. Does exist any way?
Usually this is a matter of making sure you don't inline functions in linq-to-SQL expressions that can't be turned into valid SQL.
Try this:
using (var ws = new WebService())
using (var db = new EntityFrameworkModel())
{
var originalFolders = ws.GetFolders();
foo.folders = originalFolders.Select(c => new FolderType()
{
Id = c.Description,
Items = ws.ListDocs(c.Id)
.Select((d, i) =>
{
var id = StaticMethod(d, c);
return new DocType()
{
Id = id,
Order = i,
SomeValue = db.docs.Single(doc => doc.Id == id).SomeValue
};
}).ToArray()
}).ToArray();
}

Create objects into model foreach query

I have a simple model like:
public class StatisticsModel
{
public Guid TaskId { get; set; }
public string Name { get; set; }
public int Time {get; set; }
}
Now I want to add to that model each register of foreach clause like:
StatisticsModel model = new StatisticsModel();
foreach(var i in tasks)
{
var parameters = ToDataTable(tasks.Select(x => new { i.TaskId, i.Name }).ToList());
var timeOfTasks = db.ExeSQLParamTable("usp_Get_WorkedProyectTime", parameters, "#ProjectTimeWorkedTableType");
var test = (from DataRow dr in timeOfTasks.Rows select (int)dr["TaskName"]).FirstOrDefault();
model.TaskId = i.TaskId;
model.Name = i.Name;
model.Time = test
}
var final2 = model;
So as you can see I assign TaskId, Name and Time to model, but when foreach finish it only add last register to model. How can I create one object foreach task(in my case) in model? I try something like model.Add()but add method donesn't exists in my model. What am I doing wrong? Regards
You're close, you just need a list of models:
List<StatisticsModel> models = new List<StatisticsModel>();
foreach(var i in tasks)
{
StaticsModel model = new StatisticsModel();
var parameters = ToDataTable(tasks.Select(x => new { i.TaskId, i.Name }).ToList());
var timeOfTasks = db.ExeSQLParamTable("usp_Get_WorkedProyectTime", parameters, "#ProjectTimeWorkedTableType");
var test = (from DataRow dr in timeOfTasks.Rows select (int)dr["TaskName"]).FirstOrDefault();
model.TaskId = i.TaskId;
model.Name = i.Name;
model.Time = test;
models.Add(model);
}
You can't add multiple attributes to a single thing: you must have multiple StatisticsModel objects for this.
For example, you could use a List<StatisticsModel>:
var models = List<StatisticsModel>();
foreach(var i in tasks) {
StatisticsModel model = new StatisticsModel();
var parameters = ToDataTable(tasks.Select(x => new { i.TaskId, i.Name }).ToList());
var timeOfTasks = db.ExeSQLParamTable("usp_Get_WorkedProyectTime", parameters, "#ProjectTimeWorkedTableType");
var test = (from DataRow dr in timeOfTasks.Rows select (int)dr["TaskName"]).FirstOrDefault();
model.TaskId = i.TaskId;
model.Name = i.Name;
model.Time = test;
models.Add(model);
}
If you prefer LINQ-style queries, you could eliminate the loop altogether:
var models = tasks.Select(i => {
var parameters = ToDataTable(tasks.Select(x => new { i.TaskId, i.Name }).ToList());
var timeOfTasks = db.ExeSQLParamTable("usp_Get_WorkedProyectTime", parameters, "#ProjectTimeWorkedTableType");
var test = (from DataRow dr in timeOfTasks.Rows select (int)dr["TaskName"]).FirstOrDefault();
return new StatisticsModel {
TaskId = i.TaskId
, Name = i.Name
, Time = test
};
}).ToList();
Sounds like you need a List<StatisticsModel> at the top, that way you can add to it in your foreach:
List<StatisticsModel> models = new List<StatisticsModel>();
foreach(...)
{
...
StatisticsModel model = new StatisticsModel();
// Assign props
models.Add(model);
}
Or even better; since you are actually just transforming the task collection, just use a select:
List<StatisticsModel> models = tasks.Select(task => {
//The foreach contents
StatisticsModel model = new StatisticsModel();
// Assign props
return model;
}).ToList(); //ToList not necessary; eager enumeration is not ideal most of the time but included since you seem to want a list
Each iteration of your foreach loop overwrites the model. Store it in a List:
List<StatisticsModel> allModels = new List<StatisticsModel>();
foreach(var i in tasks)
{
StatisticsModel model = new StatisticsModel();
var parameters = ToDataTable(tasks.Select(x => new { i.TaskId, i.Name }).ToList());
var timeOfTasks = db.ExeSQLParamTable("usp_Get_WorkedProyectTime", parameters, "#ProjectTimeWorkedTableType");
var test = (from DataRow dr in timeOfTasks.Rows select (int)dr["TaskName"]).FirstOrDefault();
model.TaskId = i.TaskId;
model.Name = i.Name;
model.Time = test
allModels.Add(model);
}
var final2 = allModels;
you need to create a list (or some other collection) to get a list of the models. Here's the code below
List<StatisticsModel> models = new List<StatisticsModel>();
foreach(var i in tasks)
{
var parameters = ToDataTable(tasks.Select(x => new { i.TaskId, i.Name }).ToList());
var timeOfTasks = db.ExeSQLParamTable("usp_Get_WorkedProyectTime", parameters, "#ProjectTimeWorkedTableType");
var test = (from DataRow dr in timeOfTasks.Rows select (int)dr["TaskName"]).FirstOrDefault();
StatisticsModel model = new StatisticsModel();
model.TaskId = i.TaskId;
model.Name = i.Name;
model.Time = test
models.Add(model);
}
var final2 = models; //list of model

StringBuilder within IEnumerable

I have a ControlMeasure table that holds information on each control measure and a ControlMeasurepeopleExposed Table that holds a record for each person exposed in the control measure this could be 1 record or many records.
I Have a controller that populates a List view
For each item in the list, Control Measure, I would like to create a string that shows all the People at risk
e.g.
PeopleString = "Employees, Public, Others";
Ive added a foreach in the controller to show what I'm trying to do however I'm aware that this wont work.
The controller is this:
public ActionResult ControlMeasureList(int raId)
{
//Populate the list
var hazards = new List<Hazard>(db.Hazards);
var controlMeasures = new List<ControlMeasure>(db.ControlMeasures).Where(x => x.RiskAssessmentId == raId);
var cmcombined = (
from g in hazards
join f in controlMeasures
on new { g.HazardId } equals new { f.HazardId }
select new CMCombined
{
Activity = f.Activity,
ControlMeasureId = f.ControlMeasureId,
ExistingMeasure = f.ExistingMeasure,
HazardName = g.Name,
LikelihoodId = f.LikelihoodId,
Rating = f.Rating,
RiskAssessmentId = f.RiskAssessmentId,
SeverityId = f.SeverityId,
}).OrderBy(x => x.Activity).ToList();
var cmPeopleExp = new List<ControlMeasurePeopleExposed>(db.ControlMeasurePeopleExposeds).Where(x => x.RiskAssessmentId == raId);
var peopleExp = from c in cmPeopleExp
join d in db.PeopleExposeds
on c.PeopleExposedId equals d.PeopleExposedId
orderby d.Name
select new RAPeopleExp
{
RAPeopleExpId = c.PeopleExposedId,
PeopleExpId = c.PeopleExposedId,
PeopleExpName = d.Name,
RiskAssessmentId = c.RiskAssessmentId,
ControlMeasureId = c.ControlMeasureId
};
var model = cmcombined.Select(t => new FullControlMeasureListViewModel
{
ControlMeasureId = t.ControlMeasureId,
HazardName = t.HazardName,
LikelihoodId = t.LikelihoodId,
Rating = t.Rating,
SeverityId = t.SeverityId,
Activity = t.Activity,
ExCM = t.ExistingMeasure,
//This section here is where I'm struggling
var PeopleString = new StringBuilder();
foreach (var p in peopleExp)
{
PeopleString.AppendLine(p.PeopleName);
{
PeopleExposed = PeopleString,
});
return PartialView("_ControlMeasureList", model);
}
I know I cant directly put this code in the controller but it does represent what I want to do.
You can't foreach within an object initializer (which is what you're trying to do when instantiating FullControlMeasureListViewModel). You can, however, use a combination of string.Join and peopleExp.Select:
var model = cmcombined.Select(t => new FullControlMeasureListViewModel
{
//other props
PeopleExposed = string.Join(",", peopleExp
.Where(p => p.ControlMeasureId == t.ControlMeasureId)
.Select(p => p.PeopleExpName));
//other props
});

Check for missing elements while using LINQ to XML

I am trying get data from the xml. Below is the code which
gets data from the XDocument and return list<t>.
However, p.Element("Sponsor") can sometimes be null. How can I check for the null values
var atClauseList = doc.Descendants(CLAUSE_GROUP_TAG).Descendants(AT_CLAUSE_TAG).Select(p => new AtClause()
{
ClauseNumber = (string)p.Element("Number"),
Sponsors = p.Element("Sponsor").Elements(SPONSOR_TAG).Select(y => y.Value)
.ToList(),
Page = p.Element("Sponsor").Element("aItem").Element("AmendText").Element("Page").ElementValueNull(),
Line = p.Element("Sponsor").Element("aItem").Element("AmendText").Element("Line").ElementValueNull(),
LineText = p.Element("Sponsor").Element("aItem").Element("AmendText").Nodes().OfType<XText>().FirstOrDefault().XTextValueNull(),
ItalicText = p.Element("Sponsor").Element("aItem").Element("AmendText").Element("Italic").ElementValueNull(),
ParaList = p.Element("Sponsor").Element("aItem").Element("AmendText").Elements("Para").Select(L => new Para
{
ParaText = (string)L,
Number = ((System.Xml.Linq.XElement)(L)).AttributeValueNull("Number"),
Quote = ((System.Xml.Linq.XElement)(L)).AttributeValueNull("Quote"),
}
).ToList()
}).ToList();
move your code out of an object initializer, and add some logic to it:
var atClauseList = new List<AtClause>();
foreach(var item in doc.Descendants(CLAUSE_GROUP_TAG).Descendants(AT_CLAUSE_TAG))
{
var atClause = new AtClause();
atClause.ClauseNumber = (string)item.Element("Number");
var sponsor = item.Element("Sponsor");
if (sponsor != null)
{
atClause.Sponsors = sponsor.Elements(SPONSOR_TAG).Select(y => y.Value).ToList();
atClause.Page = sponsor.Element("aItem").Element("AmendText").Element("Page").ElementValueNull();
atClause.Line = sponsor.Element("aItem").Element("AmendText").Element("Line").ElementValueNull();
atClause.LineText = sponsor.Element("aItem").Element("AmendText").Nodes().OfType<XText>().FirstOrDefault().XTextValueNull();
atClause.ItalicText = sponsor.Element("aItem").Element("AmendText").Element("Italic").ElementValueNull();
atClause.ParaList = sponsor.Element("aItem").Element("AmendText").Elements("Para").Select(L => new Para
{
ParaText = (string)L,
Number = ((System.Xml.Linq.XElement)(L)).AttributeValueNull("Number"),
Quote = ((System.Xml.Linq.XElement)(L)).AttributeValueNull("Quote"),
}).ToList();
atClauseList.Add(atClause);
}
You can use sequences rather than leaving the IEnumerable immediately:
var value = (string)p.Elements("Sponsor")
.Elements("aItem")
.Elements("AmendText")
.Elements("Page")
.SingleOrDefault()

Sum of fields using LINQ

Some background before asking my question.
Im using sql compact, and i have two tables,
The first table (IssueEmp)
The second table (RecEmp)
SqlCeDataAdapter adap = new SqlCeDataAdapter("SELECT * FROM RecEmp", cn);
DataTable dat = new DataTable();
DataSet receice = new DataSet();
adap.Fill(receice);
adap.Fill(dat);
SqlCeDataAdapter adap1 = new SqlCeDataAdapter("SELECT * FROM IssueEmp", cn);
DataTable dat1 = new DataTable();
DataSet issue = new DataSet();
adap1.Fill(issue);
adap1.Fill(dat1);
Im performing a join between RecEmp and IssueEmp using linq
var res = from t1 in receice.Tables[0].AsEnumerable()
join t2 in issue.Tables[0].AsEnumerable()
on new
{
CNo = t1.Field<int>("CNo"),
Empid = t1.Field<int>("EmpID")
}
equals new
{
CNo = t2.Field<int>("CNo"),
Empid = t2.Field<int>("EmpID")
}
select new
{
SNo = t1.Field<int>("SNo"),
ChNo = t1.Field<int>("CNo"),
EmpID = t1.Field<int>("EmpID"),
DateIssued = t2.Field<DateTime>("Date"),
RMIssued = t2.Field<string>("RMCode"),
QuantityIssued = t2.Field<double>("Quantity"),
DateReceived = t1.Field<DateTime>("Date"),
RMCodeReceived = t1.Field<string>("RMCode"),
QuantityReceived = t1.Field<double>("Quantity")
};
The output Im getting from the above linq query is
But I don't know how to get the sum of issued quantity likewise the sum of received quantity, lastly the difference between the two sum as the diff. The required is below.
Note:
I´m a bit lazy so I didn´t use all the records you provided, only the first four records.
Expected result:
This is what I got:
The Linq query:
var query = from d in data
group d by new { d.DateIssued, d.EmpId, d.ChNo, d.DateReceived }
into x
select new {
Date = x.Key.DateIssued,
CNo = x.Key.ChNo,
EmpId=x.Key.EmpId,
CRi = x.Where(c=>c.RMIssued == "CR").Sum(c=>c.QuantityIssued),
SJi = x.Where(c=>c.RMIssued == "SJ").Sum(c=>c.QuantityIssued),
TTi = x.Where(c=>c.RMIssued == "TT").Sum(c=>c.QuantityIssued),
WRi = x.Where(c=>c.RMIssued == "WR").Sum(c=>c.QuantityIssued),
TotalIssued = x.Sum(c => c.QuantityIssued),
DateReceived = x.Key.DateReceived,
CRr = x.Where(c=>c.RMCodeReceived == "CR").Sum(c=>c.QuantityReceived),
SJr = x.Where(c=>c.RMCodeReceived == "SJ").Sum(c=>c.QuantityReceived),
TTr = x.Where(c=>c.RMCodeReceived == "TT").Sum(c=>c.QuantityReceived),
WRr = x.Where(c=>c.RMCodeReceived == "WR").Sum(c=>c.QuantityReceived),
TotalReceived = x.Sum(c => c.QuantityReceived),
Diff = x.Sum(c => c.QuantityIssued) - x.Sum(c => c.QuantityReceived)
};
Data used:
And this is the set of data I used to test it:
var data= new []{
new { SNo= 9, ChNo=5, EmpId=81, DateIssued=dateIssued, RMIssued="SJ", QuantityIssued=30, DateReceived=dateReceived, RMCodeReceived="SJ", QuantityReceived=20.3},
new { SNo= 10, ChNo=5, EmpId=81, DateIssued=dateIssued, RMIssued="SJ", QuantityIssued=30, DateReceived=dateReceived, RMCodeReceived="CR", QuantityReceived=9.6},
new { SNo= 11, ChNo=28, EmpId=82, DateIssued=dateIssued, RMIssued="TT", QuantityIssued=30.5, DateReceived=dateReceived, RMCodeReceived="TT", QuantityReceived=29},
new { SNo= 12, ChNo=28, EmpId=82, DateIssued=dateIssued, RMIssued="WR", QuantityIssued=10, DateReceived=dateReceived, RMCodeReceived="TT", QuantityReceived=29}
};
I recommed you use LinqPad to test it.
Good luck!

Categories

Resources