nested LINQ query, error message - c#

I am currently trying to do a nested query in linq, but I am receiving an error message:
var subcatquery = from categories in mydb.Categories
where categories.ParentId == null
select new
{
category = categories.Name,
subcat = (from sub in mydb.Categories
where sub.ParentId == sub.Id
select new
{
subcatItem = sub.Name,
subcatId = sub.Id
})
};
Results View =The type '<>f__AnonymousType0<subcatItem,subcatId>' exists in both 'myapplication.dll' and 'System.Web.dll'
I can't underestand why.. if I remove the sub query and put it on it's own... it's perfectly fine.
the subcat type is a collection<>, is this where the problem is?
class categoryGroup
{
public string category;
public Collection<subcategoryGroup> subcat;
}
class subcategoryGroup
{
public string subcatItem;
public int subcatId;
}

In my project i am using sub query like this it's below
var data = (from con in dbData.tblPresenters
where con.PresenterID == ID
select new
{
Name = con.Name,
Title = dbData.tblTitles.Where(x => x.TitleID == con.PresenterTitleID).FirstOrDefault()
}).ToList();
i think this will help you ....

have you tried to define/name the types instead of keeping them anonymous,
var subcatquery = from categories in mydb.Categories
where categories.ParentId == null
select new categoryGroup()
{
category = categories.Name,
subcat = (from sub in mydb.Categories
where sub.ParentId == sub.Id
select new subcategoryGroup()
{
subcatItem = sub.Name,
subcatId = sub.Id
})
};

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

linq many to many query inside select

Here is my attempt to make a many to many linq request, but it doesn't work as expected. CVVM class has a property ICollection<FormationVM> Formations
var cv = (
from c in context.CVs
where c.Id == id
select new CVVM
{
Id = id,
Formations =
from f in context.Formations
from c2 in context.CVs
where c2.Id == id
select new FormationVM
{
Id = form.Id,
DateDebut = form.DateDebut,
DateFin = form.DateFin,
Ecole = form.Ecole,
Description = form.Description,
Diplome = form.Diplome
}
}).FirstOrDefault();
Why does Model.Formations.Count() return 3 instead of 2 in my View please ?
Ok i found the solution. This code works :
using (Context context = new Context())
{
var cv =
(
from c in context.CVs
where c.Id == id && c.PersonneId == userId
select new CVVM
{
Titre = c.Titre,
MontrerPhoto = c.MontrerPhoto,
Layout = c.Layout,
Id = id,
FormAction = "EditionTraitement",
FormTitre = "Edition de ce CV",
Formations = from form in c.Formations
where c.Id == id && c.PersonneId == userId
select new FormationVM
{
Id = form.Id,
DateDebut = form.DateDebut,
DateFin = form.DateFin,
Ecole = form.Ecole,
Description = form.Description,
Diplome = form.Diplome
}
}).FirstOrDefault();
}

LINQ Filtering Select Ouput with IEnumerable<T>

I have following methods:
Controller:
...
var appmap = Services.GetReqAppMapList(value);
var applist = Services.GetApplicationList(docid, appid, reqid, appmap);
...
Model:
public static IEnumerable<AppMap> GetReqAppMapList(int aiRequestTypeId)
{
try
{
var appmap = new List<AppMap>();
using (var eties = new eRequestsEntities())
{
appmap = (from ram in eties.ReqAppMaps
where ram.IsActive == 1
select new AppMap
{
RequestTypeId = ram.RequestTypeId
}).ToList();
return appmap;
}
}
catch(Exception e)
{
throw e;
}
}
public static IEnumerable<TicketApplication> GetApplicationList(int aiDocumentTypeId, int aiApplicationTypeId, int aiRequestTypeId, IEnumerable<AppMap> appmap)
{
try
{
var applicationlist = new List<TicketApplication>();
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
where 1==1
<<<Some Conditions Here???>>>
== && appmap.Contains(app.ApplicationTypeId) ==
&& app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId = app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
return applicationlist;
}
And I was thinking how can filter query result of GetApplicationList using the result from GetReqAppMapList
I'm kinda stuck with the fact that I must convert/cast something to the correct type because every time I do a result.Contains (appmap.Contains to be exact), I always get the following error
Error 4 Instance argument: cannot convert from
'System.Collections.Generic.IEnumerable<Test.Models.AppMap>' to
'System.Linq.ParallelQuery<int?>'
You should directly join the two tables in one query.
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
join ram in applicationentity.ReqAppMaps on app.ApplicationTypeId equals ram.RequestTypeId
where ram.IsActive == 1 && app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId = app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
You can delete the other method if it is not needed anymore. No point hanging onto code which is dead.
Looks like there is no other way to do this (as far as I know), so I have to refactor the code, I hope still that there would be a straight forward conversion and matching method in the future (too lazy). Anyway, please see below for my solution. Hope this helps someone with the same problem in the future. I'm not sure about the performance, but this should work for now.
Controller:
...
var appmap = Services.GetReqAppMapList(value);
var applist = Services.GetApplicationList(docid, appid, reqid, appmap);
...
Model:
<Removed GetReqAppMapList>--bad idea
public static IEnumerable<TicketApplication> GetApplicationList(int aiDocumentTypeId, int aiApplicationTypeId, int aiRequestTypeId)
{
try
{
//This is the magic potion...
List<int?> appmap = new List<int?>();
var applist = (from ram in applicationentity.ReqAppMaps
where ram.RequestTypeId == aiRequestTypeId
&& ram.IsActive == 1
select new AppMap
{
ApplicationTypeId = ram.ApplicationTypeId
}).ToList();
foreach (var item in applist)
{
appmap.Add(item.ApplicationTypeId);
}
//magic potion end
var applicationlist = new List<TicketApplication>();
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
where 1==1
===>>>&& appmap.Contains(app.ApplicationTypeId)<<<===
&& app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId =app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
return applicationlist;
}
A side-note, C# is a strongly-typed language, just make sure your data types matches during evaluation, as int? vs int etc.., will never compile. A small dose of LINQ is enough to send some newbies circling around for hours. One of my ID-10T programming experience but just enough to remind me that my feet's still flat on the ground.

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

expression cannot be translate into store expression

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

Categories