Using Let in Lambda Expression while querying from a datatable - c#

I have a data table and I need to convert the contents of that data table to a class List and I used Linq for that.It worked well.But I was asked to convert that linq query to Lambda Expression and there I had a Little trouble while using Let.I will the sample code.
Working linq query:
var NewUser = (from dt in dsMappedDataFields.Tables[0].Rows.Cast<DataRow>()
let tempDetails = dt.Field<string>("Name") == "Rojer" ? "NW" : "India"
let tempNumber = tempDetails == "India" ? "918956" : "0456"
select new User
{
Name = dt.Field<string>("Name"),
Age = dt.Field<int>("Age"),
Details = tempDetails,
Number = tempNumber
}).ToList();
Lambda expression:
var User = dsMappedDataFields.Tables[0].Rows.Cast<DataRow>().
Select(dr =>
new User
{
Name = dr.Field<string>("Name"),
Age = dr.Field<int>("Age"),
Details = dr.Field<string>("Details"),
Number = dr.Field<string>("Number")
}).ToList();
As you can see I have to check some conditions before converting the data to list which I have done earlier.. Please do help me with solving this issue.

The lambda you pass to Select can have a block, so you can add any kind of code there:
var User = dsMappedDataFields.Tables[0].Rows
.Cast<DataRow>()
.Select(dr =>
{
var tempDetails = dt.Field<string>("Name") == "Rojer" ? "NW" : "India";
var tempNumber = tempDetails == "India" ? "918956" : "0456";
return new User
{
Name = dr.Field<string>("Name"),
Age = dr.Field<int>("Age"),
Details = tempDetails,
Number = tempNumber,
};
})
.ToList();

We can use lambda expression in this case :
var User = dsMappedDataFields.Tables[0].Rows.Cast <DataRow>().
Select(dr = > new User {
Name = dr.Field<string>("Name"),
Age = dr.Field<int>("Age"),
Details = dr.Field<string>("Name") == "Rojer" ? "NW" : "India",
Number = dr.Field<string>("Name") == "Rojer" ? 0456 : 918956,
}).ToList();
I tried the sample it works as you need.
Thanks,
Narasimha

Related

Call Function In LinQ Query

I want to sum price for all products that is in list.
I called a funtion in linQ query.
Total = t0.TbOfferProducts.Sum(x => Customs.CalculateCurrency(x.TbOffer.Price))
But it didnt recognize my function
I wrote another function for linQ, then I called it. But linQ dont recognize my function.
Error:
LINQ to Entities does not recognize the method 'Double Cal_Price(Int32)' method, and this method cannot be translated into a store expression.
I try other versions but none of them didnt work.Help me please.
myList =
(from t0 in DB.TbProducts
where t0.BoActive == true && t0.BoSoftDeleted == false
let price = Cal_Price(t0.InProductId)
select new ProductActivityInfo
{
ID = t0.InProductId,
Name = t0.StProductName,
Code = t0.StProductCode,
Total = price
})
public double Cal_Price(int productId)
{
double total = 0;
using (MyEntityContext DB = new MyEntityContext())
{
var list = DB.TbOfferProducts.Where(x => x.InProductId == productId);
foreach (var item in list)
{
total += Customs.CalculateCurrency(item.TbOffer.Price);
}
}
return total;
}
EF Core is tryng to build SQL but fails when found custom compiled method in query. Correct Total on the client side:
// calculate sum by grouping
var offerPrices =
from op in DB.TbOfferProducts
group op.TbOffer.Price by x.InProductId
select new
{
ProductId = g.Key,
RawPrice = g.Sum()
};
var result =
(from t0 in DB.TbProducts
join op in offerPrices on t0.InProductId equals op.ProductId
where t0.BoActive == true && t0.BoSoftDeleted == false
select new ProductActivityInfo
{
ID = t0.InProductId,
Name = t0.StProductName,
Code = t0.StProductCode,
Total = op.RawPrice
})
.ToList();
// correct Total on the client side
result.ForEach(x => x.Total = Customs.CalculateCurrency(x.Total));

Linq to SQL grouping with embedded lists causes too many queries

