How to use lambda expression for below code block? - c#

I am writing a piece of code in order to get values from the server. There are multiple code lines and need to do the same thing using lambda expressions. This is the code I have tried and please help to rewrite this code using lambda expressions.
var newDocuments = result?.DocumentQueryResults.OrderBy(d => context.GetParams().GetAllDocumentIds().ToList().IndexOf(d.Document.Id)).ToList();
var test = new List<HealthRecord>();
foreach (DocumentQueryResult item in newDocuments)
{
test.Add(item.Document);
}

I believe you're looking for something like this:
// Cache the sort parameters, as suggested by Caius Jard:
var sortParams = context.GetParams()
.GetAllDocumentIds()
.AsEnumerable()
.Select((id, index) => new { id, index })
.ToDictionary(x => x.id, x => x.index);
var test = result?.DocumentQueryResults
.OrderBy(d => sortParams[d.Document.Id])
.Select(d => d.Document)
.ToList();

Related

how to send variable of type Func<T,decimal> to sum from IQueryable using ef core

I need to send a lamda in function parameter
but when i do that i've error say
Expression of type 'System.Func2[KokazGoodsTransfer.Models.Receipt,System.Decimal]' cannot be used for parameter of type 'System.Linq.Expressions.Expression1[System.Func2[KokazGoodsTransfer.Models.Receipt,System.Decimal]]' of method 'System.Decimal Sum[Receipt](System.Linq.IQueryable1[KokazGoodsTransfer.Models.Receipt], System.Linq.Expressions.Expression1[System.Func2[KokazGoodsTransfer.Models.Receipt,System.Decimal]])' (Parameter 'arg1')'
example of code
var sum = await context.Receipts.GroupBy(c => c.ClientId).Select(c => new { c.Key, Sum = c.Sum(c=>c.Amount) }).ToListAsync();
it's work fine
but when i try this i see the error
Func<Receipt, Decimal> func = c => c.Amount;
var sum = await context.Receipts.GroupBy(c => c.ClientId).Select(c => new { c.Key, Sum = c.Sum(func) }).ToListAsync();
thank you
EF usually requires an expression tree to be able to translate the code into actual SQL query.
You can try something like this (though not tested, but in some cases such tricks worked as far as I remember):
Expression<Func<Receipt, Decimal>> func = c => c.Amount;
var sum = await context.Receipts
.GroupBy(c => c.ClientId)
.Select(c => new { c.Key, Sum = c.AsQueryable().Sum(func) })
.ToListAsync();
Otherwise you maybe will need either to build select statement expression manually (which is not that easy) or look into 3rd party library like LINQKit which allows to use Func's with some magic. Something along this lines:
Expression<Func<Receipt, Decimal>> func = c => c.Amount;
var sum = await context.Receipts
.AsExpandable() // or WithExpressionExpanding on the context DI set up
.GroupBy(c => c.ClientId)
.Select(c => new { c.Key, Sum = c.Sum(func.Compile()) })
.ToListAsync();
You have to use not Func but Expression<Func<Receipt, Decimal>>. But it also will be not translatable without third-party extensions. I would suggest to use LINQKit. It needs just configuring DbContextOptions:
builder
.UseSqlServer(connectionString) // or any other provider
.WithExpressionExpanding(); // enabling LINQKit extension
Then your query will work in the following way:
Expression<Func<Receipt, Decimal>> func = c => c.Amount;
var sum = await context.Receipts.GroupBy(c => c.ClientId)
.Select(c => new { c.Key, Sum = c.Sum(x => func.Invoke(x)) })
.ToListAsync();

EF Dynamic Field Select using LINQ

I have the following select:
var sortedCodes = Codes
.Where(c => c.Active)
.OrderByDescending(x => x.SortOrder)
.Select(b => new { b.Display, b.NumericCode, b.SortOrder })
.Distinct()
.ToList();
The table Code has many columns such as NumericCode, TextCode, AlphaCode, ThreeCode, FourCode, FiveCode. I am using this table to build selects in my UI. Is there a way I can create some dynamic code so I can pass in the column name to use for the value?
The above select would look like this:
.Select(b => new { b.Display, "TextCode", b.SortOrder })
I was thinking I could use an Expression, but I do not think this is exactly what I need as it is actually printing the lambda as the value.
var arg = Expression.Parameter(typeof(Code), "b");
var body = Expression.Property(arg, valueColumn);
var lambda = Expression.Lambda<Func<Code, string>>(body, arg);
var sortedCodes = Codes
.Where(c => c.Active)
.OrderByDescending(x => x.SortOrder)
.Select(b => new { b.Display, Value=lambda, b.SortOrder })
.Distinct()
.ToList();

using substring and foreach in a lambda expression

