Asp.net 5 WebApi 2.0 partial response - c#

I'm working on LinQ to Entity,
Here is my working code:
var products = from p in AppContext.Products
join ... //joins tables
where ... //conditions
select p;
var result = products.OrderBy(p => p.Name);
//Move it to custom format.
return result.Select(p => new
{
p.Id,
p.Name,
p.Description,
...
/*This looks ugly and need to be moved out to somewhere else*/
Categories = FindCategories(p.Id, true),
MetaData = AppContext.ProductMetas.Where(pm => pm.ProductId == p.Id),
Photos = AppContext.ProductPhotos.Where(pp => pp.ProductId == p.Id)
}).Skip(skip).Take(take);
However, I would like to move 'return new...' part into somewhere else (better inject asp.net api handler when transform object to json). For example:
//GetAll()
...
var products = from ... in ... select p;
return products;
//How to register some handler
var results = new List<object>();
foreach (var product in resultSet) {
//merge objects into result set
results.Add(new {/*Merge properties together*/})
}
But I don't know how to do this. Anyone familiar with this, please help.

I’m not quite clear on whether you need assistance with the actual mapping of the object in Linq to Entities or how to add this logic to an ASP.NET 5 route, I am assuming it is the latter.
One approach would be to create a DTO, that will contain the entity and relation data you require, like so:
public class ProductDTO
{
public int Id {get;set;}
public string Name {get;set;}
public List<Category> Categories {get;set;}
...
}
You will then need to create a controller to handle the request:
[Route("api/[controller]")]
public class ProductController : Controller
{
[HttpGet]
public IActionResult GetAll()
{
var products = //..make call to service or query to get products..
var results = new List<ProductDTO>();
foreach (var product in products)
{
// merge objects into new ProductDTO collection
results.Add(a=> new ProductDTO() {Id = a.Id, Name = a.Name ...});
}
return new JsonResult(values);
}
}

Related

ASP.NET Core web api serialize an object's List type property

After the server returned the object, the List type field went missing.
Each EmployeeViewModel has a list of EmployeeContactViewModel objects. The intended logic is, after querying the employee object from database, populate the view model in a method, including the list, and return to client.
The view model:
public class EmployeeViewModel
{
public List<EmployeeContactViewModel> EmployeeContacts;
public EmployeeViewModel()
{
EmployeeContacts = new List<EmployeeContactViewModel>();
}
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
// more fields
}
The method to populate view models:
public EmployeeViewModel GetViewModelFromEmpObject()
{
var vm = new EmployeeViewModel();
var contact1 = this.CONTACTs.Where(e => e.ContactId == 1).FirstOrDefault();
if (contact1 != null)
{
var contactVm1 = new EmployeeContactViewModel();
CopyContactFields(contact1, contactVm1); // method to populate view-model
vm.EmployeeContacts.Add(contactVm1);
}
else
vm.EmployeeContacts.Add(new EmployeeContactViewModel());
var contact2 = this.CONTACTs.Where(e => e.ContactId == 2).FirstOrDefault();
if (contact2 != null)
{
var contactVm2 = new EmployeeContactViewModel();
CopyContactFields(contact1, contactVm2); // method to populate view-model
vm.EmployeeContacts.Add(contactVm2);
}
else
vm.EmployeeContacts.Add(new EmployeeContactViewModel());
// more lines below
}
The api controller method is below. By setting a breakpoint at the return line, I could see the EmployeeContactViewModel list was correctly created.
[HttpGet("{empid:string}")]
public async Task<EmployeeViewModel> GetAsync(string empid)
{
Employee emp = await _context.Employees
.Include(c => c.CONTACTs)
.Where(c => c.EmployeeId == empid)
.FirstOrDefaultAsync();
var viewModel = emp.GetViewModelFromEmpObject();
return viewModel;
}
However on the client side, although all the other fields (EmployeeId, EmployeeName, etc.) were present in the response, there was nothing for EmployeeContacts list, when inspecting the server response in the browser network tab.
I tried to search before asking but using "Serialize" as a keyword would just get topics on how to do serialization. Any advice is appreciated.
Do you mean that EmployeeContacts is not included in the response result?
You can install Microsoft.AspNetCore.Mvc.NewtonsoftJson, and add this line in Program.cs:
builder.Services.AddMvc().AddNewtonsoftJson(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});

LINQ Where query with object list

