Switch variable to use externally - c#

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;
}

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.

How to set List<T> var to null

Because I need to finally display on the screen, the field is the filtered field.
I don't want to use new ListLSD, new ListMPS, etc.
I need to share variables new List.
[Serializable]
public class DiseasesDataInfo
{
public String FAB { set; get; }
public String GAA { set; get; }
/* ... */
}
var newList = null; // Can't be set to null?
switch (ddlDiseases.SelectedValue)
{
case DiseasesCollections.LSD:
newList = ImportExcleInfoList.Select(x => new
{
x.GAA, x.GAAratio, x.GAAInhibition, x.GAAStatus,
x.FAB, x.FABratio, x.FABStatus,
x.GD, x.GDratio
}).ToList();
gvAdvanced.DataSource = newList;
break;
// etc.
}
gvAdvanced.DataBind();
It's a bad idea to declare a variable outside the scope it's used, or use the same variable for different jobs. In this case the variable's value is not used outside a single case block at a time. The variable should be declared and assigned at the same time in each case block, eg :
switch (ddlDiseases.SelectedValue)
{
case DiseasesCollections.LSD:
var lsdList = ImportExcleInfoList.Select(x => new
{
x.GAA, x.GAAratio, x.GAAInhibition, x.GAAStatus,
x.FAB, x.FABratio, x.FABStatus,
x.GD, x.GDratio
}).ToList();
gvAdvanced.DataSource = lsdList;
break;
case DiseasesCollections.MPS:
var mspList = ImportExcleInfoList.Select(x => new
{
x.MPS2, x.MPS2ratio, x.MPS2Status,
x.MPS3B, x.MPS3Bratio, x.MPS3BStatus
}).ToList();
gvAdvanced.DataSource = mpsList;
break;
}
gvAdvanced.DataBind();
The list could be assigned to gvAdvanced.DataSource directly too :
case DiseasesCollections.LSD:
gvAdvanced.DataSource = ImportExcleInfoList.Select(x => new
{
x.GAA, x.GAAratio, x.GAAInhibition, x.GAAStatus,
x.FAB, x.FABratio, x.FABStatus,
x.GD, x.GDratio
}).ToList();
break;
You cannot use var for null. You have to specify the type as the compiler cannot infer it. As you are creating anonymous types in your Linq query, you would probably have to use something like:
IList newList = null;
switch (ddlDiseases.SelectedValue)
{
case DiseasesCollections.LSD:
newList = ImportExcleInfoList.Select(x => new
{
x.GAA, x.GAAratio, x.GAAInhibition, x.GAAStatus,
x.FAB, x.FABratio, x.FABStatus,
x.GD, x.GDratio
}).ToList();
gvAdvanced.DataSource = newList;
break;
// etc.
}
It seems that you could also create instances of the class you specified:
IList<DiseasesDataInfo> newList = null;
// [...]
newList = ImportExcleInfoList.Select(x => new DiseasesDataInfo { GAA = x.GAA /* ... */}).ToList();
gvAdvanced.DataSource = newList;
If you don't need newList anywhere else outside the switch block, you could also just assign the result directly:
gvAdvanced.DataSource = ImportExcleInfoList.Select(x => new DiseasesDataInfo { GAA = x.GAA /* ... */}).ToList();
While I would not suggest to do it because I prefer to use models instead of anonymous types, but you can achieve it by doing something like this:
object newList = null; // Use object. Grid datasource accepts object
switch (ddlDiseases.SelectedValue)
{
case DiseasesCollections.LSD:
newList = ImportExcleInfoList.Select(x => new
{
x.GAA, x.GAAratio, x.GAAInhibition, x.GAAStatus,
x.FAB, x.FABratio, x.FABStatus,
x.GD, x.GDratio
}).ToList();
break;
case DiseasesCollections.MPS:
newList = ImportExcleInfoList.Select(x => new
{
x.MPS2, x.MPS2ratio, x.MPS2Status,
x.MPS3B, x.MPS3Bratio, x.MPS3BStatus
}).ToList();
break;
}
//****** case DiseasesCollections.Other...
gvAdvanced.DataSource = newList; //Set new list here instead on every case.
gvAdvanced.DataBind();
You can set a var to null because C# is strongly typed. var was introduced to allow us to assign anonymous types to a reference although we can use var with other data types aslong as the compiler can resolve the type that var should be. This is why we cannot do this:
var v;
Because the compiler does not know what var will be, in the following case the compiler will decide that var is actually a string:
var s = "";
In your case, setting var as null is something the compiler can't resolve as it could be anything that takes a null value (I.e. Any reference type), the compiler doesn't know that you want a list, instead you should just use an empty list:
var myList = new List<T>();
myList = null;
Or better, just declare it:
List<T> myList = null;
For reference, var exists to be used like, the following is an anonymous type which we must use var to use assign and reference to:
var anon = new { name = "", description = "" };
You can use dynamic type which will allow you to determine the type of newList at runtime:
dynamic newList = null;
...
newList = ImportExcleInfoList.Select(x => new
{
x.GAA, x.GAAratio, x.GAAInhibition, x.GAAStatus,
x.FAB, x.FABratio, x.FABStatus,
x.GD, x.GDratio
}).ToList();
...
gvAdvanced.DataSource = newList;
It basically tells the compiler that newList type unknown and might change overtime.
Change your list initializtion to the following one:
List<DiseasesDataInfo> newList = null;
This should work. The problem is that you are using the var keyword.

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));
}

