Return more than one result - c#

I have a bug that I need more than one result from a foreach without creating a new collection in the method. I need to get rid of the foreach however I don`t know what LINQ method to use.
I have tried,
return basket.Items.SelectMany(
item => item.Id == orderComplimentaryUtilities.Where(o => o.Id));
public static IEnumerable<OrderItem> WhichUtilitiesAreAlreadyInBasket(
this
IEnumerable<OrderComplimentaryUtility.OrderComplimentaryUtility>
orderComplimentaryUtilities,
Order basket)
{
if (basket == null || orderComplimentaryUtilities == null)
{
return Enumerable.Empty<OrderItem>();
}
foreach (var orderComplimentaryUtility in orderComplimentaryUtilities)
{
return basket.Items.Where(item => item.Id == orderComplimentaryUtility.Id);
}
return Enumerable.Empty<OrderItem>();
}

It appears that you are looking to join the data from two sequences (orderComplimentaryUtilities and basket), and return the data from basket where they match by id.
You can accomplish this with a LINQ join:
public static IEnumerable<OrderItem> WhichUtilitiesAreAlreadyInBasket(
this IEnumerable<OrderComplimentaryUtility.OrderComplimentaryUtility> orderComplimentaryUtilities,
Order basket)
{
if (basket == null || orderComplimentaryUtilities == null)
{
return Enumerable.Empty<OrderItem>();
}
var items = orderComplimentaryUtilities
.Join(basket,
u => u.ID,
b => b.ID,
(u, b) => b);
return items;
}

You can use Contains if you separate out the ids into a collection:
var ids = orderComplimentaryUtility.Select(i => i.id).ToArray();
return basket.Items.Where(item => ids.Contains(item.Id));
If this is all in-memory, you could inline the Select into the Where clause, but that may not work if you're querying a SQL data source that cannot convert the Select into an IN clause.

Related

Which design pattern should I use to handle multiple if conditions to get search result base on inputs

Can anyone suggest to me which design pattern should I use to handle multiple if conditions for a search function.
The search function takes the product's name, type, and location. In my handler, I handle the input by using if conditions as the example below.
if (!string.isNullOrEmpty(ProductName) && !string.isNullOrEmpty(ProductType))
{
// Query product and return base on name and type.
var product = database.product
.Where(x => x.productname == productname)
.Where(x => x.producttype == producttype)
.ToList()
}
else if (!string.isNullOrEmpty(ProductName)
&& !string.isNullOrEmpty(ProductType)
&& !string.isNullOrEmpty(ProductLocation))
{
// Query product and return base on name and location.
var product = database.product
.Where(x => x.productname == productname)
.Where(x => x.ProductLocation == ProductLocation)
.ToList()
}
So, I ended up having multiples if conditions in my handler. Code starts to get bigger and bigger. In the future, when I may have new types of input. Especially, each if condition will have the same query function and only where the condition is added or removed base on inputs.
Is there a better way to handle inputs and remove duplicated query function?
It is not design pattern but common way when using LINQ
var query = database.product.AsQueryable();
if (!string.IsNullOrEmpty(productName))
query = database.product.Where(x => x.productname == productname);
if (!string.IsNullOrEmpty(productType))
query = database.product.Where(x => x.producttype == producttype);
var product = query.ToList();
Or via helper function:
public static class MyQueryableExtensions
{
public staic IQueryble<T> WhereIf<T>(this IQueryable<T> source, bool condiion, Expression<Func<T, bool> predicate)
{
if (condition)
source = source.Where(predicate);
return source;
}
}
var product = database.product
.WhereIf(!string.IsNullOrEmpty(productName), x => x.productname == productname)
.WhereIf(!string.IsNullOrEmpty(productType), x => x.producttype == producttype)
.ToList();

Turn SQL into Lambda/Linq

I've been trying to turn a fairly basic piece of SQL code into Lamda or Linq but I'm getting nowhere. Here is the SQL query:
SELECT * FROM Form a
INNER JOIN FormItem b ON a.FormId = b.FormId
INNER JOIN FormFee c ON a.FormId = c.FormId
INNER JOIN FeeType d ON c.FeeTypeId = d.FeeTypeId
WHERE b.StatusId = 7
I tried this but it isn't doing what I want.
public Form GetFormWithNoTracking(int id)
{
return ObjectSet
.Where(x => x.FormId == id &&
(x.FormItem.Any(di => di.StatusId == (short)Status.Paid)))
.AsNoTracking()
.FirstOrDefault();
}
I'm trying to return only the rows from FormItem whose StatusId is Paid. However, the above returns all. I know that .Any() will check if there are any matches and if there are return all, so in this case my data, for this form, does have items who have a StatusId of Paid and some items whose StatusId is not paid so it brings back them all.
var query = (from a in ObjectSet.FormA
join b in ObjectSet.FormB on a.field equals b.field
where b.StatusId = 7
select new { a, b})
You can join rest with same logic.
This should be what you are asking for:
Get the Form with FormId = id
Of that form, return all FormItems that have StatusId = Paid
public IEnumerable<FormItem> GetFormWithNoTracking(int id)
{
return ObjectSet
.SingleOrDefault(x => x.FormId == id)
.Select(f => f.FormItem
.Where(di => di.StatusId == (short)Status.Paid))
.AsNoTracking();
}
If you need the Form itself too, you might want to create a custom type (edit: see #Burk's answer) or return a Tuple<Form,IEnumerable<FormItem>>, a IEnumerable<Tuple<Form,FormItem>> or whatever suits your needs best instead.
Alternatively you could remove all non-paid items of the form.
public Form GetFormWithNoTracking(int id)
{
var form = ObjectSet
.SingleOrDefault(x => x.FormId == id)
.AsNoTracking();
var nonPaid = form.Select(f => f.FormItem
.Where(di => di.StatusId != (short)Status.Paid)).ToList();
foreach(FormItem item in nonPaid)
form.FormItem.Remove(item);
return form;
}

Ability to conditionally choose what row to select

This is mostly to see if I can find a way to work around this limitation.
Let's say I have the following query:
var query = (from a in db.Table
where a.CustomFieldId == FieldID && a.ListingId == listingID
select a.SomeTypeValue);
The table I am querying is set for custom fields that may vary in type, so it has several columns but only uses the appropriate column to store the value based on the field's selected type.
The table looks somewhat like this:
I want to be able to choose which column I select without rewriting the whole query. Is this possible?
Thanks in advance,
Your query can be rewrited to use "Method Call LINQ":
db.Table
.Where(a => a.CustomFieldId == FieldID && a.ListingId == listingID)
.Select(x => x.SomeType);
You may split query into Where and Select parts then:
var result = whereQuery.Select(x => x.BoolValue);
or
var result = whereQuery.Select(x => x.IntValue);
You may even encapsulate that logic into method:
IEnumerable<T> GetValues<T>() {
var query = db.Table
.Where(a => a.CustomFieldId == FieldID && a.ListingId == listingID);
if (typeof(T)==typeof(bool)) {
return query.Select(x => x.BoolColumn);
}
else if (typeof(T) == typeof(int)) {
return query.Select(x => x.IntColumn);
}
// other types here
}

Group by, Count and Lambda Expression

I am trying to translate the following query:
SELECT STATE, COUNT(*)
FROM MYTABLE
GROUP BY STATE;
Into a lambda expression. I am using C# and EntityFramework, however it doesnt seem I can make it work. Here is what I have on my respository so far:
public IEnumerable<object> PorcentajeState(Guid id)
{
return _context.Sates.Where(a => a.Id == id)
.GroupBy(a => a.State)
.Select(n => new { n.StateId , n.Count() });
}
Of course it doesnt compile and I am lost after googling for 2 hours . Could you please help me?
thanks in advance
There are two issues here:
The result of GroupBy will will be an enumerable of type IEnumerable<IGrouping<TKey, TSource>>. The IGrouping interface only has one property you can access, Key which is the key you specified in the GroupBy expression, and implements IEnumerable<T> so you can do other Linq operations on the result.
You need to specify a property name for the anonymous type if it cannot be inferred from a property or field expression. In this case, you're calling Count on the IGrouping, so you need to specify a name for that property.
Try this:
public IEnumerable<object> PorcentajeState(Guid id)
{
return _context.Sates.Where(a => a.Id == id)
.GroupBy(a => a.StateId)
.Select(g => new { g.Key, Count = g.Count() });
}
The equivalent in query syntax would be
public IEnumerable<object> PorcentajeState(Guid id)
{
return from a in _context.Sates
where a.Id == id
group a by a.StateId into g
select new { a.Key, Count = g.Count() };
}
In either case, if you want the first property to be named StateId instead of Key, just change that to
new { StateId = g.Key, Count = g.Count() }
This one is good
public IEnumerable<object> PorcentajeState(Guid id)
{
return _context.Sates.Where(a => a.Id == id)
.GroupBy(a => a.StateId)
.Select(g => new { g.Key, Count = g.Count() });
}
But try this.
public IEnumerable<object> PorcentajeState(Guid id)
{
return _context.Sates.Where(a => a.Id == id)
.GroupBy(a => a.StateId)
.Select(g => new { g.Key.StateId, Count = g.Count() });
}

Putting multiple values in a WHERE clause in Azure Mobile Services

I'm trying to figure out how to put multiple values into a WHERE clause; this is the kind of thing you'd use a IN clause for in SQL.
My current code:
if (Log.Count() == 1)
{
items = itemTable
.Where(Item => Item.id == Log[0].i_id)
.ToCollectionView();
}
else if (Log.Count() == 2)
{
items = itemTable
.Where(Item => Item.id == Log[0].i_id || Item.id == Log[1].i_id)
.ToCollectionView();
}
else if (Log.Count() == 3)
{
items = itemTable
.Where(Item => Item.id == Log[0].i_id || Item.id == Log[1].i_id || Item.id == Log[2].i_id)
.ToCollectionView();
}
It's pretty nasty. I can't find a way to put multiple values into that WHERE clause without a big if statement. Any ideas?
Try this instead:
items = itemTable
.Where(Item => Log.Any(logItem => logItem.i_id == Item.id))
.ToCollectionView();
Edit: (in response to the "not supported exception")
You can try this alternative to see if the backend supports it:
var ids = Log.Select(logItem => logItem.i_id).ToList();
var items = itemTable
.Where(Item => ids.Contains(Item.id))
.ToCollectionView();
items = itemTable.Where(Item => Log.Any(i => i.i_id == Item.id)).ToCollectionView();
I faced the same issue. Using Any or Contains as others have suggested gave me a Not Supported Exception. I posted on the Azure Mobile Services forum and was told that IN is not supported yet. They advised I use an extension method which worked perfectly for me. Below is the code I used.
public static class Extensions
{
public async static Task<List<T>> In<T>(this IMobileServiceTable<T> table, List<int> ids)
{
var query = new StringBuilder("$filter=(");
for (int i = 0; i < ids.Count; i++)
{
query.AppendFormat("id eq {0}", ids[i]); //don't forget to url escape and 'quote' strings
if (i < ids.Count - 1)
{
query.Append(" or ");
}
}
query.Append(")");
var list = await table.ReadAsync(query.ToString());
var items = list.Select(i => MobileServiceTableSerializer.Deserialize<T>(i)).ToList();
return items;
}
}
Your list can be populated by calling the extension method like this:
FavoriteJokesList = await jokeItemTable.In(favoriteJokeIds);
Source: http://social.msdn.microsoft.com/Forums/en-US/azuremobile/thread/b573ff6c-1f6b-4846-b44d-4678e3d26f66
Just for the record, in the newest Version of the Azure Mobile Service SDK (1.0), this works:
var ids = Log.Select(logItem => logItem.i_id).ToList();
var items = await itemTable
.Where(Item => ids.Contains(Item.id))
.ToCollectionAsync();

Categories