I'm trying to write a linq to sql method that handles sorting, paging, and filtering for an ajax grid. I created a partial class Employee that has a TotalRecordCount, as I need to pass this to the javascript for setting up the pager. The problem is that it won't build because I can't set the AnonymousType#1.TotalRecordCount, it's read-only. Yet if I do "select new Employee", then it will throw the Exception - "Explicit construction of entity type 'InVision.Data.Employee' in query is not allowed.".
Here's the code...
public string GetPageJSON(string sortColumn, string sortDirection, int pageNumber, int pageSize, EmployeeSearch search)
{
var query = from e in db.Employees
select new
{
EmployeeID = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName,
LoginName = e.LoginName,
IsLockedOut = e.IsLockedOut,
TotalRecordCount = e.TotalRecordCount
};
//searching.
if (search.FirstName.Length > 0) query = query.Where(e => e.FirstName.Contains(search.FirstName));
if (search.LastName.Length > 0) query = query.Where(e => e.LastName.Contains(search.LastName));
if (search.LoginName.Length > 0) query = query.Where(e => e.LoginName.Contains(search.LoginName));
if (search.Status.Length > 0) query = query.Where(e => (search.Status == "Active" && !e.IsLockedOut)
|| (search.Status == "Inactive" && e.IsLockedOut));
//sorting.
query = query.OrderBy(sortColumn, sortDirection);
//get total record count.
int totalRecordCount = query.Count();
//paging.
query = query.Skip((pageNumber - 1) * pageSize).Take(pageSize);
//set total record count.
var list = query.ToList();
if (list.Count > 0)
{
list[0].TotalRecordCount = totalRecordCount; //throws exception
}
//return json.
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(list);
}
You'll want to select the original objects rather than mapping them to new objects (whether of the same type, or an anonymous type).
Replace this:
var query = from e in db.Employees
select new
{
EmployeeID = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName,
LoginName = e.LoginName,
IsLockedOut = e.IsLockedOut,
TotalRecordCount = e.TotalRecordCount
};
With this:
var query = db.Employees.AsQueryable();
Then later on replace this:
var list = query.ToList();
if (list.Count > 0)
{
list[0].TotalRecordCount = totalRecordCount;
}
With this:
var list = from e in query
select new
{
EmployeeID = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName,
LoginName = e.LoginName,
IsActive = !e.IsLockedOut,
TotalRecordCount = totalRecordCount
};
I think that should be everything. If the JavaScriptSerializer requires a List, just make sure you use it like this: return serializer.Serialize(list.ToList());
I ended up using a custom view class to get this to work...
partial class EmployeeView
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string LoginName { get; set; }
public bool IsActive { get; set; }
public int TotalRecordCount { get; set; }
}
public string GetPageJSON(string sortColumn, string sortDirection, int pageNumber, int pageSize, EmployeeSearch search)
{
var query = from e in db.Employees
select new EmployeeView
{
EmployeeID = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName,
LoginName = e.LoginName,
IsActive = !e.IsLockedOut,
TotalRecordCount = 0
};
//searching.
if (search.FirstName.Length > 0) query = query.Where(e => e.FirstName.Contains(search.FirstName));
if (search.LastName.Length > 0) query = query.Where(e => e.LastName.Contains(search.LastName));
if (search.LoginName.Length > 0) query = query.Where(e => e.LoginName.Contains(search.LoginName));
if (search.Status.Length > 0) query = query.Where(e => (search.Status == "Active" && e.IsActive)
|| (search.Status == "Inactive" && !e.IsActive));
//sorting.
query = query.OrderBy(sortColumn, sortDirection);
//get total record count.
int totalRecordCount = query.Count();
//paging.
query = query.Skip((pageNumber - 1) * pageSize).Take(pageSize);
//set total record count.
var list = query.ToList();
if (list.Count > 0)
{
list[0].TotalRecordCount = totalRecordCount;
}
//return json.
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(list);
}
Related
After migrating from .NET Core 2.x to .NET Core 5.0, we are facing this problem.
Error: (Added as a CODE for better readability)
The LINQ expression 'OUTER APPLY Projection Mapping:
(
SELECT e0.Id, e0.FirstName, e0.MiddleName, e0.LastName
FROM Employees AS e0
WHERE (((e0.Status != 4) && EXISTS (
Projection Mapping:
SELECT 1
FROM FunctionRoles AS f0
WHERE t.Id == f0.SchoolId)) && (e0.FunctionRoleId == (Projection Mapping:
EmptyProjectionMember -> 0
SELECT TOP(1) f1.Id
FROM FunctionRoles AS f1
WHERE (t.Id == f1.SchoolId) && (f1.Name == 'Manager')))) && (t.Id == e0.SchoolId)
) AS t0' could not be translated. Either rewrite the query in a form that can be translated,
or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
The code part:
using (var dbContext = _contextProvider.CreateContext())
{
var school = dbContext.Set<Domain.Model.School>()
.Where(s => s.Id == schoolId)
.Select(s => new SchoolSummaryDto
{
//... Some other properties
DocumentTemplates = s.DocumentTemplates != null ? s.DocumentTemplates.Select(a => new DocumentTemplateDto
{ Id = a.Id, Description = a.Description, SchoolId = a.SchoolId, FileName = a.FileName, DocumentTemplateTypeId = a.DocumentTemplateTypeId }).ToList() : new List<DocumentTemplateDto>(),
// This below chunk is causing problem.
Signers = s.Employees != null ? s.Employees.AsEnumerable().Where(
e => e.Status != PersistentStatusEnum.Removed &&
e.FunctionRoleId == s.FunctionRoles.AsEnumerable().Single(
b => b.Name == FunctionRolesEnum.Manager.ToString()).Id).AsEnumerable().Select(
a => new NameValueType { Id = a.Id, Name = string.Format("{0} {1} {2}", a.FirstName, a.MiddleName, a.LastName) }).ToList() : new List<NameValueType>(),
// .. Error chunk ends here
ContactPerson = s.ContactPerson,
Email = s.Email,
PhoneNumber = s.PhoneNumber,
SchoolId = s.Id,
SchoolName = s.Name,
Website = s.Website,
IsEnabled = s.IsEnabled,
IsRegistered = s.IsRegistered
}).FirstOrDefault();
}
What I tried:
As per these Microsoft links, Breaking Changes and Queryable projection not supported, I tried and applied changes AsEnumerable() accordingly as you can see above.
What changes are required now?
Libraries and Enviornment:
Database => MySql
Library => Pomelo.EntityFrameworkCore.MySql (5.0.0-alpha.2) Nuget Link
I sense either this MySql library is causing issue or EF Core 5 breaking changes.
Edit 1:
public class FunctionRole:AuditableEntity
{
public string Name { get; set; }
public string Description { get; set; }
public Guid SchoolId { get; set; }
public virtual School School { get; set; }
}
public class School:AuditableEntity
{
public bool IsRegistered { get; set; }
public bool IsEnabled { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Website { get; set; }
public string PhoneNumber { get; set; }
public string ContactPerson { get; set; }
public string ActivationCode { get; set; }
public virtual ICollection<FunctionRole> FunctionRoles { get; set; }
public virtual ICollection<DocumentTemplate> DocumentTemplates { get; set; }
}
I think it works with EF 2.x, because it silently evaluates this query on the client side.
Consider to rewrite query:
var query =
from s in dbContext.Set<Domain.Model.School>()
where s.Id == schoolId
from r in s.FunctionRoles
.Where(b => b.Name == FunctionRolesEnum.Manager.ToString())
.Take(1).DefaultIfEmpty()
select new SchoolSummaryDto
{
//... Some other properties
DocumentTemplates = s.DocumentTemplates
.Select(a => new DocumentTemplateDto
{
Id = a.Id,
Description = a.Description,
SchoolId = a.SchoolId,
FileName = a.FileName,
DocumentTemplateTypeId = a.DocumentTemplateTypeId
})
.ToList(),
Signers = s.Employees
.Where(e => e.Status != PersistentStatusEnum.Removed
&& e.FunctionRoleId == r.Id)
.Select(a => new NameValueType
{
Id = a.Id,
Name = a.FirstName + " " + a.MiddleName + " " + a.LastName
})
.ToList(),
ContactPerson = s.ContactPerson,
Email = s.Email,
PhoneNumber = s.PhoneNumber,
SchoolId = s.Id,
SchoolName = s.Name,
Website = s.Website,
IsEnabled = s.IsEnabled,
IsRegistered = s.IsRegistered
};
var school = query.FirstOrDefault();
You don't have to check for nulls, AsEnumerable is not needed, string.Format can be not translatable to the SQL.
I need to build a table in a view using the result of this SQL Query
SELECT f.empresaid,
e.fantasia AS Empresa,
f.filialid,
f.fantasia AS Filial,
u.consultorid AS ConsultorID,
cs.nome AS Consultor,
'' AS Midia,
fp.campoextra,
Count(DISTINCT p.usuarioid) AS Total,
Count(DISTINCT u.ativacao) AS TotalAtivos,
Iif(Count(DISTINCT p.usuarioid) > 0, ( Cast(
Count(DISTINCT u.ativacao) AS DECIMAL) / Count(DISTINCT p.usuarioid) ), 0
) * 100
AS Porcentagem
FROM formas_pagamento fp
INNER JOIN usuarios u
ON u.forma_pagamentoid = fp.forma_pagamentoid
INNER JOIN pagamentos p
ON p.usuarioid = u.usuarioid
AND p.tipo = 'MTU'
INNER JOIN cartoes c
ON c.usuarioid = u.usuarioid
AND c.dependenteid = 0
INNER JOIN midias m
ON m.descricao = u.midia
INNER JOIN consultores cs
ON cs.consultorid = u.consultorid
INNER JOIN filiais f
ON f.filialid = u.filialid
INNER JOIN empresas e
ON e.empresaid = f.empresaid
WHERE c.dependenteid = 0
AND u.cadastro > '2019-07-01'
AND u.cadastro < '2019-08-01'
AND u.filialid = 3
GROUP BY f.empresaid,
e.fantasia,
f.filialid,
f.fantasia,
u.consultorid,
cs.nome,
fp.campoextra
ORDER BY empresaid,
filialid,
consultor,
campoextra
Which gives me this result, the table in the view should be created with the structure as follows table structure and organization. I was trying to load this query data directly from my ViewModel List but that went heavy, if I could have a json array as following : JSON I could build the table with that desired structure more easily, can anyone help ? Thanks !
----------------------------------EDIT-------------------------------------
public List<ViewModelOrigemEConsultor> GetConsultorOrigem ()
{
var Lista = new List<ViewModelOrigemEConsultor>();
/*string SQL = SQL QUERY GOES HERE */
var cnn = CnnDB.OpenDB();
using (cnn)
{
var cmd = new SqlCommand(SQL, cnn);
cnn.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
decimal Porcentagem = 0;
while (reader.Read())
{
Porcentagem = (decimal)(Convert.ToInt32(reader["TotalAtivos"]) / Convert.ToInt32(reader["Total"]));
Lista.Add(new ViewModelOrigemEConsultor
{
EmpresaID = Convert.ToInt32(reader["EmpresaID"]),
Empresa = reader["Empresa"].ToString(),
FilialID = Convert.ToInt32(reader["FilialID"]),
Filial = reader["Filial"].ToString(),
ConsultorID = Convert.ToInt32(reader["ConsultorID"]),
Consultor = reader["Consultor"].ToString(),
Midia = reader["Midia"].ToString(),
FormaPagamento = reader["FormaPagamento"].ToString(),
Total = Convert.ToString(string.Format("{0:N}", reader["Total"])),
TotalAtivos = Convert.ToString(string.Format("{0:N}", reader["TotalAtivos"])),
Porcentagem = Convert.ToString(string.Format("{0:N}", Porcentagem))
});
};
}
};
return Lista;
}
MODEL
public class ViewModelOrigemEConsultor
{
public int EmpresaID { get; set; }
public string Empresa { get; set; }
public int FilialID { get; set; }
public string Filial { get; set; }
public int ConsultorID { get; set; }
public string Consultor { get; set; }
public string Midia { get; set; }
public string FormaPagamento { get; set; }
public string Total { get; set; }
public string TotalAtivos { get; set; }
public string Porcentagem { get; set; }
}
If you've got the list of objects populated and are just looking to convert it to JSON simply use something like https://www.nuget.org/packages/Newtonsoft.Json and then serialize the list to json. In this case it would be similar to:
var json = JsonConvert.SerializeObject(model);
I created an example rest webservice using WebApi.
Here is the controller class named Employee Controller.
namespace HelloWebApi.Controllers
{
public class EmployeesController : ApiController
{
private static IList<Employee> list = new List<Employee>()
{
new Employee()
{
Id = 12345, FirstName = "John", LastName = "Human",Department = 2
},
new Employee()
{
Id = 12346, FirstName = "Jane", LastName = "Public",Department = 3
},
new Employee()
{
Id = 12347, FirstName = "Joseph", LastName = "Law",Department = 2
}
};
// GET api/employees
public IEnumerable<Employee> Get()
{
return list;
}
// GET api/employees/12345
public Employee Get(int id)
{
return list.First(e => e.Id == id);
}
// POST api/employees
public void Post(Employee employee)
{
int maxId = list.Max(e => e.Id);
employee.Id = maxId + 1;
list.Add(employee);
}
// PUT api/employees/12345
public void Put(int id, Employee employee)
{
int index = list.ToList().FindIndex(e => e.Id == id);
list[index] = employee;
}
// DELETE api/employees/12345
public void Delete(int id)
{
Employee employee = Get(id);
list.Remove(employee);
}
public HttpResponseMessage Patch(int id, Delta<Employee> deltaEmployee)
{
var employee = list.FirstOrDefault(e => e.Id == id);
if (employee == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
deltaEmployee.Patch(employee);
return Request.CreateResponse(HttpStatusCode.NoContent);
}
}
}
Here I used all HTTP verbs method for insert, update, delete. I used a list that stores the Employee objects. insertion, deletion and updation are based on this list. But this program has no database connection. I want to know how to use Database access in this program. I would like to use tables from a database. How could i do it? Any useful link please.
Afternoon,
I would like to know how i would do this query in LINQ, can anyone please provide a hand.
SELECT Id, ExportDate,
(SELECT TOP (1) Id
FROM Orders
WHERE (PickupListId = PickingLists.Id)) AS StartOrderId,
(SELECT TOP (1) Id
FROM Orders AS Orders_1
WHERE (PickupListId = PickingLists.Id)
ORDER BY Id DESC) AS EndOrderId,
(SELECT COUNT(Id) AS Expr1
FROM Orders AS Orders_2
WHERE (PickupListId = PickingLists.Id)) AS NumberOfOrders
FROM PickingLists
ORDER BY ExportDate DESC
Update
I have updated my code as per Andrei's solution, however there is an issue converting the ID's please can you see what it could be?
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public List<GetPickingLists> GetPickingLists()
{
using (aboDataDataContext dc = new aboDataDataContext())
{
var query = from list in dc.pickingLists
orderby list.ExportDate descending
select new GetPickingLists
{
plId = list.Id,
plDate = list.ExportDate,
orderStart = Convert.ToInt32(dc.amzOrders.Where(order => order.pickupListId == list.Id).FirstOrDefault()),
orderEnd = Convert.ToInt32(dc.amzOrders.Where(order => order.pickupListId == list.Id).OrderByDescending(order => order.id).FirstOrDefault()),
orderCount = dc.amzOrders.Where(order => order.pickupListId == list.Id).Count(),
};
return query.ToList();
}
}
I have created this to set up the response, not sure if this is needed. Cheers.
public class GetPickingLists
{
public int plId { get; set; }
public int orderStart { get; set; }
public int orderEnd { get; set; }
public int orderCount { get; set; }
public DateTime plDate { get; set; }
}
var query = from list in dbContext.PickingLists
orderby list.ExportDate descending
select new
{
list.Id,
list.ExportDate,
StartOrderId = dbContext.Orders.Where(order => order.PickupListId == list.Id).FirstOrDefault().Id,
EndOrderId = dbContext.Orders.Where(order => order.PickupListId == list.Id).OrderByDescending(order => order.Id).FirstOrDefault().Id,
NumberOfOrders = dbContext.Orders.Where(order => order.PickupListId == list.Id).Count(),
}
Try it like this:
var query = db.PickingLists
.OrderByDescending(pl => pl.ExportDate)
.Select(pl => new GetPickingLists {
plId = pl.Id,
plDate = pl.ExportDate,
orderStart = db.Orders.FirstOrDefault(o => o.PickupListId = pl.Id).Id,
orderEnd = db.Orders.LastOrDefault(o => o.PickupListId = pl.Id).Id,
orderCount= db.Orders.Count(o => o.PickupListId = pl.Id)
});
Assume that I have a very simple db diagram:
and a view for it:
create view vTraining
as
select t.Id as TrainingId,t.[Status] ,
t.[User], t.Title,t.Organisation,t.[Month],t.[Year],
s.Id as SubjectId, s.Name as SubjectName,
c.Text as Comment
from Training t
join Subject s on s.Training = t.Id
join Comment c on c.Training = t.Id
with sample data:
As you can see, this is a single training with three subjects.
I want to map this result to this structure by linq to sql:
public class ViewModel
{
public string Comment { set; get; }
public List<Item> Trainings { set; get; }
}
public class Item
{
public int TrainingId { set; get; }
public int User { set; get; }
public int Status { set; get; }
public string Title { set; get; }
public string Organisation { set; get; }
public int? Month { set; get; }
public int Year { set; get; }
public List<KeyValuePair<int, string>> Subjects { set; get; }
}
This is my query, that I created:
var data = (from training in dc.vTrainings
group training by new
{
training.TrainingId,
training.Status,
training.Month,
training.Organisation,
training.Title
}
into g
select new ViewModel()
{
Comment = g.Select(x =>
x.Comment).First(),
Trainings = g.Select(
x => new Item()
{
Month = x.Month,
Organisation = x.Organisation,
Title = x.Title,
Year = x.Year,
Subjects = g.Select(
z => new KeyValuePair<int, string>(z.SubjectId, z.SubjectName)).ToList()
}).ToList()
})//.GroupBy(x => x.Trainings).Select(x => x.Key)
.ToList();
Unfortunatelly the result I get is not the one I want:
The ViewModel object is created only ones what is ok, but for each single subject, the new Item is created (should be one). The Subjets list is created correctly. I tried to create a second group by, and some other things, but this is the best result I can get for now.
How to write this query to get one ViewModel object which has one Item object with three subjects?
I finally got my the proper result:
var data = (from g in
(from training in dc.vTrainings
where training.Status ==1
group training
by new
{
training.TrainingId,
training.Status,
training.Month,
training.Organisation,
training.Title
}
into g
select g).AsEnumerable()
select new ViewModel()
{
Comment = g.Select(x =>
x.Comment).FirstOrDefault(),
Trainings = g.GroupBy(x => x.Status).Select(
x => new Item()
{
Month = g.Key.Month,
Organisation = g.Key.Organisation,
Title = g.Key.Title,
Subjects = (from i in g select new KeyValuePair<int, string>(i.SubjectId, i.SubjectName)).ToList()
}).ToList()
}).ToList();
This query works only for one training, but for me this is not a problem, because I filtering it by the newest status. Still I'm curious how to write it for more than one training.
Try this:
var trainings =
dc.vTrainings
.GroupBy(
t => new
{
t.TrainingId,
t.Status,
t.Month,
t.Organisation,
t.Title,
t.User,
t.Year
},
t =>
new
{
t.SubjectId,
t.SubjectName
})
.ToList()
.Select(
t =>
new Item
{
TrainingId = t.Key.TrainingId,
Status = t.Key.Status,
Month = t.Key.Month,
Organisation = t.Key.Organisation,
Title = t.Key.Title,
User = t.Key.User,
Year = t.Key.Year,
Subjects =
t.Select(s => new KeyValuePair<int,string>(s.SubjectId,s.SubjectName)).ToList()
});