Get field descriptions from a parent related table in a gridview - c#

What would be the best way to set a gridView.DataSource for LINQ query with some foreign keys and get fields in the parent tables? Like this:
The table BOOK have a Author_Id, which is related to table Author
class:
public IQueryable<Book> ListAll()
{
RENDBDataContext db = new RENDBDataContext();
var result = from b in db.Books
orderby b.Id descending
select b;
}
code-behind:
grdBooks.DataSource = vBooks.ListAll();
grdBooks.DataBind();
In the ASPX page I can get to the Author name with the [asp:TemplateField], using <%Eval("Author.Name")%>
What I'm looking for is a better solution, that doesn't involve changes in the aspx page

var result = from b in db.Books
orderby b.Id descending
select
{
AuthorName = b.Author.Name,
Title = b.Title,
etc = b.etc
};
Alternately, if you don't want to re-list every property you are going to use you could use:
var result = from b in db.Books
orderby b.Id descending
select
{
AuthorName = b.Author.Name,
Book = b
};
But then you'd have to write "Book.Title" in you r GridView. However, either way, it will get all the field you need in a single SQL statement.

Related

LINQ select new model without need of typing every parameter

is it possible in LINQ to get all properties from two tables into new ViewModel without listing PersonId, Email and so on.
var person = (from o in db.Persons
join od in db.OsobeDetails on o.PersonId equals od.PersonId
where o.PersonId == id
select new PersonViewModel
{
PersonId= o.PersonId,
Email = o.Email
}).ToList();
something like:
... select new PersonViewModel();
I tried this but I don't get any results back. "id" is input parameter into method.
You could have an override of your PersonViewModel constructor that takes a Person object, and set the properties there.
For example, if you had this constructor:
class PersonViewModel
{
public PersonViewModel(Person person)
{
this.PersonId = person.PersonId;
this.Email = person.Email;
// ... other properties set here
}
}
Then you could just do:
var person = (from person in db.Persons
join od in db.OsobeDetails
on person.PersonId equals od.PersonId
where person.PersonId == id
select new PersonViewModel(person))
.ToList();
This returns both entities o and od:
var person = (from o in db.Persons
join od in db.OsobeDetails on o.PersonId equals od.PersonId
where o.PersonId == id
select new { Persons = o, OsebeDetails = od }).ToList();
access it something like:
person.Persons.PersonID
or
person.OsebeDetails.PersonID
or any other existing property.

Referencing a row from another table c# linq

I want to use an id from one table to list a title from another table in wpf, so i did this:
var q = from a in context.associations
select a;
associations = q.ToList();
associationViewSource.Source = associations;
foreach (var item in q)
{
var qTitles = from b in context.textbooks
where b.Id == item.book_id
select b.Title;
assocListView.ItemsSource = qTitles.ToList();
}
in the first portion of the code i am making the main body of the information, it lists all the information from associations table, after that i want to list the relevant titles from textbooks table, thats where i add items to the assocListview, but it of course fails and the data isn't displayed, no errors are thrown either. i hope i was clear enough.
Please help
You can get a list of Titles that have associations with a join...
var qTitles = context.textbooks
.Join(context.associations,
b => b.Id,
a => a.book_id,
b => b.Title)
.ToList();
The intention of the code isn't clear though because assocListView.ItemsSource is assigned to in each iteration of the loop. Is that a bug?
Since you are setting assocListView.ItemsSource each pass through the loop, the resultant list will have the results of the last query.
If you want a list of all associated titles you can get it with a single query:
var titles =
from a in context.associations
join b in context.textbooks on a.book_id equals b.Id
select b.Title;
assocListView.ItemsSource = titles.ToList();
This will return every title linked to any record in your associations table, in no particular order, with duplicates, etc. Normally it makes sense to extract a little more information to make it more usable. For instance, define a structure to hold an associated title:
public struct AssocTitle
{
public int AssocID;
public int BookID;
public string Title;
}
Then query it like:
var titles =
from a in context.associations
join b in context.textbooks on a.book_id equals b.Id
select new AssocTitle { AssocID = a.Id, BookID = b.Id, Title = b.Title };
Then when you click on things in the list view you can find out which book and which association, even if you have lots of associations with the same titles.

Linq to Entity Join and Group By