Dictionary<int, string> names= GetNames().Where(x =>
x.Value.StartsWith("test") | x.Value.StartsWith(" " + "test")).ToDictionary(x => x.Key, y => y.Value);
The values from the getNames() method are something like this:
John Testing
Test Travis
Test Metsaoy
Using the above line of code I'm getting only the two last entries, but I want also the 1st one because the 2nd string starts with "test".
So, I need to modify the above where statement. I tried something like this:
.Where(x =>
foreach ( xx in x.Value.Split(' ') ) { if ( xx.StartsWith("text") ) true; })
How can I achieve this?
var res = GetNames().Where(kvp => kvp.Value.Split()
.Any(s => s.StartsWith("Test") || s.StartsWith("test")));
Optionally instead of StartsWith you can use String.Contains in the Any lambda.
Try:
var parser = new Regex(#"\bTest", RegexOptions.Compiled);
GetNames().Where(x => parser.IsMatch(x.Value)).ToDictionary(x => x.Key, y => y.Value)
Have you tried this?
x.Value.StartsWith("test") || x.Value.Contains(" test")
You'll have to use it in your query like this:
var names= GetNames()
.Where(x => x.Value.StartsWith("test") || x.Value.Contains(" test"))
.ToDictionary(x => x.Key, y => y.Value);
Hope it helps

Multiple GroupBy in 1 linq expression

I cannot seem to combine 2 GroupBy statements in 1 linq expression..
For now i'm doing something like this:
double maxInvestment = 0;
foreach (var playerAction in TotalUserActions.GroupBy(p => p.Player))
{
var MaxInvestmentPerPlayer = playerAction.GroupBy(p => p.RoundId)
.Select(p => p.LastOrDefault())
.Sum(p=> p.BetSize);
if(MaxInvestmentPerPlayer > maxInvestment)
maxInvestment = MaxInvestmentPerPlayer;
}
What I would like to do is something like this...
double maxInvestment = TotalUserActions.GroupBy(p => p.Player)
.GroupBy(p => p.RoundId)
.Select(p => p.LastOrDefault())
.Sum(p=> p.BetSize);
But that wont work.. Can someone help me on this?
Thanks!
Looks like this is what you want, the key takeaway being the inner query is wrapped in an outer call to Select():
var maxInvestment = TotalUserActions.GroupBy(p => p.Player)
.Select(g => g.GroupBy(x => x.RoundId)
.Select(x => x.LastOrDefault())
.Sum(x => x.BetSize))
.Max();
I do question your use of LastOrDefault() though as, since you have not specified any ordering, you may as well use FirstOrDefault() and save the hassle of skipping to the last element.

how to do a contains on an array of strings?

I am using a predicate builder class and I need to invoke the contains method on an array of strings so in the code below instead of radio I would be passing in an array of strings:
wherePredicate = wherePredicate.Or(m => m.MediaType.Contains("Radio"));
the full code section:
if (param.iMediaGroupID > 0)
{
var wherePredicate = PredicateBuilder.False<MediaChannelModel>();
var ss = new NeptuneRepository<Lookup_MediaTypes>();
var mediagroups = ss.FindWhere(m => m.MediaGroupID == param.iMediaGroupID).Select(m => m.Name);
//problem area
wherePredicate = wherePredicate.Or(m => mediagroups.Contains(m.MediaType));
predicate = predicate.And(wherePredicate);
}
mediaGroups is: ["Radio","Tv","Magazine"]
If m.MediaType is any of these values then the predicate is true.
Is there a way to do this in C#?
I suspect you want something like:
wherePredicate = wherePredicate.Or(m => array.Contains(m.MediaType));
Or perhaps:
wherePredicate = wherePredicate.Or(m => array.Any(x => m.MediaType.Contains(x)));
If neither of those are what you're after, please clarify your requirements.
EDIT: The problem you're now facing is that you're not actually asking whether an array contains the value. You're asking whether a query contains a value. If you change it to an actual array, you may well find it works:
var mediagroups = ss.FindWhere(m => m.MediaGroupID == param.iMediaGroupID)
.Select(m => m.Name)
.ToArray();
However, if these are querying the same database, you'd be better off trying to do this in some kind of join.
Jon Skeet's answer worked perfectly for me. I had been struggling to make the .Contains search for a substring in a string array against the database, rather than try to find a substring in a single C# string object. Thank you!
Here's the modified code that worked for me:
var predicate = PredicateBuilder.False<ClientXMemberDetail>();
predicate = predicate.Or(x => strArrselectedCustomMemberNumbers.Any<string>(y => x.MemberID.Contains(y)));
CustomSearchMembersAlreadyMatched = ClientXContext.ClientXMemberDetails
.AsExpandable()
.Where(predicate)
.ToList()
.Select(r => r.MemberID.ToString()).ToList();
(ClientXContext above is an instance of the ObjectContext class, strArrselectedCustomMemberNumbers is a string array, ClientXMemberDetails is ObjectSet, where ClientXMemberDetail is the EntityObject)
Edit: Anonymized my client's name

Categories