How should I write this Linq to Entity query? - c#

I'm new to Linq to Entity stuff, so I don't know if what I'm doing is the best approach.
When I do a query like this it compiles, but throws an error that it doesn't recognize the method GetItemSummaries. Looking it up, this seems to be because it doesn't like a custom method inside the query.
return (from c in _entity.Category
from i in c.Items
orderby c.Id, i.Id descending
select new CategoryDto
{
Id = c.Id,
Name = c.Name,
Items = GetItemSummaries(c)
}).ToList();
private IEnumerable<ItemSummary> GetItemSummaries(CategoryDto c)
{
return (from i in c.Items
select new ItemSummary
{
// Assignment stuff
}).ToList();
}
How would I combine this into a single query since I can't call a custom method?
I tried just replacing the method call with the actual query, but then that complains that ItemSummary isn't recognized instead of complaining that the method name isn't recognized. Is there any way to do this? (Or a better way?)

You should be able to do the following:
return (
from c in _entity.Category
orderby c.Id descending
select new CategoryDto
{
Id = c.Id,
Name = c.Name,
Items = (
from i in c.Items
order by i.Id descending
select new ItemSummary
{
// Assignment stuff
}
)
}).ToList();
It's just a matter of making sure that ItemSummary is public so that it's visible to the query.
If it's just a Dto though, you could use an anon type, eg:
from i in c.Items
order by i.Id descending
select new
{
Id = i.Id,
Name = i.Name
}
This creates a type with the 'Id' and 'Name' properties. All depends what your consuming code needs :)

Try making the GetItemSummaries method an extension method on the Category class
private static IEnumerable<ItemSummary> GetItemSummaries(this Category c)
and then call it with
select new Category
{
Id = c.Id,
Name = c.Name,
Items = c.GetItemSummaries()
}).ToList();
Marc

Related

returning multiple class model data from linq in list via return method in C#

I have LINQ output which I am trying to pass in list but I am getting following error
in linq result I am trying to pass data from two class model, if I do one class model (listOfCoursesWithoutURL ) then it work but I need to pass processedCourseInstance. I have created ModelView of two classes but not sure what I am missing in this picture
ViewModel
public class CoursesInstanceStudyLevel_ViewModel
{
public CourseInstanceModel _CourseInstanceModel { get; set; }
public StudyLevelModel _StudyLevelModel { get; set; }
}
My Class
public List<CoursesInstanceStudyLevel_ViewModel> ProcessAllCoursesApplicationURL(CourseApplicationsURLFeed_Model _obj)
{
using(var _uof = new Courses_UnitOfWork())
{
_uof.CourseInstances_Repository.GetAll();
var _listOfCoursesWithoutURL = (from b in ListOfCoursesInstances
where b.ApplicationURL == null
select b).ToList();
var processedCourseInstance = (from _courseInstances in _uof.CourseInstances_Repository.GetAll()
join _courses in _uof.Courses_Repository.GetAll() on _courseInstances.CourseID equals _courses.CourseID
join _studylevel in _uof.StudyLevel_Repository.GetAll() on _courses.StudyLevelId equals _studylevel.StudyLevelID
orderby _courseInstances.CourseCode
select new { _courseInstances, _studylevel }).ToList();
return processedCourseInstance; // it doesn't work ... refer to screen shot
// return _listOfCoursesWithoutURL //it works
}
}
Error
here:
select new { _courseInstances, _studylevel })
you are defining an anonymous object. You have a type ready, so use that one:
select new CoursesInstanceStudyLevel_ViewModel
{
_CourseInstanceModel = _courseInstances,
_StudyLevelModel = _studylevel
}
assuming CourseInstanceModel and StudyLevelModel are the correct types
With highlighted line in following code snippet, you are selecting an anonymous object instead of a concrete CourseIntaceStudyLeve_ViewModel
select new { _courseInstances, _studylevel }
You will have to change your query to following..
var processedCourseInstance = (from _courseInstances in _uof.CourseInstances_Repository.GetAll()
join _courses in _uof.Courses_Repository.GetAll() on _courseInstances.CourseID equals _courses.CourseID
join _studylevel in _uof.StudyLevel_Repository.GetAll() on _courses.StudyLevelId equals _studylevel.StudyLevelID
orderby _courseInstances.CourseCode
select new CoursesInstanceStudyLevel_ViewModel(){
_CourseInstanceModel = _courseInstances.FirstOrDefault(),
StudyLevelModel = _studylevel.FirstOrDefault()}).ToList();
I have assumed you would need only first course and first study level based on your view model definition and there for applied FirstOrDefault. You can choose to go along with this or change your view model definition.
here is my answer and it works
var processedCourseInstance =
(from _courseInstances in _uof.CourseInstances_Repository.GetAll()
join _courses in _uof.Courses_Repository.GetAll() on _courseInstances.CourseID equals _courses.CourseID
join _studylevel in _uof.StudyLevel_Repository.GetAll() on _courses.StudyLevelId equals _studylevel.StudyLevelID
orderby _courseInstances.CourseCode
select new CoursesInstanceStudyLevel_ViewModel() {
_CourseInstanceModel = _courseInstances,
_StudyLevelModel = _studylevel
}).ToList();

