expression cannot be translate into store expression - c#

I have to download some complex datas from database which aggregate lots of usefull infos about post. I would like to do something like this:
var list = (from message in db.BLOGS_MESSAGES
where message.BLOG_ID == blogId
orderby message.CREATED_DATE descending
select new BlogMessage()
{
AUTHORS = **(from author in message.AUTHORS
select author.USERS).ToArray()**,
CREATED_BY = message.CREATED_BY,
CREATED_DATE = message.CREATED_DATE,
BLOG_MESSAGE_ID = message.POST_ID,
MESSAGE_TITLE = message.TITLES.TITLE,
TAGS = **(from tag in message.TAGGED_MESSAGES
select tag.TAGS).ToArray()**,
LOGIN = message.USERS.LOGIN,
MESSAGE = message.MESSAGES.MESSAGE,
MESSAGE_ID = message.MESSAGE_ID,
POST_NOTE = message.POST_NOTES.Sum(x => (long?)x.NOTE) ?? 0 / message.POST_NOTES.Count(),
}).ToList();
but it doesn't work. It throws an exception that can't translate expression in store expression.
so far i did it in this way:
var mlist = (from message in db.BLOGS_MESSAGES
where ....
orderby ....
select new {
AUTHORS = (from author in message.AUTHORS
select author.USERS),
....
}
List<BlogMessage> list = new List<BlogMessage>();
foreach(var item in mlist)
{
list.Add(new BlogMessage()
{
AUTHORS = item.AUTHORS.ToArray(),
...
});
}
is it possible to make it work 'in first way - style'?

You can either:
Change BlogMessage.AUTHORS to be an IEnumerable<USERS> and remove the .ToArray() calls in the query like Grundy suggested.
Bring the results into memory before creating BlogMessage's.
For example:
var step1 = db.BLOGS_MESSAGES
.Where(...)
.Select(message => new {
Authors = message.AUTHORS.Select(a => a.USERS), // No .ToArray()
...
}).ToList();
var step2 = step1.Select(message => New BlogMessage {
Authors = message.Authors.ToArray(),
...
}).ToList();

Related

Search query with parameters in linq c#

I need to build a search query with dynamic parameters in net core 3.0.
IQueryable<UserDto> query =
from user in dbContext.DB_USER
join items in dbContext.DB__ITEMS on user.IdItem equals items.IdItem
join cars in dbContext.DB_CARS on user.IdCars equals cars.IdItem
join statsCar in dbContext.DB_STATS_CARS on cars.IdCars equals statsCar.Id
select new UserDto
{
Id = user.Id,
Name = user.Name,
Data = user.Data.HasValue ? user.Data.Value.ToUnixTime() : default(long?),
Lvl = user.L,
Items = new ItemsUdo
{
Id = items.Id,
Type = items.Type,
Value = items.Value
},
Cars = new CarsDto
{
Id = cars.Id,
Model = cars.model,
Color = cars.Color
}
};
I would like to add search parameters like user name, items type, cars model and data from user. I tried to add "where" before 'select new UserDto' but not always user will provide all search parameters. If I give below:
if(fromSearch.UserName != null && fromSearch.UserName.Lenght > 0)
{
query = query.Where(u => u.Name == fromSearch.UserName);
}
it works(on user.data does not work) but is it correct? How to do this in linq query?
Do something like this:
IQueryable<UserDto> query =
from user in dbContext.DB_USER
join items in dbContext.DB__ITEMS on user.IdItem equals items.IdItem
join cars in dbContext.DB_CARS on user.IdCars equals cars.IdItem
join statsCar in dbContext.DB_STATS_CARS on cars.IdCars equals statsCar.Id;
select new UserDto
{
Id = user.Id,
Name = user.Name,
Data = user.Data.HasValue ? user.Data.Value.ToUnixTime() : default(long?),
Lvl = user.L,
Items = new ItemsUdo
{
Id = items.Id,
Type = items.Type,
Value = items.Value
},
Cars = new CarsDto
{
Id = cars.Id,
Model = cars.model,
Color = cars.Color
}
};
if(!string.IsNullOrWhitespace(username))
query = query.Where(ud => ud.Name == username);
if(!string.IsNullOrWhitespace(itemtype))
query = query.Where(ud => ud.Items.Any(i => i.Type == itemtype));
if(!string.IsNullOrWhitespace(carmodel))
query = query.Where(ud => ud.Cars.Any(c => c.Model == carmodel));
etc. These will work like AND; if you specify a username and an itemtype you get only those users named that, with that item type somewhere in the items list.. etc

Return children and subchildren withouth having sub-array Linq

I have a linq query that returns users/employees with their corresponding supervisors.
List<OrgChartViewModel> OrgChart = new List<OrgChartViewModel>();
var userlist = (from u in users
select new OrgChartViewModel
{
id = u?.id.ToString(),
pid = u?.pid.ToString(),
name = u?.name,
title = u?.title,
img = u?.img
}).OrderBy(x => x.name).ToList();
OrgChart.AddRange(userlist);
return Json(OrgChart.DistinctBy(x => x.id));
However, I need a query that returns the supervisor and their children along with their sub-children without having a sub-array/nested array.
What I have tried :
List<OrgChartViewModel> OrgChart = new List<OrgChartViewModel>();
var userlist = (from u in users
select new OrgChartViewModel
{
id = u?.id.ToString(),
pid = u?.pid.ToString(),
name = u?.name,
title = u?.title,
img = u?.img
}).OrderBy(x => x.name).ToList();
OrgChart.AddRange(userlist);
foreach (var ul in userlist)
{
var userlist2 = (from u in users
select new OrgChartViewModel
{
id = u?.id.ToString(),
pid = u?.pid.ToString(),
name = u?.name,
title = u?.title,
img = u?.img
}).Where(x => x.pid == ul.id).OrderBy(x => x.name).ToList();
OrgChart.AddRange(userlist2);
}
return Json(OrgChart.DistinctBy(x => x.id));
But this only returns the first layer of sub-children. What I want to achieve is to return unlimited layers of sub-children without having sub-arrays.

LINQ Data Sorting

I am trying to fill select tag options from JQuery ajax call. I am using Asp.Net Core 2.1 Razor Pages, and PostgreSQL as DB.
Here is my Server side LINQ code
[HttpGet]
public ActionResult TypeofAccounts()
{
var result = (from N in _POSContext.TypeOfAccounts
select new { label = N.AccountType, id = N.AccountType });
return Json(result);
}
It works fine. Now, I want to sort those results from LINQ so I tried following ways but it always encounters Npgsql Exception "column \"label\" does not exist"
var result = (from N in _POSContext.TypeOfAccounts.OrderBy(x=>x.AccountType)
select new { label = N.AccountType, id = N.AccountType });
var result = (from N in _POSContext.TypeOfAccounts
select new { label = N.AccountType, id = N.AccountType }).OrderBy(x => x.label);
var result = (from N in _POSContext.TypeOfAccounts.OrderBy(x => x.AccountType)
where N.AccountType != null
select new { label = N.AccountType, id = N.AccountType });
I could see coloumn is missing in generated sql.
{SELECT x."AccountType" AS id
FROM "TypeOfAccounts" AS x
WHERE x."AccountType" IS NOT NULL
ORDER BY label}
You need to invoke the query from the database using ToList method, then selecting your object, like this:
var result = _POSContext.TypeOfAccounts
.Where(x => x.AccountType != null)
.OrderBy(x => x.AccountType)
.ToList()
.Select(x =>
new
{
label = x.AccountType,
id = x.AccountType
}
);
You can try this
var result = _POSContext.TypeOfAccounts
.Where(x => x.AccountType != null)
.OrderBy(x => x.AccountType)
.ToList()
.Select(x =>
new
{
label = x.AccountType,
id = x.AccountType
}
);

How can i do recursive linq function?

I want to make a menu, and this is my code. I need recursive function.
var newTopMenu = new TopMenuVm
{
TopMenus = (from amp in allMasterPages
orderby amp.DisplayOrder
select new TopMenuVm
{
DisplayOrder = amp.DisplayOrder,
Url = string.IsNullOrEmpty(amp.Url) ? GenerateUrl(amp.Page1.OrderBy(x => x.DisplayOrder).FirstOrDefault()) : amp.Url,
PageId = amp.PageId,
PageTitle = amp.PageTitle,
TopMenus = (from submenu in amp.Page1
orderby amp.DisplayOrder
select new TopMenuVm
{
DisplayOrder = submenu.DisplayOrder,
PageTitle = submenu.PageTitle,
PageId = submenu.PageId,
Url = string.IsNullOrEmpty(submenu.Url) ? GenerateUrl(submenu) : submenu.Url,
}).ToList()//Here is go on infinite...,
}).ToList()
};
How can i do that with linq, please help...
Is something like this what you are looking for?
// allMasterPages is type List<MyPage> - change to whatever it really is
Func<List<MyPage>, List<TopMenuVM>> funcTopMenuVM = null;
funcTopMenuVM = ((pages) => {
return
(from amp in pages
orderby amp.DisplayOrder
select new TopMenuVM()
{
DisplayOrder = amp.DisplayOrder,
Url = string.IsNullOrEmpty(amp.Url) ? GenerateUrl(amp.Page1.OrderBy(x => x.DisplayOrder).FirstOrDefault()) : amp.Url,
PageId = amp.PageId,
PageTitle = amp.PageTitle,
TopMenus = funcTopMenuVM(amp.Page1)
}).ToList();
});
var newTopMenu = new TopMenuVM()
{
TopMenus = funcTopMenuVM(allMasterPages)
};
Of course, it doesn't have to be a Func<>, it could just be a regular method that returns a List<TopMenuVM> and takes a List<MyPage> (or whatever allMasterPages is).

Getting specific columns from an INCLUDE statement with EF

I want to get only specific columns from a query in EF when using an INCLUDE statement instead of bringing back all the columns.
In addition, what if I also wanted to bring back a shorter result set from my Customers object as well.
Is there a way to do this?
Here is my query:
void Main()
{
IEnumerable<Customer> customerProjectsList = GetCustomerProjects();
customerProjectsList.Dump();
}
public List<Customer> GetCustomerProjects()
{
try
{
using (YeagerTech DbContext = new YeagerTech())
{
var customer = DbContext.Customers.Include("Projects");
return customer;
}
}
catch (Exception ex)
{
throw ex;
}
}
EDIT
I've been trying to use the following query, but get an error of "Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.ICollection'. An explicit conversion exists (are you missing a cast?)"
Here is the query:
void Main()
{
List customerProjectsList = GetCustomerProjects();
customerProjectsList.Dump();
}
public List<CustomerDTO> GetCustomerProjects()
{
try
{
using (YeagerTech DbContext = new YeagerTech())
{
var customerlist = DbContext.Customers.Select(s =>
new CustomerDTO()
{
CustomerID = s.CustomerID,
Projects =
from p in Projects
where p.CustomerID == s.CustomerID && p.Quote != null
select new Project { Description = p.Description, Quote = p.Quote }
}).ToList<Project>();
return customerlist.ToList();
}
}
catch (Exception ex)
{
throw ex;
}
}
If I run this same query in LINQPad as a C# statement instead of a C# program, the query results get produced fine.
I am just going bonkers over this simple way to try and get a hierarchal list back with specific columns.
var result = (from c in Customers
select new
{
c.CustomerID,
Projects =
from p in Projects
where p.CustomerID == c.CustomerID && p.Quote != null
select new { p.Description, p.Quote }
});
result.Dump();
You don't necessarily need include; if you have navigation properties between Customers and Projects you can project to new objects:
var customers = (from c in DbContext.Customers
select new
{
FirstName = c.FirstName,
ProjectName = c.Project.Name
}).ToList().Select(x => new Customer
{
FirstName = x.FirstName,
Project = new Project()
{
Name = x.ProjectName
}
}).ToList();
This will return a list of Customers where only the first name is populated and each customer will contain a Project property with the name populated. This is great for performance as the query sent by EF to your database will be short and will return a result set quickly.
Edit:
Taking into account that Projects is an ICollection, I think the most maintenable thing to do would be to create a couple of DTOs:
public CustomerDTO
{
public int CustomerId;
public List<ProjectDTO> projects;
}
public ProjectDTO
{
public string Description;
public string Quote;
}
and project to them like so:
var qry = (from c in context.Customers
select new CustomerDTO()
{
CustomerId = c.CustomerId,
Projects = (from pr in context.Projects
where c.ProjectId equals pr.Id
select new ProjectDTO
{
Description = pr.Description,
Quote = pr.Quote
}).ToList()
});
You have to use projection to limit the columns that are retrieved. Lots of examples of projection can be found on the internet a short example is like this :
DateTime twoDaysAgo = DateTime.Now.AddDays(-2);
var groupSummaries = _recipeContext.Groups.OrderBy(g => g.Name)
.Select(g => new GroupSummaryModel{
Id = g.Id,
Name = g.Name,
Description = g.Description,
NumberOfUsers = g.Users.Count(),
NumberOfNewRecipes = g.Recipes.Count(r => r.PostedOn > twoDaysAgo)
});
I took it from here :
http://www.davepaquette.com/archive/2013/02/09/writing-efficient-queries-with-entity-framework-code-first-part-2.aspx
The point is to use the new mechanism if needed multiple times. Here it is used with new GroupSummaryModel.
EDIT
var qry = (from c in context.Customers
select new CustomerDTO()
{
CustomerId = c.CustomerId,
Projects = (from pr in context.Projects
where c.ProjectId equals pr.Id
select new ProjectDTO
{
Description = pr.Description,
Quote = pr.Quote
}) //--> no tolist here
}).ToList(); //--> only here

Categories