LINQ Querying list in list - c#

I have this situation:
My ModelView:
public class Subject
{
public int ID { get; set; }
public string Name { get; set; }
public int ProfessorID { get; set; }
public string ProfessorFullName{ get; set; }
public IList<Assistant> Assistants { get; set; }
}
public class Assistant
{
public string AssistantFullName{ get; set; }
}
My query:
var subjects = from subject in Entities.Subjects
from professor in subject.Lecturers
where professor.Professor == true
select new SSVN.ModelView.Subject()
{
ID = subject.ID,
Name= subject.Name,
ProfessorFullName= professor.LastName+ " " + professor.Name,
Assistants= (from subject1 in Entities.Subjects
from assistant in subject1.Lecturers
where assistant.Professor == false
select new SSVN.ModelView.Assistant()
{
AssistantFullName = assistant.LastName+ " " + assistant.Name
}).ToList()
};
And when I call:
subjects.ToList(); I get exception:
LINQ to Entities does not recognize the method
'System.Collections.Generic.List`1[SSVN.ModelView.Assistant] ToList[Assistant]
(System.Collections.Generic.IEnumerable`1[SSVN.ModelView.Assistant])' method, and this
method cannot be translated into a store expression.

You cannot call ToList inside linq-to-entities query. Linq-to-entities query will always project to IEnumerable<T> so if you want IList<T> you must call it in linq-to-objects.
Try this:
var subjects = (from subject in Entities.Subjects
from professor in subject.Lecturers
where professor.Professor == true
select new
{
ID = subject.ID,
Name= subject.Name,
ProfessorFullName= professor.LastName+ " " + professor.Name,
Assistants= (from subject1 in Entities.Subjects
from assistant in subject1.Lecturers
where assistant.Professor == false
select new SSVN.ModelView.Assistant()
{
AssistantFullName = assistant.LastName+ " " + assistant.Name
})
}).AsEnumerable().Select(x => new SSVN.ModelView.Subject
{
ID = x.ID,
Name = x.Name,
ProfessorFullName = X.ProffesorFullName,
Assistants = x.Assistants.ToList()
});

You cannot and should not use a ToList() in an IQueryablle query. Note that this query has to be translated into SQL.

Related

LINQ/C# - Making a DTO from a collection?

I'm using EF 6.2 with SQL. Suppose I have these DTO classes:
private class ParentModel
{
public string FullName { get; set; }
public IEnumerable<ChildModel> Children { get; set; }
}
private class ChildModel
{
public string FullName { get; set; }
public string SpiritAnimalDescription { get; set; }
}
ParentModel is derived from an entity class Parent.
ChildModel is from Child, which has a relationship with another entity class SpiritAnimal. Note that I changed it in the .EDMX to Children.
As you can infer, SpiritAnimal has a Description field which I'm trying to retrieve into the ChildModel field, SpiritAnimalDescription.
Naturally, a Parent has a collection of Child, which in turn has one SpiritAnimal (by design). Now, I'm trying to obtain a List<ParentModel> with this code, which currently isn't working:
var query = from p in db.Parents
join c in db.Children on p.Id equals c.Parent_Id
join sa in db.SpiritAnimals on c.SpiritAnimal_Id equals sa.Id
select new ParentModel
{
FullName = p.LastName + ", " + p.FirstName
Children = c.Select(a => new ChildModel // <-- Error here :(
{
FullName = a.FirstName + " " + a.LastName,
SpiritAnimalDescription = sa.Description
}
};
var list = query.ToList();
How can I solve this, as efficiently as possible? Thanks!
EDIT:
Entity classes look something like this, for brevity:
private class Parent
{
public int Id { get; set; } // PK
public string LastName { get; set; }
public string FirstName { get; set; }
}
private class Child
{
public int Id { get; set; } // PK
public string LastName { get; set; }
public string FirstName { get; set; }
public int Parent_Id { get; set; } // FK
public int SpiritAnimal_Id { get; set; } // FK
}
private class SpiritAnimal
{
public int Id { get; set; } // PK
public string Description { get; set; }
}
Your code cannot be compiled and run, so it is impossible to determine exactly what should be.
I can only assume that it should be something like this:
var query = from p in db.Parents
select new ParentModel
{
FullName = p.LastName + ", " + p.FirstName,
Children = db.Children.Where(c => c.Parent_Id == p.Id)
.Select(c => new ChildModel
{
FullName = c.FirstName + " " + c.LastName,
SpiritAnimalDescription = db.SpiritAnimals
.FirstOrDefault(sa => sa.Id == c.SpiritAnimal_Id).Description
})
};
Note: use the navigation properties.
Should look something like this:
var query = from p in db.Parents
select new ParentModel()
{
FullName = p.LastName + ", " + p.FirstName,
Children = p.Clildren.Select(a => new ChildModel()
{
FullName = a.FirstName + " " + a.LastName,
SpiritAnimalDescription = sa.Description
}).ToList()
};

Adding new class member via LINQ query

I have a LINQ query and because the variable that stores the result will be used in an "if" statement, I've had to initialize it before the query. This required making a class due to the different data types being stored in the list - however I'm having trouble making class members inside the LINQ query and I'm not sure why.
Class:
public class OtherProgramType
{
public string State { get; set; }
public string PrgName { get; set; }
public short? ProgramTypeID { get; set; }
public string DisplayText { get; set; }
}
Code:
List<OtherProgramType> otherPrograms;
otherPrograms = (from hm in db.HabitatManagement
join svy in db.Survey on hm.SurveyID equals svy.SurveyID
join iu in db.InventoryUsers on hm.UserID equals iu.UserID
join pt in db.ProgramType on hm.ProgramTypeID equals pt.ProgramTypeID
where pt.Program != "State Agency Public Land Programs"
&& pt.Program != "State Agency Private Land Programs"
&& svy.ReportingYear == (from svy in db.Survey
where svy.ReportingYear.HasValue
select svy.ReportingYear.Value).Max()
|| pt.Program != "State Agency Public Land Programs"
&& pt.Program != "State Agency Private Land Programs"
&& svy.ReportingYear == (from svy in db.Survey
where svy.ReportingYear.HasValue
select svy.ReportingYear.Value).Max() - 1
select new
{
iu.StateID,
hm.ProgramTypeID,
pt.Program
})
.Distinct()
.Select(x => new OtherProgramType { x.StateID, x.Program, x.ProgramTypeID, DisplayText = x.StateID.ToString() + ", " + x.Program.ToString() })
.OrderBy(x => x.StateID)
.ToList();
This is the line where I want the new class member to be made:
.Select(x => new OtherProgramType { x.StateID, x.Program, x.ProgramTypeID, DisplayText = x.StateID.ToString() + ", " + x.Program.ToString() })
The x.StateID, x.Program, x.ProgramTypeID get underlined in red squiggles and it says "Invalid initializer member declarator."
You need to state the field assignments, especially since the fields from x don't match the fields from your type OtherProgramType
.Select(x => new OtherProgramType
{
State = x.StateID,
PrgName = x.Program,
ProgramTypeID = x.ProgramTypeID,
DisplayText = x.StateID.ToString() + ", " + x.Program.ToString()
})
You need to give the names of the properties you want to assign to:
.Select(x => new OtherProgramType {
State = x.StateID,
PrgName = x.Program,
ProgramTypeID = x.ProgramTypeID,
DisplayText = x.StateID.ToString() + ", " + x.Program.ToString()
})
Both the above answers are absolutely correct. Just to add:
If your class had a constructor taking all the parameters your code could have been:
public class OtherProgramType
{
public OtherProgramType(string s, string pn, short? ptid, string dt)
{
this.State = s;
this.PrgName = pn;
this.ProgramTypeID = ptid;
this.DisplayText = dt;
}
public string State { get; set; }
public string PrgName { get; set; }
public short? ProgramTypeID { get; set; }
public string DisplayText { get; set; }
}
Now the appropriate line could be:
...
.Select(x => new OtherProgramType ( x.StateID, x.Program, x.ProgramTypeID, x.StateID.ToString() + ", " + x.Program.ToString() ))
...
Notice the () instead of {}.

ASP.Net Bootgrid Integration (without sorting)

I've been struggling for days now trying to implement jQuery Bootgrid with my ASP.Net application. So far this is what I have: (Order By Functionality isn't working yet, I'll tackle that later)
public JsonResult IndexJson(RequestData model)
{
var result = (from x in db.ContactSet
select new
{
x.AccountId,
x.FirstName,
x.LastName,
x.FullName,
x.JobTitle,
x.ParentCustomerId,
x.EMailAddress1,
x.Telephone1,
x.MobilePhone,
x.Fax,
x.GenderCode,
x.BirthDate
}); //? Gets all rows
result = (from x in result
where x.FirstName.Contains(model.searchPhrase)
|| x.LastName.Contains(model.searchPhrase)
select x); //? Search Filter
var totalRows = result.Count(); //? Sets totalRows (for ResponseData)
if (model.rowCount == -1)
model.rowCount = totalRows; //? In case View All Rows is selected by Bootgrid (for ResponseData)
// TODO: Add Order By functionality
var tResult = new ResponseData<object>()
{
current = model.current,
rowCount = model.rowCount,
rows = result.ToList(),
total = totalRows
}; //? Builds Json Response
return Json(tResult, JsonRequestBehavior.AllowGet);
}
The problem with this code is I need to count the total number of records after the search functionality and I'm just not that skilled at using the LINQ Queries properly.
By the time I get to var totalRows = result.Count(); I get the following error:
System.NotSupportedException: 'The method 'Where' cannot follow the method 'Select' or is not supported. Try writing the query in terms of supported methods or call the 'AsEnumerable' or 'ToList' method before calling unsupported methods.'
Any idea what's wrong here?
I have been using bootgrid in difference situations, including implementing server-side paging and sorting asc, desc without any problem.
Try this:
//Let's assume this is your model....
public class RequestData
{
public int RowCount { get; set; }
public int Current { get; set; }
public string Search { get; set; }
public string SortBy { get; set; }
public string SortDirection { get; set; }
public int TotalItems { get; set; }
}
1.If you are not selecting all the columns of your DB table, create a DTO that will map your selected columns. Otherwise skip this part and replace anywhere you see ContactSetDTO with ContactSet
public class ContactSetDTO
{
public string AccountId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
public string JobTitle { get; set; }
public string ParentCustomerId { get; set; }
public string EMailAddress1 { get; set; }
public string Telephone1 { get; set; }
public string MobilePhone { get; set; }
public string Fax { get; set; }
public string GenderCode { get; set; }
public DateTime BirthDate { get; set; }
}
2.Assuming you are using SQL Server, you could use the following method to retrieve the count:
public int getContactSetCount(string searchPhrase)
{
int ret = 0;
try
{
string query = string.Empty;
if (!string.IsNullOrWhiteSpace(searchPhrase))
{
// ********* Assuming your db table is also called ContactSet **********************
query = #"SELECT COUNT(*) FROM ContactSet s WHERE s.FirstName LIKE '%' + #p0 + '%' OR s.LastName LIKE '%' + #p0 + '%')";
ret = db.Database.SqlQuery<int>(query, new System.Data.SqlClient.SqlParameter(parameterName: "#p0", value: searchPhrase)).FirstOrDefault();
}
else
{
ret = db.ContactSet.Count();
}
}
catch (Exception)
{
throw;
}
return ret;
}
3.And finally, your method would look like this:
public JsonResult IndexJson(RequestData model)
{
var searchPhrase = model.Search;
if (!string.IsNullOrWhiteSpace(searchPhrase))
{
//Notice that the select columns match the ContactSetDTO properties
string query = #"SELECT TOP " + model.RowCount + " s.AccountId, s.FirstName, s.LastName, s.FullName, s.JobTitle, s.ParentCustomerId, s.EmailAddress1, s.Telephone1, s.MobilePhone, s.Fax, s.GenderCode, s.BirthDate FROM ContactSet s WHERE s.FirstName LIKE '%' + #p0 + '%' OR s.LastName LIKE '%' + #p0 + '%')";
//Then, this should return a list of ContactSetDTO for you
var result = db.Database.SqlQuery<ContactSetDTO>(query, new System.Data.SqlClient.SqlParameter(parameterName: "#p0", value: searchPhrase)).ToList();
var totalRows = getContactSetCount(searchPhrase);
var tResult = new { rows = result, rowCount = model.RowCount, total = totalRows, current = model.Current, searchPhrase = model.Search }
};
return Json(tResult, JsonRequestBehavior.AllowGet);
}
I hope the above will help.

Using Linq to read from multiple tables

I'm sure someone else has asked this but I searched on what I could think of to find the solution.
I've got the following data models to match tables in my SQL db:
public class ProfileDetailModel
{
public string id { get; set; }
public string name { get; set; }
public StyleList[] styleList { get; set; }
public FabricList[] fabricList { get; set; }
}
public class StyleList
{
public string id { get; set; }
public string name { get; set; }
}
public class FabricList
{
public string id { get; set; }
public string fabricName { get; set; }
}
This is the current query code:
var query = (from t in db.tblProfiles
select new ProfileDetailModel()
{
id = t.id,
name = t.name
});
var querylist = await query.ToListAsync();
(prototyped linq queries below for style and fabric)
var styleQuery = (from t in db.tblStyles
select new styleList()
{
id = t.id,
name = t.name
});
var fabricQuery = (from t in db.tblFabrics
select new fabricList()
{
id = t.id,
name = t.name
});
if (queryList.Count > 0)
{
var item = queryList[0];
item.styleList = styleQuery;
item.fabricList = fabricQuery;
}
I'll have one profileDetailModel with multiple items in styleList and in fabricList. EG.
ProfileDetailModel
Data: Pants
styleList: Bell Bottom, Straight Leg, Boot fit
fabricList: jean-blue, jean-black, plaid
All three above models are tables in my db. I could issue 3 separate queries to read the data then assemble after the fact. But is there a way I can do a linq query to include the two arrays in the main query in one shot?
Try this:
var newQuery = (from p in db.tblProfiles
select p)
.AsEnumerable()
.Select(x => new ProfileDetailModel()
{
id = x.id,
name = x.name,
styleList = styleQuery,
fabricList = fabricQuery
});

Orderby a string column in linq causes an error

I have a query like this :
List<PresentClass.userpresentation> q =
(dbconnect.tblUsers.Where(
i => i.permission == permission)
.Select(arg => new PresentClass.userpresentation {
email = arg.email, pass = arg.password,
name = arg.name+" "+arg.family })).ToList();
After adding an orderby :
List<PresentClass.userpresentation> q =
(dbconnect.tblUsers.Where(
i => i.permission == permission)
.Select(arg => new PresentClass.userpresentation {
email = arg.email, pass = arg.password,
name = arg.name+" "+arg.family })).OrderBy(i=>i.family).ToList();
I got this error :
The member
'Novitiate.AdminPortal.PresentationClass.PresentClass+userpresentation.family'
has no supported translation to SQL.
My class:
public class userpresentation
{
public string username { set; get; }
public string email { set; get; }
public string family { set; get; }
public string name { set; get; }
public string pass{ set; get; }
}
Why?
It looks like it's trying to translate the OrderBy() into a SQL statement on your projection.
Try adding the OrderBy() before Select() if you want the database to do the ordering, or after the ToList() if you want to do the ordering once the collection has been loaded.
var q = (dbconnect.tblUsers.Where(i => i.permission == permission)
.OrderBy(i=>i.family)
.Select(arg => new PresentClass.userpresentation {
email = arg.email,
pass = arg.password,
name = arg.name+" "+arg.family
})).ToList();

Categories

Resources