I am developing a query to grab and join some SQL tables in C# and am having some trouble with grouping and enumerables within the dataset. My query is below. This gives me the data in the format I'm looking for, but it takes way too long when I try to add the enumerated list as indicated below. When I look under the hood I can see it is executing way too many SQL queries. I'd like to get it to just one. Using LinqPad:
void Main()
{
var nightlyRuns = (from a in LoadTestSummaries
join b in LoadTestTestSummaryData
on a.LoadTestRunId equals b.LoadTestRunId
where a.TargetStack == "LoadEnv" &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
orderby a.StartTime
group new {a, b} by new
{
a.TestGuid,
a.Name,
a.Description,
a.StartTime,
a.Duration,
a.NumAgents,
a.NumHosts,
a.PassFail,
a.ResultsFilePath,
a.Splunk
}
into g
let scenarioStart = g.Min(s => s.a.StartTime) ?? g.Min(s => s.a.DateCreated)
let testCases = g.Select(s => s.b)
orderby scenarioStart
select new
{
TestGuid = g.Key.TestGuid,
ScenarioRun = new
{
Name = g.Key.Name,
Description = g.Key.Description,
StartTime = scenarioStart,
Duration = g.Key.Duration,
NumAgents = g.Key.NumAgents,
NumHosts = g.Key.NumHosts,
Result = g.Key.PassFail,
ResultsFilePath = g.Key.ResultsFilePath,
SplunkLink = g.Key.Splunk,
// PROBLEM: Causes too many queries:
TestRuns = from t in testCases select t.TestCaseId
}
}).ToLookup(g => g.TestGuid, g => g.ScenarioRun);
nightlyRuns["ba593f66-695f-4fd1-99c3-71253a2e4981"].Dump();
}
The "TestRuns" line is causing the excessive queries. Any idea what I am doing wrong here?
Thanks for any insight.
Tough answer to test but I think we can avoid the grouping and multiple queries with something like this: (https://msdn.microsoft.com/en-us/library/bb311040.aspx)
var nightlyRuns = (from a in LoadTestSummaries
join b in LoadTestTestSummaryData
on a.LoadTestRunId equals b.LoadTestRunId
where a.TargetStack == "LoadEnv" &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
into testGroup
select new
{
TestGuid = a.TestGuid,
ScenarioRun = new
{
Name = a.TestGuid,
Description = a.Description,
StartTime = a.StartTime ?? a.DateCreated,
Duration = a.Duration,
NumAgents = g.Key.NumAgents,
NumHosts = a.NumHosts,
Result = a.PassFail,
ResultsFilePath = a.ResultsFilePath,
SplunkLink = a.Splunk,
// PROBLEM: Causes too many queries:
TestRuns =testGroup
}
}).OrderBy(x=>x.StartTime).ToLookup(x => x.TestGuid, x => x.ScenarioRun);
nightlyRuns["ba593f66-695f-4fd1-99c3-71253a2e4981"].Dump();

Conditional Where clause in LINQ

suppose i am showing data in grid and i have many textboxes for filter the data.
textbox for employee id. if employee id textbox is empty then no where clause will be added but if it is not empty then where clause will be added for that. the same way we can filter data if salary textbox has value or employee name textbox has value.
i try to compose a conditional LINQ query but got error. here is mine
var sName="";
var r = from t in TblFamilies
where 1 == 1
if(sName!="")
{
&& t.Name="Keith";
};
select new
{
t.ID,
t.ParentID,
t.Name,
t.CurDate
};
r.Dump();
Try this:-
First select the data:-
var r = from t in TblFamilie
select new
{
t.ID,
t.ParentID,
t.Name,
t.CurDate
};
Then you can filter based on condition:-
if (sName!="")
r = r.Where(x => x.Name == sName);
If you want to mix And operator and Or operator together, check PredicateBuilder out here: http://www.albahari.com/nutshell/predicatebuilder.aspx
You can simply write like:
// begin with true if you start with And operator.
var predicate = PredicateBuilder.True<TblFamilie>();
predicate = predicate.And(t => t.CureDate < DateTime.UtcNow.AddDays(-1));
// you can mix with Or operator too.
predicate = predicate.Or(t => t.Name.Contains("blah"));
var results = context.TblFamilie
.Where(predicate)
.Select(new
{
// your projection here...
});
// begin with false if you start with Or operator.
var predicate2 = PredicateBuilder.False<TblFamilie>();
predicate2 = predicate2.Or(t => t.CureDate < DateTime.UtcNow.AddDays(-1));
// you can mix with And operator too.
predicate2 = predicate2.And(t => t.Name.Contains("blah"));
var results = context.TblFamilie
.Where(predicate)
.Select(new
{
// your projection here...
});
// even nesting is possible
var inner = PredicateBuilder.False<TblFamilie>();
inner = inner.Or (p => p.Name.Contains("foo"));
inner = inner.Or (p => p.Name.Contains("bar"));
var outer = PredicateBuilder.True<TblFamilie>();
outer = outer.And (p => p.CureDate > DateTime.UtcNow.AddDays(-3));
outer = outer.And (p => p.CureDate < DateTime.UtcNow.AddDays(-1));
outer = outer.And (inner);
var results = context.TblFamilie
.Where(outer)
.Select(new
{
// your projection here...
});
Updated
Okay, lets assume you have a Family class, and you get 'Families' from some where. You can use PredicateBuilder like this:
// you have 4 families from DB, API or anywhere.
var failies = new List<Family>
{
new Family { Id = 1, ParentId = 1, Name = "foo", Birthday = new DateTime(1971, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "bar", Birthday = new DateTime(1982, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "foobar", Birthday = new DateTime(1993, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "fake", Birthday = new DateTime(2000, 1, 1) },
};
// make predicate!
// if a family's Birthday is before than 1980 'or' Name contains "ke".
var predicate = PredicateBuilder.True<Family>();
predicate = predicate.And(o => o.Birthday < new DateTime(1980, 1, 1));
predicate = predicate.Or(o => o.Name.Contains("ke"));
// you should make IQueryable in order to use PredicateBuilder.
var result = failies.AsQueryable()
.Where(predicate)
.Select(o => new
{
o.Id, o.Name, o.Birthday // only project what you want.
})
.ToList();
// now, result should contains "foo" and "fake".
foreach (var family in result)
{
Debug.WriteLine("Name: " + family.Name);
}
Updated2
You can copy & paste to LinqPad in order to test how it works. Before you run this in the LinqPad,
Download LinqKit.dll from above link.
Make sure press 'F4' > Add > Browse > select LinqKit.dll > Add LinqKit namespace in the 'Additional Namespace Imports' tab.
In the Query panel, choose Language to 'C# Statement(s)'
paste this and run.
// you have 4 strings from DB, API or anywhere.
var strings = new List<string>
{
"foo",
"bar",
"foobar",
"fake"
};
// make predicate!
// if a string contains "oo" or "ke"
var predicate = PredicateBuilder.True<string>();
predicate = predicate.And(o => o.Contains("oo"));
predicate = predicate.Or(o => o.Contains("ke"));
// you should make IQueryable in order to use PredicateBuilder.
var result = strings.AsQueryable()
.Where(predicate)
.ToList();
// now, result should contains "foo", "foobar" and "fake".
foreach (var stringResult in result)
{
Debug.WriteLine("Name: " + stringResult);
}

Converting a VB code with an Aggregate clause to C#

I've been trying to convert the following the code from VB to C#:
Dim rowsnotfound As DataRow() = (From rowstb2 As DataRow In dsNew.Tables("parts").Rows.OfType(Of DataRow)() Where (Aggregate rowstb1 As DataRow In dsOld.Tables("parts").Rows.OfType(Of DataRow)() Where ((rowstb1.Item("TRANSACTION")) = (rowstb2.Item("TRANSACTION")) And (rowstb1.Item("DESCRIPTION")) = (rowstb2.Item("DESCRIPTION")) And (rowstb1.Item("QTY")) = (rowstb2.Item("QTY")) And (rowstb1.Item("PART_NUM")) = (rowstb2.Item("PART_NUM"))) Into Count()) = 0).ToArray
I know this will appear as a long strand of code on the computer so here is a snippet of the aggregate portion of the code so you can read it easier:
(Aggregate rowstb1 As DataRow In dsOld.Tables("parts").Rows.OfType(Of DataRow)() Where ((rowstb1.Item("TRANSACTION")) = (rowstb2.Item("TRANSACTION")) And (rowstb1.Item("DESCRIPTION")) = (rowstb2.Item("DESCRIPTION")) And (rowstb1.Item("QTY")) = (rowstb2.Item("QTY")) And (rowstb1.Item("PART_NUM")) = (rowstb2.Item("PART_NUM"))) Into Count())
I researched the aggregate clause and know the basics of how it functions. However, I am having a tremendous amount of trouble converting it to C#. Any help will be greatly appreciated.
What you are doing appears to be equivalent of a SQL LEFT JOIN operation where you want to find elements in the First collection that are not in the Second. You can do this with out having to use an aggregate by using the following:
IEnumerable<DataRow> newRows = dsNew.Tables["parts"].Rows.OfType<DataRow>();
IEnumerable<DataRow> oldRows = dsOld.Tables["parts"].Rows.OfType<DataRow>();
DataRow[] rowsNotFound = newRows
.GroupJoin(oldRows,
o => new
{
Transaction = o.Field<int>("TRANSACTION"),
Description = o.Field<string>("DESCRIPTION"),
Quantity = o.Field<int>("QTY"),
PartNumber = o.Field<string>("PART_NUM")
},
i => new
{
Transaction = i.Field<int>("TRANSACTION"),
Description = i.Field<string>("DESCRIPTION"),
Quantity = i.Field<int>("QTY"),
PartNumber = i.Field<string>("PART_NUM")
},
(o, i) => new {NewRow = o, OldRows = i})
.SelectMany(g => g.OldRows.DefaultIfEmpty(), (g, oldRow) => oldRow == null ? g.NewRow : null)
.Where(r => r != null)
.ToArray();
I didn't know the data types so I guessed based on the field names.
I think it'll be something like:
DataRow[] rowsnotfound = (
from DataRow rowstb2 in dsNew.Tables("parts").Rows.OfType<DataRow>()
where ((
from DataRow rowstb1 in dsOld.Tables("parts").Rows.OfType<DataRow>()
where (rowstb1["TRANSACTION"] == rowstb2["TRANSACTION"]
&& rowstb1["DESCRIPTION"] == rowstb2["DESCRIPTION"]
&& rowstb1["QTY"] == rowstb2["QTY"]
&& rowstb1["PART_NUM"] == rowstb2["PART_NUM"])
select rowstb1).Count()) == 0).ToArray();

How to combine 2 linq statments with groupby clause into 1

I was wondering if i can consolidate below 2 linq statments into 1 statment. I am sure it should be possible, but various attempts i am unable to manage.
var prevProvisionsBySubBook = (from provision in prevProvisions
group provision by provision.SubBook
into subBookGrouping
select
new
{
Key = subBookGrouping.Key,
Value = subBookGrouping.Sum(t => t.ProvisionUSD)
});
var currentProvisionsBySubBook
= (from provision in currentProvisions
group provision by provision.SubBook
into subBookGrouping
select new
{
Key = subBookGrouping.Key,
Value = subBookGrouping.Sum(t => t.ProvisionUSD)
});
var adjustmentChangeBySubBook
= (from current in currentProvisionsBySubBook
select new
{
Key = current.Key,
Value = current.Value
- (prevProvisionsBySubBook.Any() ? prevProvisionsBySubBook.Where(t => t.Key == current.Key).Single().Value : 0)
});
any help would be apprecaited.
You can do it like this:
var adjustmentChangeBySubBook =
from provision in
(from currentProvision in currentProvisions select new
{
currentProvision.SubBook,
CurrentUSD = currentProvision.ProvisionUSD,
PreviousUSD = 0
}).Concat
(from prevProvision in prevProvisions select new
{
prevProvision.SubBook,
CurrentUSD = 0,
PreviousUSD = prevProvision.ProvisionUSD
})
group provision by provision.SubBook into subBookGrouping select new
{
subBookGrouping.Key,
Value = subBookGrouping.Sum(t => t.CurrentUSD - t.PreviousUSD)
};

Categories