I am new to Linq to Entity and here is my test scenario:
There are Users
Users have Photo Albums. An album belongs to only one User
Albums have Photos. A Photo belongs to only one Album
Photos have Tags. A Tag may belong to many Photos
I want to write a method which displays Count and Name of Tags used in Albums of a particular User using Linq.
Here is the method that I wrote and it works fine
public static void TestStatistics(int userId)
{
// select AlbumTitle, TagName, TagCount where UserId = userId
var results = from photo in dbSet.PhotoSet
join album in dbSet.AlbumSet on photo.AlbumId equals album.Id into albumSet
from alb in albumSet
where alb.UserId == userId
join photoTag in dbSet.PhotoTagSet on photo.Id equals photoTag.PhotoId into photoTagSet
from pt in photoTagSet
join tag in dbSet.TagSet on pt.TagId equals tag.Id
group new { alb, tag } by new { alb.Title, tag.Name }
into resultSet
orderby resultSet.Key.Name
select new
{
AlbumTitle = resultSet.Key.Title,
TagName = resultSet.Key.Name,
TagCount = resultSet.Count()
};
foreach (var item in results)
{
Console.WriteLine(item.AlbumTitle + "\t" + item.TagName + "\t" + item.TagCount);
}
}
And this is the standart T-SQL query which does the same
SELECT a.Title AS AlbumTitle, t.Name AS TagName , COUNT(t.Name) AS TagCount
FROM TblPhoto p, TblAlbum a, TblTag t, TblPhotoTag pt
WHERE p.Id = pt.PhotoId AND t.Id = pt.TagId AND p.AlbumId = a.Id AND a.UserId = 1
GROUP BY a.Title, t.Name
ORDER BY t.Name
It is pretty obvious that standard T-SQL query is much simpler than the Linq query.
I know Linq does not supposed to be simpler than T-SQL but this complexity difference makes me think that I am doing something terribly wrong. Besides the SQL query generated by Linq is extremly complex.
Is there any way to make the Linq query simpler?
UPDATE 1:
I made it a little simpler without using joins but using a approach like used in T-SQL. Actually it is now as simple as T-SQL. Still no navigation properties and no relations on db.
var results = from photo in dbSet.PhotoSet
from album in dbSet.AlbumSet
from photoTag in dbSet.PhotoTagSet
from tag in dbSet.TagSet
where photo.AlbumId == album.Id && photo.Id == photoTag.PhotoId &&
tag.Id == photoTag.TagId && album.UserId == userId
group new { album, tag } by new { album.Title, tag.Name } into resultSet
orderby resultSet.Key.Name
select new {
AlbumTitle = resultSet.Key.Title,
TagName = resultSet.Key.Name,
TagCount = resultSet.Count()
};
If every photo has at least one tag , then try
var results = (from r in PhotoTag
where r.Photo.Album.UserID == userId
group r by new { r.Photo.Album.Title, r.Tag.Name } into resultsSet
orderby resultsSet.Key.Name
select new
{
AlbumTitle = resultsSet.Key.Title ,
TagName = resultsSet.Key.Name ,
TagCount = resultsSet.Count()
}
);
First things first, you need to setup foreignkeys in your database then rebuild EF and it will 'know' (i.e. navigation properties) about the relationships, which then allows you to omit all of your joins and use something along the lines of the following:
List<AlbumTag> query = (from ps in dbSet.PhotoSet
where ps.Album.UserId = userId
group new { album, tag } by new { ps.Album.Title, ps.PhotoTag.Tag.Name } into resultSet
orderby resultSet.Key.Name
select new AlbumTag()
{
AlbumTitle = resultSet.Key.Title,
TagName = resultSet.Key.Name,
TagCount = resultSet.Count()
}).ToList();

Linq query with join - help

I am trying to write a slightly complex LINQ to SQL query.
I have a table called
Fruit (FruitID, FieldOne, FieldTwo)
and
FruitChangeHistory (FruitChangeHIstoryID, FruitID, Date)
What I want to do is return the Fruit list, to the view. But the View model will contain an extra field, LastChangeDate. So like: FruitID, FieldOne, FieldTwo, LastChangedDate
I need to work out how to join on the fruitchangehistory with the fruitid, then sort the dates and return only the latest change date.
This is what I have so far:
var list = from p in EntityFramework.Fruits
join h on EntityFramework.FruitChangHistory
on p.FruitID equals h.FruitID
orderby h.LastChangedDate ascending
select new FruitVM
{
FruitID = p.FruitId,
FieldOne = p.FieldOne,
FieldTwo = p.FieldTwo,
LastChangedDate = h.Date
}
but not quite working as planned.
Anyone can help?
Something like this:
var list = from p in EntityFramework.Fruits
select new FruitVM
{
FruitID = p.FruitId,
FieldOne = p.FieldOne,
FieldTwo = p.FieldTwo,
LastChangedDate = (from h in EntityFramework.FruitChangHistory
where p.FruitID == h.FruitID
orderby h.LastChangedDate ascending
select h.LastChangedDate).FirstOrDefault()
}
It sounds like you are using Linq to Entities / Entity Framework and not Linq to Sql.
If your Fruits entity has a navigation property FruitChangeHistories (which it sounds like it should, sinceFruitChangeHistory has a FK to Fruit) you can do:
var list = from p in EntityFramework.Fruits
select new FruitVM()
{
FruitID = p.FruitId,
FieldOne = p.FieldOne,
FieldTwo = p.FieldTwo,
LastChangedDate = FruitChangeHistories.OrderByDescending( x => x.Date)
.FirstOrDefault()
}

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

Categories