Syntax to execute code block inside Linq query?

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)
};

Total results of paged projected nhibernate query with aggregates

I'm dynamically building a nhibernate projected query that needs to implement paging. Something like...
var projections = Projections.ProjectionList();
foreach (var p in projection.Projections)
{
IProjection newProjection = null;
switch (p.AggregateFunc)
{
case AggregateFuncTypeEnum.GroupProperty:
newProjection = Projections.GroupProperty(p.Path);
break;
case AggregateFuncTypeEnum.Sum:
newProjection = Projections.Sum(p.Path);
break;
default:
newProjection = Projections.Property(p.Path);
break;
}
projections.Add(newProjection, p.Name);
}
criteria.SetProjection(projections).SetResultTransformer(new AliasToBeanResultTransformer(projectionType));
I can get the first 15 results like so
criteria.SetFirstResult(0);
criteria.SetMaxResults(15);
var results = criteria.List();
But I also need to send another query to get the total number of records but so far I've failed to figure this out. The projection still needs to be applied i.e. if the results are grouped by 'code' with a sum of 'cost' then 100 records might return 20 rows, and it's the 20 I'm interested in.
How do I get the total number of records that will be returned? Thanks
maybe this:
var rowcount = CriteriaTransformer.Clone(criteria);
var goupprojections = Projections.ProjectionList();
var projections = Projections.ProjectionList();
foreach (var p in projection.Projections)
{
IProjection newProjection = null;
switch (p.AggregateFunc)
{
case AggregateFuncTypeEnum.GroupProperty:
newProjection = Projections.GroupProperty(p.Path);
goupprojections.Add(Projections.GroupProperty(p.Path), p.Name);
break;
case AggregateFuncTypeEnum.Sum:
newProjection = Projections.Sum(p.Path);
break;
default:
newProjection = Projections.Property(p.Path);
break;
}
projections.Add(newProjection, p.Name);
}
criteria.SetProjection(projections).SetResultTransformer(new AliasToBeanResultTransformer(projectionType));
if (goupprojections.Aliases.Length == 0)
{
rowcount.SetProjection(Projections.RowCount())
}
else
{
rowcount.SetProjection(Projections.Count(goupprojections))
}
var results = criteria.Future();
var count = rowcount.FutureValue<int>();

Categories