I have a list of objects ListA with property Id and I have to make a query in a table that has a column Id and find the rows that the ids are the same. How exactly can I achieve that with a single query and not a foreach loop of listA?
Thank you for your time
foreach(var object in listA)
{
context.Table.Where(x => x.id == object.Id)....
}
Looks like you want to return all rows from the table that have an ID contained in the list of objects with the same ID. The following will achieve this. I can modify my answer to suit your need. Just let me know if you are looking for something slightly different.
void Main()
{
var listA = new List<A> { new A { Id = 1 }, new A { Id = 4 } };
var results = context.Table
.Where(t => listA.Select(l => l.Id).Contains(t.Id))
}
public class A
{
public int Id { get; set; }
}

ASP.NET Core web application - display database data C#

I'm trying to display data from a SQL Server database. I've been struggling with it for a whole day now and still can't find any working solution or tutorial.
What I want to do - make a simple "database browser". The best thing that worked so far was this tutorial https://www.c-sharpcorner.com/article/entity-framework-database-first-in-asp-net-core2/
But I have only one table to display and I don't know how to write this part of code:
public IActionResult Index()
{
var _emplst = _dbContext.tblEmployees
.Join(_dbContext.tblSkills, e => e.SkillID, s => s.SkillID,
(e, s) => new EmployeeViewModel
{ EmployeeID = e.EmployeeID, EmployeeName = e.EmployeeName,
PhoneNumber = e.PhoneNumber, Skill = s.Title,
YearsExperience = e.YearsExperience }).ToList();
IList<EmployeeViewModel> emplst = _emplst;
return View(emplst);
}
for just one table (without any join). Everything I try ends up with an error that I cannot convert tblEmployees to EmployeeViewModel.
Could someone possibly help me? Or suggest any other solution, that might work? I really just want to drag a data from a table and display it on a web page.
EDIT:
ComponentContext.cs:
public class ComponentsContext:DbContext
{
public ComponentsContext(DbContextOptions<ComponentsContext> options) : base(options)
{
}
public DbSet<tblComponents> tblComponent { get; set; }
}
}
Your _emplst list is of a different type (class) than the type (class) EmployeeViewModel.
So you need to go through you list _emplst and transfer the values needed in EmployeeViewModel.
This can be something like this:
public IActionResult Index()
{
var _emplst = _dbContext.tblEmployees.
Join(_dbContext.tblSkills, e => e.SkillID, s => s.SkillID,
(e, s) => new EmployeeViewModel
{ EmployeeID = e.EmployeeID, EmployeeName = e.EmployeeName,
PhoneNumber = e.PhoneNumber, Skill = s.Title,
YearsExperience = e.YearsExperience }).ToList();
var emplst = _emplst.Select( e=> new EmployeeViewModel {
.. i dont known the properties ..
A = e.A,
B = e.B,
... ..
}).ToList();
return View(emplst);
}
As answer to your comment below on the tlbComponent, try this:
var _cmplist = _dbContext.tblComponent.ToList().Select(e => new ComponentsViewModel { ID = e.ID, Name = e.Name, } ).ToList();
IList<ComponentsViewModel> cmplist = _cmplist;
return View(cmplist);
i have change _dbContext.tblComponent.Select(... to _dbContext.tblComponent.ToList().Select(....

Get parent department node in Entity Framework

I have a SQL table like this:
DepartmentID is parent of department. I've build a tree by this table(in ASP.net (C#) project):
Records in tree above is:
I need to get parents in this tree.
I can do it in SQL Server like this(for Example id=2, id is input argument):
with cte1
as
(
select id,name,DepartmentID, 0 AS level
from Department
where id =2
union all
select Department.ID,Department.name,Department.DepartmentID, level+1
from Department
inner join cte1 on Department.ID=cte1.DepartmentID
)
select * from cte1
Output(id=2 (A))
Output(id=4 (A1))
I know EF does not support cte, but I need to get this result in EF.
It would be very helpful if someone could explain solution for this problem.
These posts are similar to your question.please see these:
writing-recursive-cte-using-entity-framework-fluent-syntax-or-inline-syntax
converting-sql-statement-that-contains-with-cte-to-linq
I think there is no way to write a single LINQ to SQL query that could get all However, LINQ supports a method to execute a query (strangly enough called DataContext.ExecuteQuery). Looks like you can use that to call a arbitrary piece of SQL and map it back to LINQ.
See this post:
common-table-expression-in-entityframework
The easiest way I can think of is to map the relationship in EF and then retrieve all departments and then get the root parent from that list. All of them should be loaded in memory and EF will take care of the tree structure with the mapping. Alternatively you can enable lazy loading and just get the parent but then with each child or childset a query will be executed by EF during retrieval.
Model
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public int? DepartmentId { get; set; }
public Department ParentDepartment { get; set; }
public virtual ICollection<Department> ChildDepartments { get; set; }
}
Mapping (using fluent)
public DbSet<Department> Departments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// other mapping code
modelBuilder.Entity<Department>()
.HasOptional(x => x.ParentDepartment)
.WithMany(x => x.ChildDepartments)
.HasForeignKey(x => x.DepartmentId);
// other mapping code
}
Eager retrieval of root parent
using (var context = new YourDbContext())
{
var allDepartments = context.Departments.ToList(); // eagerly return everything
var rootDepartment = allDepartments.Single(x => x.DepartmentId == null);
}
Retrieval of only root parent and then use lazy loading, note that the DbContext needs to be available for Lazy Loading to work and it must also be enabled on the DbContext
using (var context = new YourDbContext())
{
var rootDepartment = context.Departments.Single(x => x.DepartmentId == null);
// do other stuff, as soon as context is disposed you cant lazy load anymore
}
Try one of these,
1-
int _ID = 2; // ID criteria
List<object> result = new List<object>(); // we will use this to split parent at child, it is object type because we need Level
var departments = entites.Departments.Where(x => x.ID == _ID).SelectMany(t => entites.Departments.Where(f => f.ID == t.DepartmentID),
(child, parent) => new { departmentID = child.DepartmentID, Name = child.Name, ID = child.ID, level = 0,
Parent = new { DepartmentID = parent.DepartmentID, Name = parent.Name, ID = parent.ID, level = 1 }});
// first we check our ID (we take A from where criteria), then with selectmany T represents the Department A, we need
// department A's departmentID to find its parent, so another where criteria that checks ID == DepartmentID, so we got T and the new list
// basically child from first where parent from second where, and object created.
// for showing the results
foreach (var item in departments)
{
result.Add(new { DepartmentID = item.departmentID,ID = item.ID, level= item.level,Name = item.Name}); // child added to list
result.Add(new { DepartmentID = item.Parent.DepartmentID, ID = item.Parent.ID, level = item.Parent.level, Name = item.Parent.Name }); // parent added to list
}
Result;
2-
List<object> childParent = new List<object>();
// basically get the child first
Departments child1 = entites.Departments.Where(x => x.ID == _ID).FirstOrDefault();
// find parent with child object
Departments parent1 = entites.Departments.Where(x => x.ID == child1.DepartmentID).FirstOrDefault();
// create child object with level
childParent.Add(new { child1.DepartmentID, child1.ID,child1.Name , level = 0});
// create parent object with level
childParent.Add(new { parent1.DepartmentID,parent1.ID,parent1.Name, level = 1 });
Result (not the same image, check column Header Text);
Edit 1:
3-
Another way, by giving ID as input and assuming that ID column is unique, so there will be always 2 values at the array and by returning list, the index of items actually represent their levels. (won't add results because they are same :)).Btw you can also use Union instead of Concat.
var ress = list.Where(x=> x.ID ==2)
.SelectMany(x=> list.Where(c=> c.ID == x.ID).Concat(list.Where(s => s.ID == x.DepartmentID))).ToList();
DataTable dt = new DataTable();
dt.Columns.Add("DepartmentID");
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Columns.Add("Level");
for (int i = 0; i < ress.Count(); i++)
{
dt.Rows.Add(ress[i].DepartmentID, ress[i].ID, ress[i].Name, i);
}
dataGridView1.DataSource = dt;
Edit 2
There is not cte in linq, basically using view,sp is the first choise but here is a solution, it might be a little push. Anyway it gives the result.
List<Departments> childParent = new List<Departments>();
// or basically get the child first
Departments child1 = entites.Departments.Where(x => x.ID == 7).FirstOrDefault();
// find parent with child object
Departments parent1 = entites.Departments.Where(x => x.ID == child1.DepartmentID).FirstOrDefault();
// create child object with level
Departments dep = new Departments(); // I add to department class a string level field
dep.DepartmentID = child1.DepartmentID;
dep.ID = child1.ID;
dep.Name = child1.Name;
dep.level = 0; // first item
childParent.Add(dep);
// create parent object with level
dep = new Departments();
dep.DepartmentID = parent1.DepartmentID;
dep.ID = parent1.ID;
dep.Name = parent1.Name;
dep.level = 1; // parent one
childParent.Add(dep);
while (childParent.Select(t => t.DepartmentID).Last() != null) // after added to list now we always check the last one if it's departmentID is null, if null we need to stop searching list for another parent
{
int? lastDepID = childParent.Last().DepartmentID; // get last departmentID
Departments tempDep = entites.Departments.Single(x => x.ID == lastDepID); // find as object
tempDep.level = childParent.Last().level + 1; // increase last level
childParent.Add(tempDep); // add to list
}
(Added another C1 to check 4th level)
Hope helps,
Below is the simple console project Program class code.
You can check with different IDs for the input parameter of the GetParentSet method.
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var result= p.GetParentSet(6);
foreach(var a in result)
{
Console.WriteLine(string.Format("{0} {1} {2}",a.ID,a.Name,a.DepartmentId));
}
Console.Read();
}
private List<Department> GetParentSet(int id)
{
List<Department> result = new List<Department>(); //Result set
using (RamzDBEntities context = new RamzDBEntities())
{
var nodeList = context.Departments.Where(t=>t.ID<=id).ToList(); //Get All the the entries where ID is below or greater than the given to the list
var item = nodeList.Where(a => a.ID == id).SingleOrDefault(); //Get the default item for the given ID
result.Add(item); //Add it to the list. This will be the leaf of the tree
int size = nodeList.Count(); //Get the nodes count
for (int i = size; i >= 1;i--)
{
var newItem= nodeList.Where(j => j.ID == item.DepartmentId).SingleOrDefault(); //Get the immediate parent. This can be done by matching the leaf Department ID against the parent ID
if (item!=null && !result.Contains(newItem)) //If the selcted immediate parent item is not null and it is not alreday in the list
{
result.Add(newItem); //Add immediate parent item to the list
}
if (newItem.ID == 1) //If the immediate parent item ID is 1 that means we have reached the root of the tree and no need to iterate any more.
break;
item = newItem; //If the immediate parent item ID is not 1 that means there are more iterations. Se the immediate parent as the leaf and continue the loop to find its parent
}
}
return result; //return the result set
}
}
Code itself is self-explanatory. However below is the explanation. Hope this will help!
First all the entries with ID below or equal to the given ID is
assigned to a List
Then get the leaf of the tree and add it to the list named result. This is the first element of our result set
We iterate through the retrieved entries descending order. Get the immediate parent of the leaf by equating parent's ID to leaf's department ID
If this immediate parent is not null and its not already in the list add it to the list.
Make the immediate parent item as the leaf and continue the loop so that we can get the parent of the immediate parent.
continue this until we reach the root of the tree.
If the immediate parent ID is=1 that means we have reached the root of the tree and we can break the loop.
Since you generated the edmx, you have the code generated for your DbContext and for your Model Classes including Departments like on this screenshot.
You shouldn't modify them because they might (will) get overwritten by EF tools anyway on any model manipulation. Fortunately both classes are generated as partial so the creators thought about people wanting to customize it safely.
Example below is made for simplicity of implementation not for top performance. I assumed that the table containing Departments is not enormously big and the levels of nesting in hierarchy are not enormously deep.
Create a new Class (*.cs file) in your project and extend your auto-generated Departments class by your custom method or property:
using System;
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class Departments
{
public List<Departments> Hierarchy {
get {
List<Departments> retVal = new List<Departments>();
retVal.Add(this);
using (YourAutoGeneratedContext ctx = new YourAutoGeneratedContext())
{
Departments tmp = this;
while(tmp.DepartmentID != null)
{
tmp = ctx.Departments.First(d => d.ID == tmp.DepartmentID);
retVal.Add(tmp);
}
}
return retVal;
}
private set { }
}
}
}
When you extend the partial class, make sure that you put it in the same namespace. In my case I named my project CustomEF and I've placed the edmx file in the EFStuff subfolder so the generator placed the auto generated class in the CustomEF.EFStuff namespace.
The example above will allow you to get the hierarchy for any Departments object e.g.
int level = 0;
foreach(Departments d in someDepartmentObject.Hierarchy)
{
Console.WriteLine(d.ID.ToString() + ", " + d.DepartmentID.ToString() + ", " + d.Name +", " +(level++).ToString());
}
If you also need to get the hierarchy from some code where you have an ID but not the object, you can additionally create another class (*.cs file) where you'll extend the auto-generated context.
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class YourAutoGeneratedContext
{
public List<Departments> GetDepartmentHierarchy(int departmentId)
{
Departments mydep = this.Departments.FirstOrDefault(d => d.ID == departmentId);
if (mydep == null)
{
throw new System.Data.Entity.Core.ObjectNotFoundException("There is no department with ID = " + departmentId.ToString());
}
return mydep.Hierarchy;
}
}
}
Or in this case you might want to move the implementation to the Context class entirely, without extending the Departments class at all (and you wouldn't have to create an additional instance of your context, you'll have the this to use).
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class YourAutoGeneratedContext
{
public List<Departments> GetDepartmentHierarchy(int departmentId)
{
Departments tmp = this.Departments.FirstOrDefault(d => d.ID == departmentId);
if (tmp == null)
{
throw new System.Data.Entity.Core.ObjectNotFoundException("There is no department with ID = " + departmentId.ToString());
}
List<Departments> retVal = new List<Departments>();
retVal.Add(tmp);
while (tmp.DepartmentID != null)
{
tmp = this.Departments.First(d => d.ID == tmp.DepartmentID);
retVal.Add(tmp);
}
return retVal;
}
}
}
As another unsophisticated use example:
YourAutoGeneratedContext ctx = new YourAutoGeneratedContext();
level = 0;
foreach (Departments currentHier in ctx.GetDepartmentHierarchy(10))
{
Console.WriteLine(currentHier.ID.ToString() + ", " + currentHier.DepartmentID.ToString() + ", " + currentHier.Name + ", " + (level++).ToString());
}
I don't know how much you can trust the data in the database. You might want to implement some checks including cross-referencing departments to prevent infinite loop.
Note that formally the term 'to extend a class' may apply to extension methods rather then to partial classes. I used this word from lack of better one. Extension methods would be something that you might want to use if, for some reason, you'd need your method/property returning EF native DbSet<> instead of the List<>. In such case you might want to take look at: https://shelakel.co.za/entity-framework-repository-pattern/
Example in EF6 to get all parents up to root node.
public class Department
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Department Parent { get; set; }
public virtual ICollection<Department> Children { get; set; }
private IList<Department> allParentsList = new List<Department>();
public IEnumerable<Department> AllParents()
{
var parent = Parent;
while (!(parent is null))
{
allParentsList.Add(parent);
parent = parent.Parent;
}
return allParentsList;
}
}
use include keyword.
_context.Invoices.Include(x => x.Users).Include(x => x.Food).ToList();

