Getting specific columns from an INCLUDE statement with EF - c#

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

Related

How can I get a list of objects related to a min from a Linq

I have a Linq Query that is working as intended, but I need to add the code so that it will show me ONLY the people with the less cases assigned for an app that is been used to treat customers inquiries. The idea behind the query is so that it will let me automatically assign inquiries randomly between those agents which have less assigned issues to cover.
As a simple example, lets say I have 5 agents with just 1 case each, I need to randomly assign one of them to an Inquire which has currently no agent assigned. So all I'm looking for is a way to actually get all the agents with the smallest number of cases assigned.
So far this is the full proof of concept code:
var inquires = new List<Inquire>();
var agents = new List<Agent>();
LoadData();
var assignationsPerAgent = (from agent in agents
join inq in inquires on agent equals inq.AssignedAgent into agentsInInquires
select new {
o_agent = agent,
casesAssignedTo = agentsInInquires.Count()
}).ToList();
//This works but is NOT the kind of solution I'm looking for
var min = assignationsPerAgent.Min(c => c.casesAsignedTo);
var agentWithMin = assignationsPerAgent.Where(a => a.casesAsignedTo == min);
Console.WriteLine();
void LoadData()
{
agents = new(){
new Agent{ Id = Guid.Parse("317d3d26-25c2-49da-aa4b-b7e49a1b9015"), Name = "Robert" },
new Agent{ Id = Guid.Parse("84188e21-8147-498f-bc2a-59874dc4a24a"), Name = "Corina" },
new Agent{ Id = Guid.Parse("90ca6658-95d4-4df4-a072-159087feddc0"), Name = "John" },
new Agent{ Id = Guid.Parse("34e091e4-cc7a-4222-9885-5de5bb5a0291"), Name = "Jack"},
new Agent{ Id = Guid.Parse("f22dcb4e-e927-4ddf-ae66-f37c0de6753d"), Name = "Samuel"}
};
inquires = new(){
new Inquire{ CustomerName = "Paula", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("317d3d26-25c2-49da-aa4b-b7e49a1b9015"))},
new Inquire{ CustomerName = "Barry", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("84188e21-8147-498f-bc2a-59874dc4a24a"))},
new Inquire{ CustomerName = "Bertie", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("84188e21-8147-498f-bc2a-59874dc4a24a"))},
new Inquire{ CustomerName = "Herman", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("90ca6658-95d4-4df4-a072-159087feddc0"))},
new Inquire{ CustomerName = "Ashley", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("317d3d26-25c2-49da-aa4b-b7e49a1b9015"))},
new Inquire{ CustomerName = "Tate", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("90ca6658-95d4-4df4-a072-159087feddc0"))},
new Inquire{ CustomerName = "Bonnie", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("90ca6658-95d4-4df4-a072-159087feddc0"))},
new Inquire{ CustomerName = "Tabitha", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("90ca6658-95d4-4df4-a072-159087feddc0"))},
new Inquire{ CustomerName = "Ashley", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("34e091e4-cc7a-4222-9885-5de5bb5a0291"))},
new Inquire{ CustomerName = "Josie", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("84188e21-8147-498f-bc2a-59874dc4a24a"))},
new Inquire{ CustomerName = "Kelly", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("84188e21-8147-498f-bc2a-59874dc4a24a"))},
new Inquire{ CustomerName = "Kelly", AsignedAgent = agents.Single(a => a.Id == Guid.Parse("f22dcb4e-e927-4ddf-ae66-f37c0de6753d"))},
};
}
#nullable disable
class Inquire
{
private Guid _id;
public Guid Id
{
get
{
return _id;
}
private set
{
_id = Guid.NewGuid();
}
}
public string CustomerName { get; set; }
public Agent AsignedAgent { get; set; }
}
#nullable disable
class Agent
{
public Guid Id {get; set;}
public string Name { get; set; }
}
Please take in consideration that the assignationsPerAgent variable is simulating data coming from a query in the database (The actual query is below). If the "easy" way to do this comes from the SQL that it's also an acceptable solution.
SELECT u.Id, COUNT(g.Id) as QtyAsig
FROM Users as u
LEFT JOIN GeneralInquires AS g ON u.Id = g.UserId
GROUP BY u.Id
ORDER BY COUNT(g.Id)
What I'm getting from this Query is:
Id QtyAsig
8A21A6D2-0CEC-4F5C-2A6B-08DA60967E94 1
323C8D1A-2FAE-4ECC-D7A2-08DA6098F19A 1
BA485F3C-C44A-4FE5-9BFA-08DA64EF283A 1
8F0E856E-FA0B-4167-BBEF-08DA6451FA81 2
40952727-5C76-4902-9C4F-08DA638B3068 3
DD51085A-5BE3-4872-F4B5-08DA6E7828AA 4
What I need is:
Id QtyAsig
8A21A6D2-0CEC-4F5C-2A6B-08DA60967E94 1
323C8D1A-2FAE-4ECC-D7A2-08DA6098F19A 1
BA485F3C-C44A-4FE5-9BFA-08DA64EF283A 1
Thank you in advance!
check this
var assignationsPerAgent = (from agent in agents
join inq in inquires on agent equals inq.AsignedAgent into agentsInInquires
from inq in agentsInInquires.DefaultIfEmpty()
select new
{
o_agent = agent,
casesAssignedTo = agentsInInquires.Count()
}).GroupBy(x=>x.casesAssignedTo).OrderBy(x=>x.Key).FirstOrDefault();
Linq doesn't require one statement and splitting results has no impact on performance. Try following :
List<Inquire> sortedInquires = inquires.OrderBy(x => x.AsignedAgent.Id).ToList();
var results = (from a in agents
//join i in sortedInquires on a.Id equals i.Id
join i in sortedInquires on a.Id equals i.AssignedAgent
select new { agent = a, inquires = i}
).GroupBy(x => x.agent.Id)
.Select(x => x.First())
.ToList();

