Initializing var type in LINQ - c#

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

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

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

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.

Related

How could I generate a c# .NET core function that can mimic the Include().ThenInclude.(Where) behaviour?

I'm trying to create a generic blazor component that and has a config object.
So far I've been able to successfully mimic .Where(), .Include().ThenInclude() separately, but now I need to add condition to the include.
My config file has the following structure
class Config
{
public List<string> includes {get;set;}
public List<Expression<Func<T,bool>>> conditions {get;set;}
}
database structure
For the example lets assume the following structure.
Dataset (1)<--(n) DatasetItem
DatasetItem (1)-->(1) Image
DatasetItem (1)-->(1) Label
Image (1)<--(n) Labels
Label(1)<--(n) Tags
where an image can be a part of more than one dataset and it's tags might or might not be included in the same dataset. Imagine an picture of a city park with dogs, people, and cars. Someone would want a dataset of vehicles and consider only the cars and ignore the labels about animals and people.
A Label include the coordinates and one or more TagNames (vehicle, car, red, fourWheeler, etc)
what works
And I can give conditions and includes this way:
var config = new Config<DatasetItem>()
{
includes = new() {"Image", "Image.Labels", "Image.Labels.Tags"}
conditions = new() {di => di.datasetId == 1, di => di.Label.width > 100}
}
the problems and limitations of this approach
For this to work I use the .Include(str) overload, but I don't like that approach since it is prone to fail.
The other problem with this approach is a limitation on the conditions.
For example, I can not add a condition to get only the Tags that belong to a specific category.
It seems that is not possible to add a condition that would give me this filter. Best I got was with the use of All or Any, but those do not give the desire result.
config.conditions.Add(
di => di.Images.Any(i => Labels.Any(l => l.Tags.Any(t => t.Name == "vehicle"))
)
what I'm looking for
Whith regular use of Include, obtaining the desire result would be like this.
DatasetItems.Include(di => di.Images)
.ThenInclude(i => i.Labels)
.ThenInclude(l => l.Tags.Where(t => t.Name == "vehicle"))
So, is there any way to achieve this?
I thing the solution would require to use Expression Trees, Reflection, and Recursive functions using MakeGenericMethod, but I havent found a way to achive this.
the ideal solution would be to find a structure like the one for conditions
List<Expression<Func<T,something>>> that lets me add the condition in this way
includes.add(di => di.Images.Labels.Tags.Where(t => t.Name == "vehicle"))
a failed attempt
I'm including a failed attempt I took to solve this, just in case this helps to narrow down the intention and for if someone can use it for solving the problem. This attempt is far from ideal.
property was a dot-separated string, like "Image.Labels.Tags" and filter was suppoused to be the Where expression.
This function does not compile.
public static IQueryable<T> FilteredInclude<T>(this IQueryable<T> query, string property, Func<dynamic, bool> filter) where T : class
{
var type = typeof(T);
var properties = property.Split('.');
var propertyInfo = type.GetProperty(properties[0]);
var parameter = Expression.Parameter(type, "x");
var propertyExpression = Expression.Property(parameter, propertyInfo);
var lambda = Expression.Lambda(propertyExpression, parameter);
var filteredQuery = query.Where(x => filter(propertyInfo.GetValue(x)));
if (properties.Length == 1)
{
var methodInfo = typeof(QueryableExtensions).GetMethod(nameof(Include));
methodInfo = methodInfo.MakeGenericMethod(type, propertyInfo.PropertyType);
var result = methodInfo.Invoke(null, new object[] { filteredQuery, lambda });
return (IQueryable<T>)result;
}
else
{
var nextProperty = string.Join(".", properties.Skip(1));
var result = filteredQuery.FilteredInclude(nextProperty, filter);
var thenIncludeMethod = typeof(Microsoft.EntityFrameworkCore.Query.Internal.IncludeExpression)
.GetMethod("ThenInclude")
.MakeGenericMethod(propertyInfo.PropertyType);
var resultWithThenInclude = thenIncludeMethod.Invoke(result, new object[] { lambda });
return (IQueryable<T>)resultWithThenInclude;
}
}

How to pass variables to Expression Func Projection before compile

I am trying to write Expression Functions for my projections. I found a good article about that link. But I couldn't figure out how can I pass variables to these functions.
How can I write a projection function for this one
int i = 3;
var query = await _db.Users
.FilterByName(name)
.Select(item => new SearchResultItemViewModel
{
Id = item.Id,
Article = item.FirstName + i.ToString()
});
}))
This one is working. In select SQL string has only Id and Firstname but I can't pass any variable.
var query = await _db.Users
.FilterByName(name)
.Select(item => SearchResultItemViewModel.Projection)
public static Expression<Func<ApplicationUser, SearchResultItemViewModel>> Projection
{
get
{
return item => new SearchResultItemViewModel
{
Id = item.Id,
Article = item.FirstName
};
}
}
This one is working only if you call compile and invoke. SQL string has all rows. Leading to bad performance
var query = await _db.Users
.FilterByName(name)
.Select(item => SearchResultItemViewModel.Projection.Compile().Invoke(item,i))
public static Expression<Func<ApplicationUser,int, SearchResultItemViewModel>> Projection
{
get
{
return( item,i) => new SearchResultItemViewModel
{
Id = item.Id,
Article = item.FirstName + i.ToString()
};
}
}
I don't use EF so this might vary in your specific use case, but this seems to be a question about LINQ Expressions more than anything else.
The first big problem is that you are trying to use an Expression<Func<ApplicationUser, int, SearchResultItemViewModel>> where you really meant Expression<Func<ApplicationUser, SearchResultItemViewModel>> and that's not going to do what you want. Instead of binding to a variable you're invoking the indexed variant of Select. So instead of getting the same value of i for all rows you get the index of the row.
When you create an expression that references a variable, one of two things happens. For local variables (and parameters) the value is copied to an anonymous class instance which is bound to the expression, so you can't change it afterwards. For other variables the expression contains a reference to the variable itself, as well as the containing object for non-static variables.
Which means that you could in principle use a static variable to alter the parameter and never have to recreate the projection expression. There are certainly times when this is useful.
On the other hand, your code above is creating a new instance each time you access the Projection property. So why not just change it to a function and generate the expression you need when you need it?
public static Expression<Func<ApplicationUser, SearchResultItemViewModel>> Projection(int parm)
=> item => new SearchResultItemViewModel
{
Id = item.Id,
Article = item.FirstName + parm.ToString()
};
Each time you invoke the method you'll get back an expression that uses the specified value.
Or you could use an expression visitor to take a template expression and modify the constants in it to whatever you need at the time. Fun, but a bit beyond scope here I think.