Explicit construction of entity type 'Artist' in query is not allowed

Compile just fine but execution fail with the error in the title.
ArtistService.cs
public class ArtistService : IArtistService
{
public List<Artist> ArtistDetail()
{
using (ArtistDataContext db = new ArtistDataContext())
{
return (from artist in db.Artists
select new Artist()
{
Id = artist.Id,
Artist_name = Artist.Artist_name
}).ToList(); <=== error happened here
}
}
}
code Behind
private List<ArtistServiceReference.Artist> ArtistDetail()
{
ArtistServiceReference.ArtistServiceClient client = new
ArtistServiceReference.ArtistServiceClient();
ArtistServiceReference.Artist[] artists = client.ArtistDetail();
return artists.ToList();
I want to move the Artist List to a DropdownList.
The error is happening in the ArtistService.cs at the end {).ToList();
Any explanation on how to fix this issue? Thanks
I based my code on this example and this example is working fine.
example code MyService.cs
public class MyService : IMyService
{
public List<Task> GetTasks()
{
using (TasksDataContext db = new TasksDataContext())
{
return (from task in db.TasksLists
select new Task()
{
Id = task.taskId,
Name = task.taskName,
}).ToList();
}
}
}
Example default.aspx.cs
private List<TaskService.Task> GetTasks()
{
TaskService.MyServiceClient client = new TaskService.MyServiceClient();
TaskService.Task[] tasks = client.GetTasks();
return tasks.ToList();
}
I don't understand why this example will work and not mine. The only difference was this example is returning to a gridview and I want to return to a dropdownlist.
Linq to Entities cannot translate the Artist object creation into SQL code (really, what is this supposed to look like?). Linq to Entities can only execute SQL queries and map returned fields to some entity to which it knows how to map (i.e. your DbSet entities). So, you need to execute the query first and then create the Artist entities locally:
public class ArtistService : IArtistService
{
public List<Artist> ArtistDetail()
{
using (ArtistDataContext db = new ArtistDataContext())
{
return (from artist in db.Artists
select new { // select only columns you need
artist.Id,
artist.Artist_name
})
.AsEnumerable() // execute query
.Select(x => new Artist { // create instance of class
Id = x.Id,
Artist_name = x.Artist_name
})
.ToList();
}
}
}
BTW it looks like you have Artist entities in your Artists DbSet. Why not simply return
return db.Artists.ToList();

Categories

Resources