How to return Task list using LINQ or lambda in WinForm

I want to use async and await function in my LINQ function, but I don't how. I have two tables Order and Product I want join them and popout in DataGridView by using async.
Here is my async function.
My list definition is:
List<OrderDTO> transactionByDate;
and my function:
private async Task<List<OrderDTO>> GetTransactionByDate(DateTime today)
{
return await Task.Factory.StartNew(() =>
{
using (Db db = new Db())
{
var totalOrder = (from u in db.Orders
join p in db.Product on u.ProductId equals p.ProductId
where u.OrderDate == today
select new OrderDTO
{
OrderId = u.OrderId,
ProductName = p.ProductName,
Date = u.OrderDate,
Price = u.Price,
}).OrderByDescending(x => x.Date).ToList();
return totalOrder;
}
});
}
And then in my button event:
private async void button1_Click(object sender, EventArgs e)
{
transactionByDate = await GetTransactionByDate(today);
dgvTransactions.DataSource = transactionByDate;
}
Error I'm getting is:
The entity or complex type 'OrderDTO' cannot be constructed in a LINQ to Entities query.
I really don't know how to return from my async GetTransactionByDate(today) function. I would appreciate if you could help me, I'am new for this kind of coding.
I couldn't test this because it doesn't compile locally (obviously) but I think you could do it like this:
private async Task<List<OrderDTO>> GetTransactionByDate(DateTime today)
{
return await Task.Factory.StartNew(() =>
{
using (Db db = new Db())
{
var totalOrder = (from u in db.Orders
join p in db.Product on u.ProductId equals p.ProductId
where u.OrderDate == today
select new
{
OrderId = u.OrderId,
ProductName = p.ProductName,
Date = u.OrderDate,
Price = u.Price
})
.ToList();
var result = totalOrder.Select(ano =>
new OrderDTO
{
OrderId = ano.OrderId,
ProductName = ano.ProductName,
Date = ano.OrderDate,
Price = ano.Price,
})
.OrderByDescending(x => x.Date)
.ToList();
return result;
}
}
}
The key is to separate what happens on the database and what happens in memory.
Before you start, though, I'd make a nice helper function that takes out some of the boilerplate code.
private async Task<List<T>> GetDtosAsync<T>(Func<Db, List<T>> getDtos) =>
await Task.Run(() =>
{
using (Db db = new Db())
{
return getDtos(db);
}
});
Note that I'm using Task.Run as Task.Factory.StartNew is outdated and no longer recommended.
Now you can write your method like this:
private async Task<List<OrderDTO>> GetTransactionByDate(DateTime today) =>
await GetDtosAsync(db =>
{
var query =
from u in db.Orders
join p in db.Product on u.ProductId equals p.ProductId
where u.OrderDate == today
select new
{
u.OrderId,
p.ProductName,
u.OrderDate,
u.Price,
};
var totalOrder =
query
.ToList()
.Select(x => new OrderDTO
{
OrderId = x.OrderId,
ProductName = x.ProductName,
Date = x.OrderDate,
Price = x.Price,
})
.ToList();
return totalOrder;
});
It's the query.ToList() that takes the data from the database and brings it in to memory before you construct you final list.

crateDB read 10k rows without iteration in Npgsql C# client

I have 10k row data in crate database. How to read the data without iteration. I am using crateDB C# Npgsql client for my service.
var connString = "Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase";
await using var conn = new NpgsqlConnection(connString);
await conn.OpenAsync();
// Retrieve all rows
var cmd = new NpgsqlCommand("select * from sensordata where timestamp >= extract(epoch from (now() - interval '1 week'))", conn);
var result = new List<SensorDataViewModel>();
using (var reader = cmd.ExecuteReader())
{
while(reader.HasRows && reader.Read())
{
SensorDataViewModel item = new SensorDataViewModel {
sensorid = reader["sensorid"].ToString(),
deviceid = reader["deviceid"].ToString(),
reading = Convert.ToInt32(reader["reading"]),
timestamp = (double)reader["timestamp"]
};
result.Add(item);
}
}
here im reading each row at a time in while loop. that take lot of time in processing ?
Maybe you need to consider using EntityFrameworkCore. For more details please refer to https://www.npgsql.org/efcore/index.html
Below is my sample code which I have tried. In the CrateDb contain 2 tables. One is customers and another is todo. These table has a relationship where customer.id=todo.customer_id.
The first and second query inside the sample is just select every records from the 2 tables respectively.
The third query is using join to retrieve related records according to the relationship and filtered by customers.id.
class Program
{
static async Task Main(string[] args)
{
MemoContext memoContext = new MemoContext();
//select * from customers
List<Customer> customers = memoContext.customers.Select(x => new Customer
{
id = x.id,
name = x.name,
contactno = x.contactno,
email = x.email
}).ToList();
//select * from todo
List<Todo> todos = memoContext.todo.Select(x => new Todo
{
complete = x.complete,
customer_id = x.customer_id,
id = x.id,
title = x.title
}).ToList();
//SELECT c.name, c.email, c.contactno, t.title, t.complete
//FROM customers AS c
//JOIN todo AS t ON t.customer_id = c.id
//WHERE c.id=1
var memo = memoContext.customers.Join(
memoContext.todo,
c => c.id,
t => t.customer_id,
(c, t) => new
{
id = c.id,
name = c.name,
email = c.email,
contactno = c.contactno,
todotitle = t.title,
complete = t.complete
}).Where(n => n.id == 1).ToList();
}
}
class MemoContext : DbContext
{
public DbSet<Customer> customers { get; set; }
public DbSet<Todo> todo { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql("Host=localhost;Port=5432;Username=crate;SSL Mode=Prefer;Database=doc");
}
I hope it is useful to you.

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.

nested LINQ query, error message

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

Categories