I have an example database, it contains tables for films, people and credits. The movie table contains a title and an identifier. The People table contains the name and identifier. The Credits table refers to films for people who have worked on these films in a specific role. The table looks like this:
CREATE TABLE [dbo].[Credits] ( [Id] [int] IDENTITY (1, 1) NOT NULL PRIMARY KEY, [PersonId] [int] NOT NULL FOREIGN KEY REFERENCES People(Id), [MovieId] [int] NOT NULL FOREIGN KEY REFERENCES Movies(Id), [Role] [char] (1) NULL
In this simple example, the [Role] column is a single character, by my agreement, either “A” to indicate that the person was an actor in that particular film, or “D” for the director.
I would like to fulfill a query for a specific person who returns the person’s name, as well as a list of all the films the person worked on and their roles in these films.
If I serialized it for json, it might look like this:
{ "name" : "Clint Eastwood", "movies" : [ { "title": "Unforgiven", "roles": ["actor", "director"] }, { "title": "Sands of Iwo Jima", "roles": ["director"] }, { "title": "Dirty Harry", "roles": ["actor"] }, ... ] }
How can I write a LINQ-to-SQL query that generates this output?
I have problems with efficiency.
Try # 1
if i use this query:
int personId = 10007; var persons = from p in db.People where p.Id == personId select new { name = p.Name, movies = (from m in db.Movies join c in db.Credits on m.Id equals c.MovieId where (c.PersonId == personId) select new { title = m.Title, role = (c.Role=="D"?"director":"actor") }) };
I get something like this:
{ "name" : "Clint Eastwood", "movies" : [ { "title": "Unforgiven", "role": "actor" }, { "title": "Unforgiven", "role": "director" }, { "title": "Sands of Iwo Jima", "role": "director" }, { "title": "Dirty Harry", "role": "actor" }, ... ] }
This is not entirely correct. As you can see there is a duplicate of each film, for which Eastwood played several roles. I would expect, because in the credit table there are several rows for this film + person combination, one for each role.
Try # 2
I thought I would use a group, for example:
var persons = from p in db.People where p.Id == personId select new { name = p.Name, movies = (from m in db.Movies join c in db.Credits on m.Id equals c.MovieId where (c.PersonId == personId) orderby m.Year group ((c.Role == "A")? "actor":"director") by m.Id into g select new {roles = g }) };
The result is pretty close to what I want. It looks like this:
{ "name" : "Clint Eastwood", "movies" : [ { "roles": ["actor", "director"]}, { "roles": ["director"]}, { "roles": ["actor"]}, ... ] }
This is close, but, of course, I have no movie titles.
Try # 3
If I use a group and include the name of the movie, for example:
var persons = from p in db.People where p.Id == personId select new { name = p.Name, movies = (from m in db.Movies join c in db.Credits on m.Id equals c.MovieId where (c.PersonId == personId) orderby m.Year group ((c.Role == "A")? "actor":"director") by m.Id into g select new { title = m.Title, roles = g }) };
... then it will not compile due
error CS0103: name "m" does not exist in the current context
How can I form the output the way I want?