Linq query syntax is off when selecting into new data transfer obj - c#

I am fairly new to linq and am having some issues with syntax. I haven't bothered trying to run it, because I can see from all the redlines that the syntax is way off.
Essentially I just want to select from this table into a new DTO. I think if you look at what my code is, you can probably understand what I am trying to do. Here is my linq query.
var SubLoanTypes = _ctx.PrsnVaFhaTypes.Where(p => p.PrsnPk == PersonPk
into tmp
from sl in tmp.DefaultIfEmpty()
select new ReviewerSubLoanTypeDto()
{
VaFHATypeID = sl.VaFHATypeID,
IsActivated = sl.IsActivated
}).OrderBy(x => x.VaFHATypeId).ToList();
redline over into tmp: into does not exist in the current context.
The question is: how can i fix the syntax for this to work?
I cant find anything as far as resources for something similar to mine.

Here is a simplified and working way to do what I wanted:
var SubLoanTypes = _ctx.PrsnVaFhaTypes.Where(p => p.PrsnPk == PersonPk)
.Select(sl => new ReviewerSubLoanTypeDto()
{
VaFHATypeID = sl.VaFHATypeID,
IsActivated = sl.IsActivated.Value
}).OrderBy(x => x.VaFHATypeID).ToList();
Thanks

Related

How to keep initializer list order within Select and/or SelectMany

I hope this is not a duplicate but I wasn't able to find an answer on this.
It either seems to be an undesired behavior or missing knowledge on my part.
I have a list of platform and configuration objects. Both contains a member string CodeName in it.
The list of CodeNames look like this:
dbContext.Platforms.Select(x => x.CodeName) => {"test", "PC", "Nintendo"}
dbContext.Configurations.Select(x => x.CodeName) => {"debug", "release"}
They are obtained from a MySQL database hence the dbContext object.
Here is a simple code that I was to translate in LINQ because 2 foreach are things of the past:
var choiceList = new List<List<string>>();
foreach (Platform platform in dbContext.Platforms.ToList())
{
foreach (Configuration configuration in dbContext.Configurations.ToList())
{
choiceList.Add(new List<string>() { platform.CodeName, configuration.CodeName });
}
}
This code gives my exactly what I want, keeping the platform name first which looks like :
var results = new List<List<string>>() {
{"test", "debug"},
{"test", "release"},
{"PC", "debug"}
{"PC", "release"}
{"Nintendo", "debug"}
{"Nintendo", "release"}};
But if I translate that to this, my list contains item in a different order:
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
I will end up with this, where the platform name isn't always first, which is not what is desired:
var results = new List<List<string>>() {
{"debug", "test"},
{"release", "test"},
{"debug", "PC"}
{"PC", "release"}
{"debug", "Nintendo"}
{"Nintendo", "release"}};
My question is, is it possible to obtain the desired result using LINQ?
Let me know if I'm not clear or my question lacks certain details.
Thanks
EDIT: So Ivan found the explanation and I modified my code in consequence.
In fact, only the Enumerable in front of the SelectMany needed the .ToList().
I should also have mentioned that I was stuck with the need of a List>.
Thanks everyone for the fast input, this was really appreciated.
When you use
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
it's really translated to some SQL query where the order of the returned records in not defined as soon as you don't use ORDER BY.
To get the same results as your nested loops, execute and materialize both queries, and then do SelectMany in memory:
var platforms = dbContext.Platforms.ToList();
var configurations = dbContext.Configurations.ToList();
var choiceList = platforms.SelectMany(p => configurations,
(p, c) => new List<string>() { p.CodeName, c.CodeName })
.ToList();
Rather than projecting it out to an array, project it out two a new object with two fields (potentially an anonymous object) and then, if you need it, project that into a two element array after you have retrieved the objects from the database, if you really do need these values in an array.
Try this-
var platforms= dbContext.Platforms.Select(x=>x.CodeName);
var configurations=dbContext.Configurations.Select(x=>x.CodeName);
var mix=platforms.SelectMany(num => configurations, (n, a) => new { n, a });
If you want to learn more in detail- Difference between Select and SelectMany

