I am currently building reports and have a need to Group columns dynamically, depending on user's choice. Now, assuming that the situation is fixed on all columns, the query would be as follows:
var groupedInvoiceItems = invoiceItems.GroupBy(x => new { x.SalesInvoice.name, x.SalesInvoice.currencyISO, x.CatalogProduct });
Doing so would return results as desired, IGrouping. I would then run a loop to process the necessary data as below:
foreach (var groupedInvoiceItem in groupedInvoiceItems)
{
// Perform work here
}
Now, the headache comes in when I try to make the Grouping dynamic by using Dynamic Linq. The query is as follows:
var groupedInvoiceItems = invoiceItems.GroupBy("new (SalesInvoice.name, SalesInvoice.currencyISO, CatalogProduct)", "it");
The problem with this is that it does not return IGrouping anymore. Hence, my foreach loop no longer works. Is there any solution to the matter? I tried casting IGrouping to the Dynamic query but to no avail. Help is needed urgently.
The result of the GroupBy is an IEnumerable<IGrouping<DynamicClass,InvoiceItem>>, so you can proceed by something like:
foreach (IGrouping<DynamicClass,InvoiceItem> invoiceItemGroup in groupedInvoiceItems)
{
}
You should do the grouping then select the specified attributes to iterate throw them in the foreach loop. Try this out:
var groupedInvoiceItems = invoiceItems.GroupBy("SalesInvoice.name","it").GroupBy("SalesInvoice.currencyISO","it").GroupBy("CatalogProduct","it").Select("new (it.SalesInvoice.name,it.SalesInvoice.currencyISO,it.CatalogProduct)");
Hope this works.
Related
I'm having some troubles selecting a Tuple of Objects from my custom SQL query with ORMLite.
I have the following code:
var query = "select definition.*, timeslot.*, type.* from <blah blah>";
var defs = dbConnection.SqlList<Tuple<Definition, Timeslot, Type>>(query, new
{
/* query parameters */
});
The query itself is fine (I've tested it in SQL Management Studio).
The code above sets attributes only for the first item of the Tuple, leaving to the default state the others.
I've selected singularly each object and the result is correct (so no trouble during the conversion to POCO I guess).
Same thing goes if I use Select<Tuple<Definition, Timeslot, Type>> instead of SqlList.
I couldn't manage to try with MultiSelect since it appears to not take a string.
What is the correct way to select a Tuple in this manner?
I am working in C#.
Thanks in advance!
SelectMulti seems to be what you're looking for here.
From the documentation under the Selecting multiple columns across joined tables heading:
// Note: I'm making an assumption on your query here.
// Build the `q` object however it needs to be.
var q = db.From<Definition>()
.Join<Definition, Timeslot>()
.Join<Definition, Type>();
var results = db.SelectMulti<Definition, Timeslot, Type>(q);
foreach (var tuple in results)
{
var definition = tuple.Item1;
var timeslot = tuple.Item2;
var type = tuple.Item3;
}
I am trying to grab a single column from my linq query and put it into another list may not be the right way of doing it so please excuse me if my code is not
good. Wanting to learn more about linq.
List<StockM> _stockm = new List<StockM>();
List<priceLists> _priceList = new List<priceLists>();
stockm = _db.getStockBySupplierCode(_supplierCode);
foreach (var stockitem in _stockm)
{
_priceList = _db.getPriceTypesByProductCode(stockitem.product, "LPS");
stockitem.lpePrice = Convert.ToDecimal(_priceList.Select(s => s.lpsPrice));
}
I think the probelems lies in how I am attempting to select the column out here
_priceList.Select(s => s.lpsPrice)
try using FirstorDefault() as your _priceList is a list and you are assigning it to a value
_priceList.Select(s => s.lpsPrice).FirstOrDefault();
Also, do you need a where to find a particular value that matches? Just curious
There is several things wrong in your code.
First of all, I encourage you to read the .NET convention. There is no leading _ in local variable, the properties must start with an upper case, for examples.
Then, you do not need to instantiate a default instance if you are going to assign another instance to your variable next. You can assign it directly:
List<StockM> _stockm = _db.getStockBySupplierCode(_supplierCode);
Finally, the Select extension method you are using is already doing a foreach:
var result = _priceList.Select(s => s.lpsPrice);
Is basically (not exactly but it is a start to explain it like this) the same as:
var result = new List<decimal>();
foreach(var priceList in _priceList)
{
result.Add(priceList.lpsPrice);
}
That being said, you can improve your code like this:
List<StockM> stockm = _db.getStockBySupplierCode(_supplierCode);
foreach (var stockitem in stockm)
{
List<priceLists> _priceList = _db.getPriceTypesByProductCode(stockitem.product, "LPS");
stockitem.lpePrice = ??? // you cannot use the Select here as it returns a collection of item. What do you really want to achieve?
}
Consider the following search:
return SearchClient.Instance.UnifiedSearchFor(Request.Query)
.Filter(x => ((IContent)x).Ancestors().Match([ANCESTOR ID]))
.GetResult();
This works fine, as long as there is only one ANCESTOR ID to match with. There aren't, there are multiple - but I'm not sure of how many exactly.
How can I perform multiple filters on this result set?
What I've Tried
var query = SearchClient.Instance.UnifiedSearchFor(Request.Query);
[ANCESTOR IDS].ForEach(o => query.Filter(x => ((IContent)x).Ancestors().Match(o.ToString())));
return query.Skip(offset).GetResult();
This doesn't appear to work, the filter isn't applied. I assume that's because of the way the methods are chained!?
Any help massively appreciated. Bounty on the table for anyone who helps me crack it.
What Else I've Tried
var ancestorFilterBuilder = SearchClient.Instance.BuildFilter<MyPageType>();
foreach (var ancestorID in ancestorIDs)
{
ancestorFilterBuilder.Or(o => o.Ancestors().Match(ancestorID.ToString()));
}
.....
SearchClient.Instance.UnifiedSearchFor(Request.Query)
.Filter(ancestorFilterBuilder)......
This didn't successfully filter either.
I'd convinced myself I'd misunderstood the EpiServer API, when in fact my approach of dynamically building a filter was correct, aside from the reassigning of the additional Or calls to the filter itself (inspired by Ted's reminder to do so in the context of the other loop I was using):
var ancestorFilterBuilder = SearchClient.Instance.BuildFilter<MyPageType>();
foreach (var ancestorID in ancestorIDs)
{
ancestorFilterBuilder = ancestorFilterBuilder.Or(o => o.Ancestors().Match(ancestorID.ToString()));
}
I think you need to re-assign query in your ForEach loop.
Looks like you´re not using the return value, meaning your query object is unmodified by the loop.
[ANCESTOR IDS].ForEach(o => query = query.Filter(x => ((IContent)x).Ancestors().Match(o.ToString())));
I've got a collection of items (ADO.NET Entity Framework), and need to return a subset as search results based on a couple different criteria. Unfortunately, the criteria overlap in such a way that I can't just take the collection Where the criteria are met (or drop Where the criteria are not met), since this would leave out or duplicate valid items that should be returned.
I decided I would do each check individually, and combine the results. I considered using AddRange, but that would result in duplicates in the results list (and my understanding is it would enumerate the collection every time - am I correct/mistaken here?). I realized Union does not insert duplicates, and defers enumeration until necessary (again, is this understanding correct?).
The search is written as follows:
IEnumerable<MyClass> Results = Enumerable.Empty<MyClass>();
IEnumerable<MyClass> Potential = db.MyClasses.Where(x => x.Y); //Precondition
int parsed_key;
//For each searchable value
foreach(var selected in SelectedValues1)
{
IEnumerable<MyClass> matched = Potential.Where(x => x.Value1 == selected);
Results = Results.Union(matched); //This is where the problem is
}
//Ellipsed....
foreach(var selected in SelectedValuesN) //Happens to be integer
{
if(!int.TryParse(selected, out parsed_id))
continue;
IEnumerable<MyClass> matched = Potential.Where(x => x.ValueN == parsed_id);
Results = Results.Union(matched); //This is where the problem is
}
It seems, however, that Results = Results.Union(matched) is working more like Results = matched. I've stepped through with some test data and a test search. The search asks for results where the first field is -1, 0, 1, or 3. This should return 4 results (two 0s, a 1 and a 3). The first iteration of the loops works as expected, with Results still being empty. The second iteration also works as expected, with Results containing two items. After the third iteration, however, Results contains only one item.
Have I just misunderstood how .Union works, or is there something else going on here?
Because of deferred execution, by the time you eventually consume Results, it is the union of many Where queries all of which are based on the last value of selected.
So you have
Results = Potential.Where(selected)
.Union(Potential.Where(selected))
.Union(potential.Where(selected))...
and all the selected values are the same.
You need to create a var currentSelected = selected inside your loop and pass that to the query. That way each value of selected will be captured individually and you won't have this problem.
You can do this much more simply:
Reuslts = SelectedValues.SelectMany(s => Potential.Where(x => x.Value == s));
(this may return duplicates)
Or
Results = Potential.Where(x => SelectedValues.Contains(x.Value));
As pointed out by others, your LINQ expression is a closure. This means your variable selected is captured by the LINQ expression in each iteration of your foreach-loop. The same variable is used in each iteration of the foreach, so it will end up having whatever the last value was. To get around this, you will need to declare a local variable within the foreach-loop, like so:
//For each searchable value
foreach(var selected in SelectedValues1)
{
var localSelected = selected;
Results = Results.Union(Potential.Where(x => x.Value1 == localSelected));
}
It is much shorter to just use .Contains():
Results = Results.Union(Potential.Where(x => SelectedValues1.Contains(x.Value1)));
Since you need to query multiple SelectedValues collections, you could put them all inside their own collection and iterate over that as well, although you'd need some way of matching the correct field/property on your objects.
You could possibly do this by storing your lists of selected values in a Dictionary with the name of the field/property as the key. You would use Reflection to look up the correct field and perform your check. You could then shorten the code to the following:
// Store each of your searchable lists here
Dictionary<string, IEnumerable<MyClass>> DictionaryOfSelectedValues = ...;
Type t = typeof(MyType);
// For each list of searchable values
foreach(var selectedValues in DictionaryOfSelectedValues) // Returns KeyValuePair<TKey, TValue>
{
// Try to get a property for this key
PropertyInfo prop = t.GetProperty(selectedValues.Key);
IEnumerable<MyClass> localSelected = selectedValues.Value;
if( prop != null )
{
Results = Results.Union(Potential.Where(x =>
localSelected.Contains(prop.GetValue(x, null))));
}
else // If it's not a property, check if the entry is for a field
{
FieldInfo field = t.GetField(selectedValues.Key);
if( field != null )
{
Results = Results.Union(Potential.Where(x =>
localSelected.Contains(field.GetValue(x, null))));
}
}
}
No, your use of union is absoloutely correct.
The only thing to keep in mind is it excludes duplicates as based on the equality operator. Do you have sample data?
Okay, I think you are are haveing a problem because Union uses deferred execution.
What happens if you do,
var unionResults = Results.Union(matched).ToList();
Results = unionResults;
What's the best way to populate a collection with multiple data?
var query = (from p in obsDay where p.courseFaculty == "abellana" select p);
foreach (var item in query)
{
facultyTime.Add(
new TimePerDay()
{ subjTime =
new ObservableCollection<TimeSpan>
{ item.courseTimeStart }
}
);
}
availProf[randomNumber].actualTime = facultyTime;
I tried this one but it only adds one data on my collection? What am I doing wrong?
You should run the query from LinqPad to make sure that your data actually matches your expectations. Your foreach loop is fine, your query is only returning one result, which makes it a data issue.
actualTime gets set as the facultyTime. Which is constantly getting Added to? I'd guess it's either something in the Add method, or something in the actualTime property setter that isn't going through the whole facultyTime collection (if indeed it is a collection). Some clarity on what these objects are would be helpful in determining the issue.
Your query object probably only contains 1 item. try stepping through it with your debugger.