How to add to linq2sql entity set for master detail if I can't explicitly define the detail?

Maybe I'm going about this the wrong way...
I have an Order table and an OrderItem table. I create a new Order using linq2sql generated classes.
I then attempt to get all orderable items out of my database using a query that goes after various tables.
I try to then create a new list of OrderItem from that query, but it squawks that I can't explicitly create the object.
Explicit construction of entity type OrderItem in query is not allowed.
Here is the query:
return (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new OrderItem()
{
OrderItemId = im.ItemId
});
The idea is to populate the database with all orderable items when a new order is created, and then display them in a grid for updates. I'm taking the results of that query and attempting to use AddRange on Order.OrderItems
Is there a proper strategy for accomplishing this using linq2sql?
Thanks in advance for your help.
From my understanding of L2S, I don't think you can use explicit construction (in other words new SomeObj() { ... }) in a query because you aren't enumerating the results yet. In other words, the query has just been built, so how are you supposed to do this:
SELECT new OrderItem() FROM MasterItems im JOIN Categories c on c.CATEGORY1 = im.CATEGORY
This is what you're trying to do, which doesn't work because you can't return a POCO (unless you join the OrderItem back somehow and do OrderItem.* somewhere). Ultimately, what you would have to do is just enumerate the collection (either in a foreach loop or by calling ToList()) on the query first and then build your OrderItem objects.
var query = (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new { MasterItem = im, Category = c});
List<OrderItem> returnItems = new List<OrderItem>();
foreach(var item in query)
{
returnItems.Add(new OrderItem() { OrderItemId = item.MasterItem.ItemId });
}
return returnItems;
OR
return (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new { MasterItem = im, Category = c})
.ToList()
.Select(tr => new OrderItem() { OrderItemId = tr.MasterItem.ItemId });
Try that out and let me know if that helps.
Expand the order class by creating a partial file where that class OrderItem now has property(ies) which lend itself to business logic needs, but don't need to be saved off to the database.
public partial class OrderItem
{
public int JoinedOrderItemId { get; set; }
public bool HasBeenProcessed { get; set; }
}

LINQ and the KeyValuePairs

I'm trying to count items in a group. So I have this LINQ to Entities query:
var qry2 = from c in qry
group c by c.Content.DownloadType into grouped
select new KeyValuePair(grouped.Key,grouped.Count());
But it doesn't work because LINQ to Entities only accepts parameter initializers or parameterless constructors. So I created a simple class to envelop the KeyValuePair type:
public class ValueCount
{
public string Key { get; set; }
public int Value { get; set; }
public KeyValuePair<string, int> ToKeyValuePair()
{
return new KeyValuePair<string, int>(this.Key, this.Value);
}
}
And changed the query to:
var qry2 = from c in qry
group c by c.Content.DownloadType into grouped
select new ValueCount
{
Key = grouped.Key,
Value = grouped.Count()
}.ToKeyValuePair();
But still doesn't work. It says that it doesn't recognizes the method ToKeyValuePair()
How can I collect KeyValuePairs from a LINQ to Entities query?
You have to call your method once you have the results back from the db and you can do that by forcing the query using ToList() and then doing a select to call your method on each item.
(from c in qry
group c by c.Content.DownloadType into grouped
select new ValueCount
{
Key = grouped.Key,
Value = grouped.Count()
}).ToList().Select(x=>x.ToKeyValuePair());
Like Eric rightly says in the comments you can get rid of your custom class and do something like
(from c in qry
group c by c.Content.DownloadType into grouped
select new
{
Key = grouped.Key,
Value = grouped.Count()
}).ToList().Select(x=>new KeyValuePair<string, int>(x.Key, x.Value));
Try adding AsEnumerable() to isolate your code from EF's:
var qry2 = from c in qry
group c by c.Content.DownloadType into grouped
select new ValueCount
{
Key = grouped.Key,
Value = grouped.Count()
}.AsEnumerable() // This "cuts off" your method from the Entity Framework,
.Select(vc => vc.ToKeyValuePair()); // letting you nicely complete the conversion in memory