Apply "List" of IQueryables to a target?

I have this idea to create a "list" of IQueryables that do different kinds of operations.
So basically:
var query1 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Name == "Ronald");
var query2 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Age == 43);
var query3 = Enumerable.Empty<Person>().AsQueryable().Select(e => e.EyeColor);
var listOfQueries = new List<IQueryable<Person>
{
query1,
query2,
query3
};
Now, I also have this DbSet full of "Persons" and I would like to "apply" all my queries against that DbSet. How would I do that? Is it even possible?
An updated example:
var personQueryFactory = new PersonQueryFactory();
var personQueryByFirstname = personQueryFactory.CreateQueryByFirstname("Ronald"); //Query by Firstname.
var personQueryByAge = personQueryFactory.CreateQueryByAge(42); //Query by age.
var personQueryByHasChildWithAgeOver = personQueryFactory.CreateQueryByChildAgeOver(25); //Query using a "join" to the child-relationship.
var personQuerySkip = personQueryFactory.Take(5); //Only get the 5 first matching the queries.
var personQuery = personQueryFactory.AggregateQueries //Aggragate all the queries into one single query.
(
personQueryByFirstname,
personQueryByAge,
personQueryByHasChildWithAgeOver,
personQuerySkip
);
var personSurnames = personsService.Query(personQuery, e => new { Surname = e.Surname }); //Get only the surname of the first 5 persons with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
var personDomainObjects = personsService.Query<DomainPerson>(personQuery); //Get the first 5 persons as a domain-object (mapping behind the "scenes") with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
var personDaos = personsService.Query(personQuery); //Get the first 5 persons as a DAO-objects/entityframework-entities with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
The reason for doing this would be to create a more "unified" way of creating and re-using predefined queries, and then to be able to execute them against the DbSet and return the result as a domain-object and not an "entity-framework-model/object"
This is possible, but you need to reframe your approach slightly.
Storing the filters
var query1 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Name == "Ronald");
var query2 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Age == 43);
var query3 = Enumerable.Empty<Person>().AsQueryable().Select(e => e.EyeColor);
Reading your intention, you don't actually want to handle IQueryable objects, but rather the parameter that you supply to the Where method.
Edit: I missed that the third one was a Select(), not a Where(). Ive adjusted the rest of the answer as if this had also been a Where(). See the comments below for my response as to why you can't mix Select() with Where() easily.
Func<Person,bool> filter1 = (e => e.Name == "Ronald");
Func<Person,bool> filter2 = (e => e.Age == 43);
Func<Person,bool> filter3 = (e => e.EyeColor == "Blue");
This stores the same information (filter criteria), but it doesn't wrap each filter in an IQueryable of its own.
A short explanation
Notice the Func<A,B> notation. In this case, A is the input type (Person), and B is the output type (bool).
This can be extended further. A Func<string,Person,bool> has two input parameters (string, Person) and one output parameter (bool). A usage example:
Func<string, Person, bool> filter = (inputString, inputPerson) => inputString == "TEST" && inputPerson.Age > 35;
There is always one output parameter (the last type). Every other mentioned type is an input parameter.
Putting the filters in a list
Intuitively, since Func<Person,bool> represents a single filter; you can represent a list of filters by using a List<Func<Person,bool>>.
Nesting generic types get a bit hard to read, but it does work just like any other List<T>.
List<Func<Person,bool>> listOfFilters = new List<Func<Person,bool>>()
{
(e => e.Name == "Ronald"),
(e => e.Age == 43),
(e => e.EyeColor == "Blue")
};
Executing the filters
You're in luck. Because you want to apply all the filters (logical AND), you can do this very easily by stacking them:
var myFilteredData = myContext.Set<Person>()
.Where(filter1)
.Where(filter2)
.Where(filter3)
.ToList();
Or, if you're using a List<Func<Person,bool>>:
var myFilteredData = myContext.Set<Person>().AsQueryable();
foreach(var filter in listOfFilters)
{
myFilteredData = myFilteredData.Where(filter);
}
Fringe cases
However, if you were trying to look for all items which fit one or more filters (logical OR), it becomes slightly more difficult.
The full answer is quite complicated.. You can check it here.
However, assuming you have the filters set in known variables, there is a simpler method:
Func<Person, bool> filterCombined =
e => filter1(e) || filter2(e) || filter3(e);
var myFilteredData = myContext.Set<Person>()
.Where(filterCombined)
.ToList();
One of the problems is that the collection of IQueryable is only valid as long as your DbSet is valid. As soon as your DbContext is Disposed your carefully filled collection is worthless.
So you have to think of another method to reconstruct the query than the one that uses the DbSet<Person>
Although at first glance they seem the same, there is a difference between IEnumerable and IQueryable. An Enumerable has everything in it to enumerate over the resulting sequence.
A Queryable on the other hand holds an Expression and a Provider. The Provider knows where the data can be fetched. This is usually a database, but it can also be a CSV-file or other items where you can fetch sequences. It is the task of the Provider to interpret the Expression and to translate it info a format that the database can understand, usually SQL.
While concatenating the linq statements into one big linq statements, the database is not accessed. Only the Expression is changed.
Once you call GetEnumerator() and MoveNext() (usually by doing ForEach, or ToList(), or similar), the Expression is sent to the Provider who will translate it into a query format that the database understands and perform the query. the result of the query is an Enumerable sequence, so Getenumerator() and MoveNext() of the provider's query result are called.
Because your IQueryable holds this Provider, you can't enumerate anymore after the Provider has been disposed.
When using entity framework, the DbSet holds the Provider. In the Provider is the Database information held by the DbContext. Once you Dispose the DbContext you can't use the IQueryable anymore:
IQueryable<Person> query = null;
using (var dbContext = new MyDbcontext())
{
query = dbContext.Persons.Where(person => person.Age > 20);
}
foreach (var person in query)
{
// expect exception: the DbContext is already Disposed
}
So you can't put the Provider in your collection or possible queries. However, you could remember the Expression. The only thing your require from your Expression is that it returns a Person. You also need a function that takes this Expression and a QueryaProvider for Persons to convert it to an IQueryable.
Let's create a generic function for this, so It can be used for any type, not just for Persons:
static IQueryable<TSource> ToQueryable<TSource>(this IQueryProvider provider,
Expression expression)
{
return provider.CreateQuery(expression);
}
// well, let's add the following also:
static IQueryable<Tsource> ToQueryable<TSource>(this DbContext dbContext,
Expression expression)
{
return dbContext.Set<TSource>.Provider.ToQueryable<TSource>(expression);
}
For help on extension functions see Extension Functions Demystified
Now only once you create your collection of Expressions. For fast lookup make it a Dictionary:
enum PersonQuery
{
ByFirstname,
ByAge,
ByHasChildWithAgeOver,
Skip,
}
public IReadOnlyDictionary<PersonQuery, Expression> CreateExpressions()
{
Dictionary<PersonQuery, Expression> dict = new Dictionary<PersonQuery, Expression>();
using (var dbContext = new MyDbContext())
{
IQueryable<Person> queryByFirstName = dbContext.Persons
.Where(...);
dict.Add(PersonQuery.ByfirstName, queryByFirstName.Expression);
... // etc for the other queries
}
return dict.
}
Usage:
IReadOnlyCollection<Person> PerformQuery(PersonQuery queryId)
{
using (var dbContext = new MyDbContext())
{
// get the Expression from the dictionary:
var expression = this.QueryDictionary[queryId];
// translate to IQueryable:
var query = dbContext.ToQueryable<Person>(expression);
// perform the query:
return query.ToList();
// because all items are fetched by now, the DbContext may be Disposed
}
}