two foreach in linq?

...I've tried something but I got the "Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator." exception.
I'm talking about this:
Query = Query.Where(t => this.SysTextBox.Text.CSL2Array().All(ss => t.SysName.Contains(ss)));
I'm kind of new at this, but I kept trying to make it work. Thank you in advance!
You should be able to do the query after ToList:
Query = Query.ToList<TagsDeleted>()
.Where(t => this.SubSystemTextBox.Text.CSL2Array().All(ss => t.SubsystemName.Contains(ss)))
.AsQueryable<TagsDeleted>();
But that is not easy to read, I would refactor to the following to make it clear that "I create a list, and want to remove some items":
var words = this.SubSystemTextBox.Text.CSL2Array();
var list = Query.ToList<TagsDeleted>();
list.RemoveAll(t => !words.All(word => t.SubsystemName.Contains(word)));
Query = list.AsQueryable<TagsDeleted>();
var arr = this.SubSystemTextBox.Text.CSL2Array();
var notContained = Query.Where(td=>arr.Any(ss => !td.SubsystemName.Contains(ss)));
Query = Query.Except(notContained);
You can figure this out how to make it in one line, this was just to be clear.

Creating a single Type from muliple entities in Lambda

I have 3 entities
I'd like to flatten the tblCondition and BusinessAreas into one object. What I want is Category.ID, Category.Category, BusinessArea.ID, BusinessArea AreaName.
I know this can be done by creating an Anonymous type with Lambda but I'm relatively unskilled with Lampda or LINQ.
Forgot to mention that I need to get to the two tables through the first one.
My original call looks like this.
myConditionTemplate = EE.ConditionTemplates.Where(c => c.TemplateCode == TextBoxSearchConditionCode.Text).FirstOrDefault();
Here's the official documentation: http://msdn.microsoft.com/en-us/library/vstudio/bb384105.aspx
Essentially in your select portion use the new keyword without a class name like:
select new { Category.ID, Category.Category, BusinessArea.ID, BusinessArea.AreaName }
The webpage only shows an example using Linq in query form, but to do it in method form:
var results = db.GetStuff().Select(x => new { x.ID, x.Name });
Ack, that may not be very clear. I just found some great examples at How to do a join in linq to sql with method syntax?. This was a different question, but the answer's example shows you how to do the lamba for a joined enumerable set.
UPDATE: Since you updated your question, see if this helps:
var results = myConditionTemplate.Select(x => new { CategoryID = x.tblCondition.ID, Category = x.tblCondition.Category, BusinessAreaID = x.tblCondition.BusinessArea.ID, AreaName = x.tblCondition.BusinessArea.AreaName});

LINQ - "Unable to create constant value of type [type]"?