LINQ to Entity Framework many to many with list of strings

This question is continuation from:
Can't think of query that solves this many to many
This LINQ query is recommended to me by user #juharr, i just added string concatenation in purpose of grouping first and last name into full name.
var courseViews = from c in db.Courses
select new CourseView()
{
CourseID = c.ID,
ProfessorName = (from l in c.Leturers
where l.Is_Professor
select l.LastName+" "+l.FirstName).FirstOrDefault(),
AssistantNames = (from l in c.Leturers
where !l.Is_Professor
select l.LastName+" "+l.FirstName)
.ToList() //hmmm problem
};
ModelView that i used is another possible cause of problems:
public class CourseView
{
public int ID { get; set; }
public string CourseName { get; set; }
public string ProfessorName { get; set; }
public List AssistantNames { get; set; }
}
Hmm List of strings for Assistant names problematic isn't it?
At the end of my stupidity, in View i looped through this list with #foreach(var s in item.AssistantNames){#s}
#Ladislav suggested using IQueryable instead of string,how where?
For solution that i made so far i get following error:
LINQ to Entities does not recognize the method 'System.Collections.Generic.List1[System.String] ToList[String](System.Collections.Generic.IEnumerable1[System.String])' method, and this method cannot be translated into a store expression.
Need help!
Drop the ToList() call and then change the Assistants property to this:
public IQueryable AssistantNames { get; set; }
See if this works
var courseViews = from c in db.Courses
let assistantsList = (from l in c.Leturers
where !l.Is_Professor
select l.LastName+" "+l.FirstName).ToList()
select new CourseView()
{
CourseID = c.ID,
ProfessorName = (from l in c.Leturers
where l.Is_Professor
select l.LastName+" "+l.FirstName).FirstOrDefault(),
AssistantNames = assistantsList
};
Another approach would be, since you are materializing all courses anyway to split the query in two parts, the first one materializes the data, the second one uses it to create the course views (this is now a Linq to Objects query):
var courses = (from c in db.Courses
select new { c.ID, Leturers = c.Leturers.ToList() }).ToList();
var courseViews = from c in courses
select new CourseView()
{
CourseID = c.ID,
ProfessorName = (from l in c.Leturers
where l.Is_Professor
select l.LastName+" "+l.FirstName).FirstOrDefault(),
AssistantNames = (from l in c.Leturers
where !l.Is_Professor
select l.LastName+" "+l.FirstName)
.ToList()
};

Problem with order by in LINQ

I'm passing from the controller an array generated by the next code:
public ActionResult GetClasses(bool ajax, string kingdom)
{
int _kingdom = _taxon.getKingdom(kingdom);
var query = (from c in vwAnimalsTaxon.All()
orderby c.ClaName
select new { taxRecID = c.ClaRecID, taxName = c.ClaName }).Distinct();
return Json(query, JsonRequestBehavior.AllowGet);
}
The query List should be ordered, but it doesn't work, I get the names of the classes ordered wrong in the array, because I've seen it debugging that the names are not ordered.The view is just a dropdownbox loaded automatically, so I'm almost sure the problem is with the action. Do you see anything wrong?Am I missing something?
I think gmcalab is almost there. The reason it's not working is that Distinct blows away the ordering. So you need Distinct THEN OrderBy. But this means you have to sort by the new attribute name:
var query = (from c in vwAnimalsTaxon.All()
select new { taxRecID = c.ClaRecID, taxName = c.ClaName }
).Distinct().OrderBy(t => t.taxName);
Give this a try:
var query = (from c in vwAnimalsTaxon.All()
select new { taxRecID = c.ClaRecID, taxName = c.ClaName }
).Distinct().OrdeyBy(c => c.ClaName);
In LINQ the Distinct method makes no guarantees about the order of results. In many cases the Distinct causes the OrderBy method to get optimized away. So it's necessary to do the OrderBy last, after the Distinct.
var query = (from c in vwAnimalsTaxon.All()
select new { taxRecID = c.ClaRecID, taxName = c.ClaName })
.Distinct()
.OrderBy(c => c.ClaName);
The select will also blow away the sorting. So either Distinct or Select needs orderby after.

Categories