How to convert this lambda expression to linq

I can't read complicated lambda expression and only the super basic lambda expression only I know I am just starting to study lambda.
As my title above how to convert this lambda into linq?
var train = db.Certificates
.Join(db.TrainingSchedules, a => a.CertificateId, b => b.CertificateId, (a, b) => new { a, b })
.Where(x => x.a.Year.Value.Year == year && x.a.TrainingTypeId.Value == trainingTypeId && x.a.IsApproved.Value && x.b.EndDate >= DateTime.Now)
.Select(z => z.a).Distinct().Where(q => !db.Registrations.Where(s => s.EmployeeId == empId).Select(t => t.Certificate).Any(u => u.CertificateId == q.CertificateId));
Can someone explain to me why it has a different variables?. Like x , q , z , b?
As my title above how to convert this lambda into linq?
Have you every worked in Linq to know how does it looks like ?
If the above specified code is not Linq, then what is Linq, Lambda is an integral part of fluent representation of the Linq, since most of the APIs would need a Func delegate, that's where Lambda comes in.
Now regarding x , q , z , b, what do they represent ?
The Linq APIs are nothing but extension methods for IEnumerable<T>, like List<T>, Dictionary<TK,TV>, whether we write the fluent' or theSqlsyntax these variables represent, each and every element in the collection, which is processed as part of logic provided by theFunc Delegate, you can certainly use and should use more credible variable to represent the exact thing, similar to other parts of code whereint x, float y, DateTime z` is a bad way to code
Regarding the statement posted above consider following changes:
Rename a,b as cert,ts, which refers to a Certificate and Training Schedule classes respectively
Instead of generating anonymous type new { a, b }, create a class like CerificateTrainingSchedule that has all the elements of the Certificate and Training Schedule class respectively, it would be easier to work as you move forward
Rename x as cts, to represent combined CerificateTrainingSchedule
If its easy to read then separate Where clause in multiple chains, like:
.Where(cts => cts.a.Year.Value.Year == year)
.Where(cts => cts.a.TrainingTypeId.Value == trainingTypeId)
.Where(cts => cts.a.IsApproved.Value)
.Where(cts => cts.b.EndDate >= DateTime.Now)
Similarly the names of other variables can be modified to represent the true class and its objects, instead of random a,b,c,d. Also calls can be chained for clear understanding of the logic
Edit - // Modified Linq Query
// Joined / Merged version of Certificate and Training Schedule, add more fields as required, current one is based on certain assumptions of the fields / properties in the Certificate & TrainingSchedule classes respectively
public class CertificateTrainingSchedule
{
public int Year {get; set;} // Certificate Class Property
public int TrainingTypeId {get; set;} // Certificate Class Property
public bool IsApproved {get; set;} // Certificate Class Property
public DateTime EndDate {get; set;} // TrainingSchedule Class Property
}
var train = db.Certificates
.Join(db.TrainingSchedules, cert => cert.CertificateId, ts => ts.CertificateId, (cert, ts) => new CertificateTrainingSchedule{ Year = cert.Year, TrainingTypeId = cert.TrainingTypeId, IsApproved = cert.IsApproved,EndDate = ts.EndDate})
.Where(cts => cts.Year == year)
.Where(cts => cts.TrainingTypeId == trainingTypeId)
.Where(cts => cts.IsApproved)
.Where(cts => cts.EndDate >= DateTime.Now)
.Select(cts => new {cts.Year,cts.TrainingTypeId,cts.IsApproved})
.Distinct() // Allowing anonymous type to avoid IEqualityComparer<Certificate>
.Where(certMain => !db.Registrations.Where(s => s.EmployeeId == empId)
.Select(cert => new Certificate{Year = cert.Year,TrainingTypeId = cert.TrainingTypeId,IsApproved = cert.IsApproved})
.Any(cert => cert.CertificateId == certMain.CertificateId))
I assume that by your question, you mean you want a query expression that is equivalent to calling the LINQ methods explicitly. Without a good Minimal, Complete, and Verifiable code example, it's impossible to know for sure what a correct example would be. However, the following is I believe what you're looking for:
var train =
from q in
(from x in
(from a in db.Certificates
join b in db.TrainingSchedules on a.CertificateId equals b.CertificateId
select new { a, b })
where x.a.Year.Value.Year == year && x.a.TrainingTypeId == trainingTypeId &&
x.a.IsApproved.Value && x.b.EndDate >= DateTime.Now
select x.a).Distinct()
where !(from s in db.Registrations where s.EmployeeId == empId select s.Certificate)
.Any(u => u.CertificateId == q.CertificateId)
select q;
Note that not all of the LINQ methods have a C# query expression language equivalent. In particular, there's no equivalent for Distinct() or Any(), so these are still written out explicitly.
Can someone explain to me why it has a different variables?. Like x , q , z , b?
Each lambda expression has the input on the left side of the => and the result expression on the right. The variables you're referring to are the inputs. These are commonly written using single letters when writing lambda expressions, because the a lambda expression is so short, the meaning can be clear without a longer variable name. For that matter, independent lambda expressions could even use the same variable name.
Note that in the query expression syntax, not all of the variables "made it". In particular, we lost z and t because those variables were superfluous.
In an expression this long, it's possible you might find longer variable names helpful. But it's a trade-off. The query expression language is meant to provide a compact way to represent queries on data sources. Longer variable names could make it harder to understand the query itself, even as it potentially makes it easier to understand the intent of each individual part of the expression. It's very much a matter of personal preference.
I can't read complicated lambda expression and only the super basic lambda expression only I know I am just starting to study lambda.
Try reading this:
var train = db.Certificates
.Where(c => c.Year.Value.Year == year &&
c.TrainingTypeId.Value == trainingTypeId &&
c.IsApproved.Value &&
c.TrainingSchedules.Any(ts => ts.EndDate >= DateTime.Now) &&
!c.Registrations.Any(r => r.EmployeeId == empId));
If you can, then you are just fine.
Note that this is not an exact translation of the sample query, but is functionally equivalent (should produce the same result). The sample query is a good example of badly written query - variable naming, unnecessary multiplicative Join which requires then a Distinct operator (while GroupJoin would do the same w/o the need of Distinct), inconsistent handling of two similar detail criteria (Join for TrainingSchedules and Any for Registrations), overcomplicated criteria for Registrations part etc.
Shortly, don't write such queries. Concentrate on the desired result from the query and use the most logical constructs to express it. Avoid manual joins when you have navigation properties. If you don't have navigation properties, then add them to the model - it's easy one time action which helps a lot when writing queries. For instance, in my translation I assume you have something like this:
class Certificate
{
// Other properties ...
public ICollection<TrainingSchedule> TrainingSchedules { get; set; }
public ICollection<Registration> Registrations { get; set; }
}
class TrainingSchedule
{
// Other properties ...
public Certificate Certificate { get; set; }
}
class Registration
{
// Other properties ...
public Certificate Certificate { get; set; }
}
UPDATE: Here is the same using the query syntax:
var train =
from c in db.Certificates
where c.Year.Value.Year == year &&
c.TrainingTypeId.Value == trainingTypeId &&
c.IsApproved.Value &&
c.TrainingSchedules.Any(ts => ts.EndDate >= DateTime.Now) &&
!c.Registrations.Any(r => r.EmployeeId == empId)
select c;

How to refactor LINQ select statement

Given the following LINQ statement, can someone tell me if it is possible to refactor the select portion into an expression tree? I have not used expression tree's before and have not been able to find much information regarding Selects.. Note this is to be translated into SQL and run inside SQL Server, not in memory.
var results = db.Widgets
.Select(w => new
{
Name = (w is x) ? "Widget A" : "Widget B"
});
I would like to be able to do this..
var name = [INSERT REUSABLE EXPRESSION]
var somethingElse = [INSERT REUSABLE EXPRESSION]
var results = db.Widgets.Select(w => new { Name = name, SomethingElse = somethingElse });
Obviously the intended use is for more complex statements.
You can do this using LinqKit. It'll work as long as your method is translatable to SQL. This is essentially what a complete example might look like:
public static class ReusableMethods
{
public static Expression<Func<int, Person>> GetAge()
{
return p => p.Age;
}
}
var getAge = ReusableMethods.GetAge();
var ageQuery = from p in People.AsExpandable()
select getAge.Invoke(p);
Note that:
You need to add AsExpandable() to your IQueryable.
You must assign your method to a local variable before using it (not sure about the exact reason why, but its a must).

Categories