简体   繁体   中英

Initializing var type in LINQ

Question : How do I initialize var in the following code? [Then, of course, I'll remove var declaration from the if statement]

The last line of the following code returns the well know error: The name lstCurrent does not exists in current context . Clearly the error is because the var is defined inside if statement and used outside if statement

Note : I'm selecting only a few columns from the table and hence dealing with anonymous type. Some examples I saw online did not work - probably since my code is selecting anonymous type. But this is just a guess.

var lstCurrent =????;

if(Type==1)
  var lstCurrent =_context.Customers().Where(t =>t.type=="current").Select(c => new { c.LastName, c.City});
else
  lstCurrent  = _context.Customers().Where(...).Select(...)

return View(lstCurrent.ToList());

Try

IEnumerable lstCurrent;
if(Type == 1)
    lstCurrent = foo;
else
    lstCurrent = bar;

var is not a type - it means "I don't care to (or can't) specify what the type is - let the compiler do it for me".

In your case, you're assigning it the result of one of two queries, one of which returns an anonymous type, so you can't specify the type since you don't know the name of the anonymous type (hence the term "anonymous").

In order to use var , the compiler needs some expression at initialization to know what the actual type is.

I'd suggest something like:

var lstCurrent = Type==1 
              ? _context.Customers().Where(t =>t.type=="current").Select(c => new { c.LastName, c.City})
              : _context.Customers().Where(...).Select(...)

But note that your "selects" must return the same type (or anonymous types with the exact same fields) or you won't be able to use var .

In the end I would try to bake the condition into your Where clause for less repetetive code:

bool isTypeOne = Type==1;

var lstCurrent = _context.Customers()
                         .Where(t => isTypeOne ? t.type=="current" : ...)
                         .Select(c => new { c.LastName, c.City})

How do I initialize var in the following code? [Then, of course, I'll remove var declaration from the if statement]

This is not possible since the type of the object you assign at the left should be known. For instance

var a = "text";

The type of a is known at compile time since the right hand expression is a string . This cannot be done with a sequence of anonymous types, like the one you define.

I can see two options. One is that D Stanley already mentioned. The other is to define a class with two properties like below:

public class PersonCity
{
    public string LastName { get; set; }
    public string City { get; set; }
}

and then project each element of your query to a PersonCity object.

lstCurrent context.Customers()
                  .Where(t =>t.type=="current")
                  .Select(c => new PersonCity
                  { 
                      LastName = c.LastName, 
                      City = c.City
                  });

Doing so, you can define now you lstCurrent as below:

var lstCurrent = Enumerable.Empty<PersonCity>();

Important Note

In case of your queries return different types, the above are meaningless . Both queries (one in if and the other at else) should return the same type.

This is a common trap when expecting to use an implicit type declaration or when refactoring code that already has an implicit type ( var ). The current accepted answer is very much valid but reducing all expression variations into a single 1-liner linq expression can easily impact on readability of the code.

The issue in this case is complicated by the projection to an anonymous type at the end of the query, which can be solved by using an explicit type definition for the projection, but it is simpler to break up the query construction into multiple steps:

var customerQuery = _context.Customers().AsQueryable();
if (Type == 1)
    customerQuery = customerQuery.Where(t => t.type == "current");
else
    customerQuery = customerQuery.Where(...);
... // any other filtering or sorting expressions?

var lstCurrent = customQuery.Select(c => new { c.LastName, c.City});
return View(lstCurrent.ToList());

or of course that last segment could have been a one liner or if there are no further references to lstCurrent the compiler may optimise that into the following:

return View(customQuery.Select(c => new { c.LastName, c.City}).ToList());

In this example I have deliberately cast to IQueryable<T> to ensure this solution is compatible with both IQueryable<T> / DbSet<T> contexts and repository style IEnumerable<T> contexts.


This variation is usually the first that comes to mind, but we are still declaring the source of the query in two places, which increases the ambiguity of this code and the risk of divergence in later refactoring (by accidentally editing only one branch and not maintaining the code in the other branch):

IQueryable<Customer> customerQuery = null;
if (Type == 1)
    customerQuery = _context.Customers().Where(t => t.type == "current");
else
    customerQuery = _context.Customers().Where(...);
... // any other filtering or sorting expressions?
var lstCurrent = customQuery.Select(c => new { c.LastName, c.City});
return View(lstCurrent.ToList());

A different solution is to explicitly define the output as its own concrete class:

public class CustomerSummary
{
    public string LastName { get;set; }
    public string City { get;set; }
}
...

List<Customers> customers = null;
if (Type == 1)
    customers = _context.Customers().Where(c => c.type == "current")
                                    .Select(c => new CustomerSummary
                                    {
                                        LastName = c.LastName,
                                        City = c.City
                                    }).ToList();
else
    customers = _context.Customers().Where(c => ...)
                                    .Select(c => new CustomerSummary
                                    {
                                        LastName = c.LastName,
                                        City = c.City
                                    }).ToList();
... // any other filtering or sorting expressions?
return View(customers);

It's a lot of code for a once-off, but if you make it abstract enough it could be re-used for other scenarios, I would still combine this with the first code example, that keeps the source, filter and projection logic separated, over the lifetime of an application these 3 elements tend to evolve differently, so separating out the code makes refactoring or future maintenance easier to complete and review.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM