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

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

Related

SelectListItem is empty, if statement inside method shows it to be filled

error message:
http://prntscr.com/qtlodf
method:
public IActionResult GroepsResultaten(int vakId, int groepId)
{
var studentenLijst = _context.Student.Join(_context.StudentGroep,
s => s.Id,
sg => sg.StudentId,
(s, sg) => new { Student = s, StudentGroep = sg })
.Where(x => x.StudentGroep.GroepId == groepId)
.Select(x => x.Student);
ViewBag.Studenten = new SelectList(studentenLijst, "Id", "Naam");
return View();
}
I've also tried this:
public IActionResult GroepsResultaten(int vakId, int groepId)
{
var studentInfo = _context.Student
.Select(s =>
new
{
s.Id,
Naam = string.IsNullOrEmpty(s.Tussenvoegsel)
? s.Voornaam + " " + s.Achternaam + " - " + s.Studentnummer
: s.Voornaam + " " + s.Tussenvoegsel + " " + s.Achternaam + " - " + s.Studentnummer,
forStudent = s.Studentnummer + "-" + s.Achternaam
});
ViewBag.Studenten = new SelectList(studentInfo, "Id", "Naam");
return View();
}
I'm a bit stuck at this. I want to return multiple input fields (I'm just testing with selectlist at the moment) for all students of group x, from there on I want to be able to grade students for the subject that's included in the view using get method. Because English isn't my first language I've included two screenshots to clarify what I mean.
clarification of what I want to achieve:
group view: http://prntscr.com/qtlrqd
wireframe of method view: http://prntscr.com/qtlswn
models:
public class Student
{
public int Id { get; set; }
[Required]
public string Voornaam { get; set; }
[Required]
public string Achternaam { get; set; }
public string Tussenvoegsel { get; set; }
public string Studentnummer { get; set; }
public List<Resultaat> Resultaten { get; set; }
public List<StudentGroep> Groepen { get; set; }
}
public class Groep
{
public int Id { get; set; }
[Required]
public string Naam { get; set; }
[Required]
public string Groepscode { get; set; }
public List<GroepVak> Vakken { get; set; }
public List<StudentGroep> Studenten { get; set; }
}
public class StudentGroep
{
public Student Student { get; set; }
public int StudentId { get; set; }
public Groep Groep { get; set; }
public int GroepId { get; set; }
}
I hope I've included enough information, I'm available on discord too if that makes it easier.
The problem is what the SelectList class returns. Because view side results that ViewBag.Studenten is null.
Also, you must make sure that the database query returns a value.
Using ViewData resulted in what I want, from here on I can hopefully figure out how to use it for posting grades for each student.
Method:
public IActionResult GroepsResultaten(int vakId, int groepId)
{
var studentenLijst = _context.Student.Join(_context.StudentGroep,
s => s.Id,
sg => sg.StudentId,
(s, sg) => new { Student = s, StudentGroep = sg })
.Where(x => x.StudentGroep.GroepId == groepId)
.Select(x => x.Student)
.ToList();
if (groepId >= 1)
{
ViewData["Studenten"] = studentenLijst.ToList();
}
//ViewBag.Studenten = new SelectList(studentenLijst, "Id", "Naam");
return View();
}
View:
#foreach (var item in ViewBag.Studenten)
{
#item.Voornaam;
<input type="number" />
}

How to get the newly created object inside the .Select() in a LINQ Query

How can I get a reference to the 'parent' object in the Linq below. Something like the way EF does it when you query for objects that are of EF Classes?
void Main()
{
IEnumerable<SomeModel> Brands = ....;
var list = Brands
.Select(b => new BrandModel()
{
ID = b.ID,
BrandName = b.Name,
Locations = b.Locations.Select(l => new LocationModel()
{
ID = l.ID,
LocationName = l.Name,
Brand = *here I would want the Brand object of this Location*
}).ToList()
}).ToList();
}
private class BrandModel
{
public int ID { get; set; }
public string BrandName { get; set; }
public List<LocationModel> Locations { get; set; }
}
private class LocationModel
{
public int ID { get; set; }
public string LocationName { get; set; }
public BrandModel Brand { get; set; }
}
You can create your BrandModel in two steps. First create it without locations, then set locations to it
To do so you need to convert your lambda b => new BrandModel() to block of statements b => { return new BrandModel() }. Try this code:
.Select(b =>
{
var model = new BrandModel
{
ID = b.ID,
BrandName = b.Name
};
model.Locations = b.Locations.Select(l => new LocationModel
{
Brand = model
}).ToList();
return model;
});

How can I find matching values from two tables?

I have a question about how to find collections of values from one table that match some values from another table.
Here's my code snippet:
public async Task<List<TableB>> GetTableBResults(string vCode, string number)
{
var tableARepo = DependencyResolver.Get<IRepository<DBTableA>>();
var TableBRepo = DependencyResolver.Get<IRepository<DBTableB>>();
var tableAQuery = string.Format("SELECT * FROM DBTableA WHERE Identifier = '{0}'",
number);
List<DBTableA> tableA = await tableARepo.QueryAsync(tableAQuery);
if (tableA != null)
{
//Find all tableB records with info from Identifier
//And then do a distinct on BusinessName and return those results
foreach (var item in DBTableA)
{
var TableBQuery = String.Format("SELECT *" +
"FROM[DBTableB] INNER JOIN DBTableA" +
"ON DBTableB.Code = {0}" +
"AND DBTableB.HouseNo = {1}" +
"AND DBTableB.BusinessName = {2}" +
"AND DBTableB.VCode = {3}",
item.Code, item.HouseNo, item.FirstName, vCode);
List<DBTableB> tableB = await TableBRepo.QueryAsync(TableBQuery);
if (tableBs != null)
{
return tableBs.Select(_ => new TableB
{
BoroCode = _.BoroCode,
Code = _.Code,
HouseNo = _.HouseNo,
Date = _.Date,
BusinessName = _.BusinessName,
}).ToList();
}
else
{
return new List<TableB>();
}
}
}
return new List<TableB>();
}
Here are the entities:
public class DBTableA
{
[PrimaryKey, AutoIncrement]
public int DBTableAKey { get; set; }
[NotNull]
[Indexed]
public Int64 Identifier { get; set; }
[NotNull]
public int Code { get; set; }
[Indexed]
public int? HouseNo { get; set; }
public string FirstName { get; set; }
}
public class DBTableB
{
[PrimaryKey, AutoIncrement]
public int DBTableBKey { get; set; }
[NotNull]
[Indexed]
public string BoroCode { get; set; }
[NotNull]
[Indexed]
public int Code { get; set; }
[NotNull]
[Indexed]
public string HouseNo { get; set; }
[NotNull]
public DateTime Date { get; set; }
[NotNull]
public string BusinessName { get; set; }
[NotNull]
[Indexed]
public string VCode { get; set; }
}
Basically, using Identifier from TableA, I want to get all matches rows from Table A where Identifier is equal to the number passed in. Then, do a compare where select fields from that tableA set match same values in TableB set
but I'm not sure how to set up the logic, I added my attempt above.
I mean I just want to return the values from Table B that matches the parameters I want to check/match in the sql query above if any. How can I improve my code above to make return the correct values?
EDIT:
Here is some mock data:
Identifier: 123 (only in TableA: grab Code, HouseNo, FirstName that have Identifier 123)
Then match those from Table A with values from Table B where the values are the same like the sample values below:
Code = 456
HouseNo = 34
BusinessName = 'Bar, Foo'
VCode = 'E4T' (passed in to method)
Return TableB row(s) that match the above info.
You should just be able to use LINQ to handle this query:
var ans = (from a in tableARepo
where a.Identifier == number
join b in tableBRepo on new { a.Code, HouseNo = a.HouseNo.ToString(), a.FirstName, VCode = vCode } equals new { b.Code, b.HouseNo, FirstName = b.BusinessName, b.VCode }
select b).ToList();
return ans;

Expression-tree to build sub-select results

I'm trying to build a sub-query by using expression-trees. In linq I would write something like:
var single = MyTable
.AsExpandable()
.Select(c => new
{
Childs = Enumerable.Select(
MyTable.VisibleChilds.Invoke(c, dbContext),
cc => Convert(cfg.ChildsConfig).Invoke(dbContext, cc))
});
where the Convert is building an expression like
p => new MyTableSelect {
Id = p.Id,
Name = p.Name
}
depending on the given values from the config (to only read needed data from database).
but I'm struggeling with the second parameter to be passed to the Select call as I need cc to be passed to the Convert-call.
I guess I need something like Expression.Lambda<Func<>> but I don't see it.
Expression.Lambda>(Expression.Invoke(Instance.Convert(cfg.ChildOrganizersFilterConfig), ContextParameter, theEntity));
I am not familiar with your use of Invoke but if you just want to run a 'Converter' in a fluent syntax for use in a Linq Expression I could show you an example of that. Say I have three POCO classes, one parent container, a child container, and a container I want to convert to.
public class POC
{
public int Id { get; set; }
public string Name { get; set; }
public POC(int id, string name)
{
Id = id;
Name = name;
}
}
public class ChildPOC
{
public int ParentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ChildPOC(int parentId, string firstName, string lastName)
{
ParentId = parentId;
FirstName = firstName;
LastName = lastName;
}
}
public class ChildPOCAlter
{
public int ParentId { get; set; }
public string Name { get; set; }
public ChildPOCAlter(string first, string last, int parentId)
{
ParentId = parentId;
Name = $"{first} {last}";
}
}
I could write a converter method to take ChildPOC to ChildPOCAlter like so:
public static Converter<ChildPOC, ChildPOCAlter> ChildPOCOAlter()
{
return new Converter<ChildPOC, ChildPOCAlter>((x) => { return new ChildPOCAlter(x.FirstName, x.LastName, x.ParentId); });
}
I could then populate some data:
var someParents = new List<POC> { new POC(1, "A"), new POC(2, "B") };
var somechildren = new List<ChildPOC> { new ChildPOC(1, "Brett", "x"), new ChildPOC(1, "Emily", "X"), new ChildPOC(2, "John", "Y") };
And then I may want to take these relationships and apply a converter directly on it:
var relationships = someParents.Select(x => new
{
Id = x.Id,
Name = x.Name,
Children = somechildren.Where(y => y.ParentId == x.Id).ToList().ConvertAll(ChildPOCOAlter())
});

LINQ Querying list in list

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.

Categories

Resources