Syntax to execute code block inside Linq query? - c#

Here's some code that (obviously) doesn't compile:
var q = from x in myAnonymousTypeCollection
select new {
x.ID,
CalcField = {
switch(x.SomeField) {
case 1:
return Math.Sqrt(x.Field1);
case 2:
return Math.Pow(x.Field2, 2);
default:
return x.Field3;
}
}
};
You get the picture; I'm trying to calculate CalcField in a completely different way, depending on what the value of SomeField is. I can't use a Func<> (or can I?), because the input type is anonymous. So what's the right syntax to get this to work?

First off, I usually prefer the method chain syntax over the query syntax for Linq. With that you can do this easily.
var q = myAnonymousTypeCollection
.Select(x =>
{
object calcField;
switch(x.SomeField)
{
case 1:
calcField = Math.Sqrt(x.Field1);
case 2:
calcField = Math.Pow(x.Field2, 2);
default:
calcField = x.Field3;
return new
{
x.ID,
CalcField = calcField
};
});
Without using method chains, you need either a method or an Func. Let's assume a Func
//replace these with actual types if you can.
Func<dynamic, dynamic> calculateField =
x =>
{
switch(x.SomeField) {
case 1:
return Math.Sqrt(x.Field1);
case 2:
return Math.Pow(x.Field2, 2);
default:
return x.Field3;
}
var q = from x in myAnonymousTypeCollection
select new { x.Id, CalcField = calculateField(x) };
Note: I didn't write this in an IDE, so please excuse any simple errors.
Here is the MSDN for dynamic. However, I have found that once you need to start passing anonymous types around, it is best to make an actual class.

You could wrap your anonymous function as a (self-executing) Func<> delegate. This assumes you know the return type.
var q = from x in myAnonymousTypeCollection
select new {
ID = x.ID,
CalcField = new Func<double>( () => {
switch(x.SomeField) {
case 1:
return Math.Sqrt(x.Field1);
case 2:
return Math.Pow(x.Field2, 2);
default:
return x.Field3;
}
} )()
};

You could quite easily move the switch logic out into another function like so:
private static T GetReturnValue<T>(myClass x)
{
switch (x)
{
case 1:
return Math.Sqrt(x.Field1);
break;
case 2:
return Math.Pow(x.Field2,
2);
break;
default:
return x.Field3;
break;
}
}
And then you just need to pass your object to that function to get back the value you want:
var q = from x in myAnonymousTypeCollection
select new
{
ID = x.ID,
CalcField = GetReturnValue(x)
};

Related

C# how to define an anonymous type with IQueryable?

Depending on a switch/case a variable is set to an EF chaining.
but I cannot figure how to define the variable groupByLeadIdQuery
IQueryable groupByLeadIdQuery = null;
switch (...)
{
case ...:
{
groupByLeadIdQuery = context.HistoryRecords.GroupBy(h => h.ProductId)
.Select(g => new
{
Id = g.Key,
Retributions = g.Sum(s => s.Retribution),
});
}
break;
case ...:
{
groupByLeadIdQuery = null;
}
...
}
later in the code, I am processing the results depending on the variable is null or not
if (groupByLeadIdQuery2 != null)
{
var list = groupByLeadIdQuery2.ToList(); <<<<<<<<<<<<<<<<<<
...
}
the compiler complains: IQueryable does not contain a definition of ToList
I tried
IQueryable<T> groupByLeadIdQuery = null;
IQueryable<Anonymous> groupByLeadIdQuery = null;
IQueryable<'a> groupByLeadIdQuery = null;
var groupByLeadIdQuery = null;
nothing works.
can you enlight me on this?
thanks for your help
from King King
groupByLeadIdQuery2.Cast<dynamic>().ToList()
works like a charm
For having ToList method you should have define your IQueryable variable as List. For example IQueryable<Product> or something Like this. If there is not a certain Type you can use just var and get your anonymous type.
In your case I think easiest solution will be setting a default value and than updating by cases. But in this case you have to set same anonymous type for each cases, otherwise you have to cast types.
var groupByLeadIdQuery = context.HistoryRecords.GroupBy(h => h.ProductId)
.Select(g => new
{
Id = g.Key,
Retributions = g.Sum(s => s.Retribution),
});
switch (...)
{
case ...:
{
groupByLeadIdQuery = ...;
}
break;
case ...:
{
groupByLeadIdQuery = null;
}
...
}
If this is not solving your problem you should use interfaces, an interface which contains the properties you need, or dynamics maybe.
Switch expressions may be a good option here. The fundamental problem is that you need the <T> in IQueryable<T>, but it is "unpronounceable", so you can only use var to declare such, but:
var query = whatever switch {
SomeCase => your.Query.Here...,
_ => null,
};
Note that while you can have as many case lines as you need, they must all have the same shape for this to work.
You can make generic Group By method some thing like this:
public async Task<List<T>> GetGroupByResult<T>(string groupByItem) where T : class
{
var groupByLeadIdQuery = _dbContext.Set<T>();
switch (groupByItem)
{
case "ProductId":
{
groupByLeadIdQuery = groupByLeadIdQuery.GroupBy(h => h.ProductId)
.Select(g => new
{
Id = g.Key,
Retributions = g.Sum(s => s.Retribution),
});
break;
}
case "Other":
{
groupByLeadIdQuery = ...;
break;
}
default:
{
groupByLeadIdQuery = null;
}
}
if (groupByLeadIdQuery != null)
return await groupByLeadIdQuery.ToListAsync();
return default;
}
It can use for any of your entities.

Is there a way I can replace a switch statement with actions by using a dictionary and some other code?

Here is what my app has:
private void saveInDatabase(string key, string value)
switch (key)
{
case "a":
def = Helpers.Utils.listOfDoubleFromString(value);
break;
case "b":
chi = int.Parse(value);
break;
....
}
Is there a way that I could use a dictionary like this to decide what actions happen with the different case values?
{"a", () => def = Helpers.Utils.listOfDoubleFromString(value)},
{"b", () => chi = int.Parse(value)},
My app has a large number of these case statements so I am interested to know if I could replace them by setting up a dictionary and then some more code.
I mean you pretty much got it:
string def;
int chi;
var map = new Dictionary<string, Action<string>>
{
["a"] = value => def = Helpers.Utils.listOfDoubleFromString(value),
["b"] = value => chi = int.Parse(value),
};
map[key](value);

How to initialize var when it is used for different things in a method

I have a method in my controller that is performing some logic based on data submitted and my varitem should be assigned to different results?
For example my controller's method is something like this:
public ActionResult Index(SearchCriteria criteria)
{
var db = new EbuyDataEntities();
//criteria.SearchKeyword = "movie";
if (!string.IsNullOrEmpty(criteria.SearchKeyword))
{
var auctionData = db.Auctions.First(q => q.Description.Contains(criteria.SearchKeyword));
}
else
{
var auctionData = db.Auctions.OrderBy(item => item.EndTime);
}
switch (criteria.GetSortByField())
{
case SearchCriteria.SearchFieldType.Price:
auctionData = auctionData.OrderBy(q => q.CurrentPrice.Value);
break;
case SearchCriteria.SearchFieldType.RemainingTime:
auctionData = auctionData.OrderBy(q => q.EndTime);
break;
case SearchCriteria.SearchFieldType.Keyword:
default:
auctionData = auctionData.OrderBy(q => q.Title);
break;
}
auctionData = SomeMethod();
var viewModel = new SearchViewModel();
return View("Search",viewModel);
}
What is the right way to do something like this.
Well, two options:
Move the declaration to before the if statement, and give it an explicit type:
IQueryable<Auction> auctionData;
if (...)
{
...
}
else
{
...
}
Change the structure to only have a single declaration, e.g. using the conditional operator:
var auctionData = !string.IsNullOrEmpty(criteria.SearchKeyword)
? db.Auctions.Where(q => q.Description.Contains(criteria.SearchKeyword))
: db.Auctions.OrderBy(item => item.EndTime);
Note that I've changed First to Where here - otherwise you would only be matching a single entry, which doesn't sound like much of a search to me, and would make of the rest of the method very odd.
That in itself suggests a third option:
var auctionData = db.Auctions.OrderBy(item => item.EndTime);
var keyword = criteria.SearchKeyword;
if (!string.IsNullOrEmpty(keyword))
{
auctionData = auctionData.Where((q => q.Description.Contains(keyword));
}

Switch variable to use externally

The goal
Declare some variable within a switch and use it externally.
The problem
I don't know the syntax.
What I'm thought about
To perform my problem, I was thinking to do something like this, but doesn't work because it is just a philosophy:
public ActionResult Compare(int id, string orderBy)
{
var productsList = Products.BuildIndividualProductComparisonList(id);
var product = Products.BuildToCompare(id);
switch (orderBy)
{
case "lowestToBiggest":
var organizedProductsList =
productsList.OrderBy(x => x.minProductPrice);
break;
case "biggestToLowest":
var organizedProductsList =
productsList.OrderBy(x => x.maxProductPrice);
break;
default:
var organizedProductsList =
productsList.OrderBy(x => x.minProductPrice);
break;
}
ComparisonViewModel comparisonViewModel =
new ComparisonViewModel
{
Product = product,
ProductList = organizedProductsList
};
return View(comparisonViewModel);
}
Spotlight
This is my original C#'s code that works good:
public ActionResult Compare(int id, string orderBy)
{
var productsList = Products.BuildIndividualProductComparisonList(id);
var product = Products.BuildToCompare(id);
ComparisonViewModel comparisonViewModel =
new ComparisonViewModel
{
Product = product,
ProductList = productsList
};
return View(comparisonViewModel);
}
The question
Someone has any idea to resolve my problem?
Declare some variable within a switch and use it externally.
You can't. Variables defined inside the scope would only be visible within that scope.
You have to declare your variable outside the switch statement and then you will be able to use it outside.
I see that you are using var (Implicitly typed variable) and you can't declare it outside of your switch statement, (since that needs to be assigned), You should see: Declaring an implicitly typed variable inside conditional scope and using it outside and the answer from Eric Lippert
Declare variable outside switch block and assign value to it. Also try not to use List suffix for collection names:
IEnumerable<Product> sortedProducts;
switch (orderBy)
{
case "lowestToBiggest":
sortedProducts = products.OrderBy(x => x.minProductPrice);
break;
case "biggestToLowest":
sortedProducts = products.OrderBy(x => x.maxProductPrice);
break;
default:
sortedProducts = products.OrderBy(x => x.minProductPrice);
break;
}
Actually your code can be simplified to:
IEnumerable<Product> sortedProducts =
products.OrderBy(p => orderBy == "biggestToLowest" ?
p.maxProductPrice :
p.minProductPrice);
Just put the declaration of the variable outside of the switch block.
You have to specify the type of the variable at declaration, though, instead of using var because the type cannot be inferred.
public ActionResult Compare(int id, string orderBy)
{
var productsList = Products.BuildIndividualProductComparisonList(id);
var product = Products.BuildToCompare(id);
var organizedProductsList = null;
switch (orderBy)
{
case "lowestToBiggest":
organizedProductsList =
productsList.OrderBy(x => x.minProductPrice);
break;
case "biggestToLowest":
organizedProductsList =
productsList.OrderBy(x => x.maxProductPrice);
break;
default:
organizedProductsList =
productsList.OrderBy(x => x.minProductPrice);
break;
}
ComparisonViewModel comparisonViewModel =
new ComparisonViewModel
{
Product = product,
ProductList = organizedProductsList
};
return View(comparisonViewModel);
}
You cannot refer to a variable outside its enclosing scope.
Either:
Move the variable to a wider scope. The downside of this is that we always strive to keep scope as narrow as possible.
Extract the switch statement into a function and have it return the value.
Option 1 is, I think, obvious. To expand on option 2, the code would look like this:
ProductList organizedProductsList(...)
{
return productsList.OrderBy(...);
}
....
ComparisonViewModel comparisonViewModel =
new ComparisonViewModel
{
Product = product,
ProductList = organizedProductsList(...)
};
You will need declare the variable organizedProductsList outside your case statement, as it will only have scope with the case statement.
Note: I can't tell what Products.BuildIndividualProductComparisonList(id) returns, I presume its a List of Products.
Thus something like:
IEnumerable<Products> organizedProductsList = null;
switch (orderBy)
{
case "lowestToBiggest":
organizedProductsList =
productsList.OrderBy(x => x.minProductPrice);
break;
case "biggestToLowest":
organizedProductsList =
productsList.OrderBy(x => x.maxProductPrice);
break;
default:
organizedProductsList =
productsList.OrderBy(x => x.minProductPrice);
break;
}

Elegant way to save/load values into an array

I'm basically saving and loading values into an array to feed into a JSON library, and I'm just looking for a more elegant way to do this:
class properties to array
return new object[] { path, pathDir, name };
array to class properties
c.path = values[0];
c.pathDir = values[1];
c.name = values[2];
Simple solutions that ideally do not require additional run time overheads such as Reflection are appreciated.
Can I do something like this?
c.{path, pathDir, name} = values[0...2]
Edit: I'm specifically asking for arrays. I know about serialization and JSON and Protobuf and everything else everyone is suggesting.
Would this not do the trick?
return new {path= "/Some/Path", pathDir= "SiteRoot", name="MyPath"}
Edit:
//Mock function to simulate creating 5 objects with 'CreateArrayOb' function
public void CreatingObjects()
{
var lst = new List<object>();
for (var x = 0; x < 5; x++)
{
lst.Add(CreateArrayOb(new string[] {"Path" + x, "Dir" + x, "Path" + x}));
}
}
public object CreateArrayOb(object[] vals)
{
if (vals != null && vals.Any())
{
//Switch cases in the event that you would like to alter the object type returned
//based on the number of parameters sent
switch (vals.Count())
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
return new { path = vals.ElementAt(0), pathDir = vals.ElementAt(1), name = vals.ElementAt(2) };
}
}
return null;
}

Categories