Here's my error
Unable to create a constant value of type 'Courses.Model.Track'. Only primitive types or enumeration types are supported in this context.
and here's the code that causes it:
model.Courses = db.Courses
.Where(c => model.Tracks.Any(t => t.Certification.ID == certificate))
.ToList();
Also, model.Courses is IEnumerable<Courses>, and I'm not sure if that has anything to do with my problem.
Could anybody shed some light on this for me? Many thanks.
db.Courses is an IQueryable. While the syntax is virtually identical to the LINQ methods of IEnumerable, under the hood they're completely different.
The IQueryable code isn't actually exectued anywhere at all. It just creates a bunch of Expression objects that different query providers are able to use to do...whatever they want (in this case query a database). Those query providers need to specifically have code to handle any given type of input. There are some things that they either can't sensibly transform into a SQL query, or things that the programmers simply didn't think of or choose to handle (even if it might have a sensible SQL translation).
In sort, the query provider just doesn't know how to translate model.Tracks.Any(t => t.Certification.ID == certificate) into SQL.
You simply need to know what types of code is and isn't supported by the query provider that you're using and try to manipulate the code you have into something that it can handle. Something like this should work:
var certificates = model.Tracks.Select(t => t.Certification.ID).ToList();
model.Courses = db.Courses
.Where(c => certificates.Contains(c))
.ToList();
If this were C# code executing all of this in LINQ to objects the two queries would be identical, but to a query provider they're not. They simply have special support for knowing when they see List.Contains to map it to a SQL IN clause. They didn't add specific support for what you did in your first query.
Try using LINQ 'Where In', this should solve your problem:
var names = new string[] { "Alex", "Colin", "Danny", "Diego" };
var matches = from person in people
where names.Contains(person.Firstname)
select person;
OR
var matches = from person in people
where person.Firstname == "Alex" ||
person.Firstname == "Colin" ||
person.Firstname == "Danny" ||
person.Firstname == "Diego"
select person;
http://blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx
My apologies for the almost unnecessary question, although #klugerama did help me figure it out.
There was a problem with this:
model.Courses = db.Courses
.Where(c => model.Tracks.Any(t => t.Certification.ID == certificate))
.ToList();
Until I changed it to this:
model.Courses = db.Courses
.Where(c => c.Track.Certification.ID == certificate)
.ToList();
Again, my apologies. Thanks to everyone for their input.

C#, Linq to Sql: Why can I not use an expression to filter SubEntities?

I have made myself an ExpressionBuilder class that helps me put together expressions that can be used as a predicate when doing Linq to Sql queries. It has worked great. However, I just discovered Expressions can only be used to filter on Tables, and not on EntitySets??Why on earth is this the case?
For example if I have Company and an Employee with a Salary. I could create these two expressions:
Expression<Func<Company, bool>> cp = x => x.Name.StartsWith("Micro");
Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
I would then expect to be able to do the following, however it only partially works:
var companies = dataContext.Companies
.Where(cp) // Goes fine
.Select(x => new
{
x.Name,
SumOfSalaries = x.Employees
.Where(ep) // Causes compile-time error
.Sum(y => y.Salary),
}
.ToList();
Also, if I do a ep.Compile() it compiles, but then I get an error when running the query.
Why is this the case? Am I missing something? I don't find this logical. Can I fix this somehow? Or do you have a good workaround?
I know that I in this case could just use Where(x => x.Name.StartsWith("John")) instead, but the problem is that the expressions I need are not that trivial. They are longer strings of AndAlsos and OrElses.
If you are going to pass a lambda expression to a LINQ to SQL provider don't create it as an Expression<T> - let the provider do that for you.
The following works for me - note both are compiled:
var companies = dataContext.Companies.Where(cp.Compile())
.Select(x => new
{
x.Name,
SumOfSalaries = x.Employees
.Where( ep.Compile() )
.Sum(y => y.Salary),
}
).ToList();
The expression parser seems to be losing type information in there somewhere after the first where clause when you put in the second. To be honest, I'm not sure why yet.
Edit: To be clear, I do understand that EntitySet doesn't support passing an expression into the where clause. What I don't completely understand is why it fails when you add the Where(ep.Compile()).
My theory is that in compiling the first where (Where(cp.Compile()), Linq2Sql quits parsing the expression - that it can't parse the ep.Compile() against an entityset, and is unable to decide to break up the query into two until you compile the first where clause.
I think you need to rewrite your query. Another way to ask for the details you want, is: "Give me the sum of the salaries for the selected employees in the selected companies, organized by the company name".
So, with the employee salaries in focus, we can write:
//Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
//Expression<Func<Company, bool>> cp = x => x.Name.StartsWith("Micro");
Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
Expression<Func<Employee, bool>> cp = x => x.Company.Name.StartsWith("Micro");
var salaryByCompany = dataContext.Employees
.Where(ep)
.Where(cp)
.GroupBy(employee => employee.Company.Name)
.Select(companyEmployees => new
{
Name = companyEmployees.Key,
SumOfSalaries = companyEmployees.Sum(employee => employee.Salary)
});
var companies = salaryByCompany.ToList();

Categories