What is the best practice for the following scenario:
I have a LINQ to SQL expression where in its projection I want to call a private method.
I understand that my method cannot be translated into SQL, but I do need the logic.
Changing the property after getting the query result is not possible since you cannot change a projected property (it is read only).
10x
var projectedOrders = from order in orders
select new
{
orderId = order.Id,
orderName = order.FriendlyName,
OrderDate = order.OrderDate,
CustomerName = helper.GetUserNameByUserId(order.UserId)
};
You'll have to do it in 2 steps
query the raw data from the database, and materialize it
project onwards using your logic
var projectedOrders = (from order in orders
select new
{
orderId = order.Id,
orderName = order.FriendlyName,
OrderDate = order.OrderDate,
UserId= order.UserId
})
.ToArray()
.Select(o =>
new{
o.orderId,
o.orderName,
o.OrderDate,
CustomerName = helper.GetUserNameByUserId(o.UserId)
});
You can store the UserId temporarily into a property and set CustomerNames to null, then use a loop after your query and change the value of CustomerNames:
var projectedOrders = (from order in orders
select new
{
orderId = order.Id,
orderName = order.FriendlyName,
OrderDate = order.OrderDate,
UserId = order.UserId,
CustomerName = null
}).ToList();
foreach(var order in projectedOrders)
order.CustomerName = helper.GetUserNameByUserId(order.UserId);
Related
I am using below code to join two tables based on officeId field. Its retuning 0 records.
IQueryable<Usage> usages = this.context.Usage;
usages = usages.Where(usage => usage.OfficeId == officeId);
var agencyList = this.context.Agencies.ToList();
var usage = usages.ToList();
var query = usage.Join(agencyList,
r => r.OfficeId,
a => a.OfficeId,
(r, a) => new UsageAgencyApiModel () {
Id = r.Id,
Product = r.Product,
Chain = a.Chain,
Name = a.Name
}).ToList();
I have 1000+ records in agencies table and 26 records in usage table.
I am expecting 26 records as a result with chain and name colums attached to result from agency table.
Its not returning anything. I am new to .net please guide me if I am missing anything
EDIT
#Tim Schmelter's solution works fine if I get both table context while executing join. But I need to add filter on top of usage table before applying join
IQueryable<Usage> usages = this.context.Usage;
usages = usages.Where(usage => usage.OfficeId == officeId);
var query = from a in usages
// works with this.context.usages instead of usages
join u in this.context.Agencies on a.OfficeId equals u.OfficeId
select new
{
Id = a.Id,
Product = a.Product,
Chain = u.Chain,
Name = u.Name
};
return query.ToList();
Attaching screenshot here
same join query works fine with in memory data as you see below
Both ways works fine if I add in memory datasource or both datasource directly. But not working if I add filter on usages based on officeId before applying join query
One problem ist that you load all into memory first(ToList()).
With joins i prefer query syntax, it is less verbose:
var query = from a in this.context.Agencies
join u in this.context.Usage on a.OfficeId equals u.OfficeId
select new UsageAgencyApiModel()
{
Id = u.Id,
Product = u.Product,
Chain = a.Chain,
Name = a.Name
};
List<UsageAgencyApiModel> resultList = query.ToList();
Edit: You should be able to apply the Where after the Join. If you still don't get records there are no matching:
var query = from a in this.context.Agencies
join u in this.context.Usage on a.OfficeId equals u.OfficeId
where u.OfficeId == officeId
select new UsageAgencyApiModel{ ... };
The following code can help to get the output based on the ID value.
Of course, I wrote with Lambda.
var officeId = 1;
var query = context.Agencies // your starting point - table in the "from" statement
.Join(database.context.Usage, // the source table of the inner join
agency => agency.OfficeId, // Select the primary key (the first part of the "on" clause in an sql "join" statement)
usage => usage.OfficeId , // Select the foreign key (the second part of the "on" clause)
(agency, usage) => new {Agency = agency, Usage = usage }) // selection
.Where(x => x.Agency.OfficeId == id); // where statement
I am trying to get the same results as with a SQL query using Entity Framework method syntax.
SQL query :
select
mr.*, mrf.userId as RequesterUserId, mrt.UserId as ReceiverUserId
from
MoneyRequests mr
inner join
MoneyReqFrom mrf on mr.MoneyRequestId = mrf.MoneyRequestId
inner join
MoneyReqTo mrt on mr.MoneyRequestId = mrt.MoneyRequestId
where
mr.MoneyRequestId = 'acfc8008-4cf7-47ec-a3fe-0fe245af77cc'
EF Linq method syntax :
var moneyreqResponse = context.MoneyRequests
.Join(context.MoneyReqFroms,
mr => mr.MoneyRequestId,
mrf => mrf.MoneyRequestId,
(mr, mrf) => new
{
MoneyRequestId = mr.MoneyRequestId,
Amount = mr.Amount,
RequestType = mr.RequestType,
CreationDate = mr.CreationDate,
RequesterUserId = mrf.UserId
})
.Join(context.MoneyReqTos,
mr => mr.MoneyRequestId,
mrt => mrt.MoneyRequestId,
(mr, mrt) => new
{
MoneyRequestId = mr.MoneyRequestId,
Amount = mr.Amount,
RequestType = mr.RequestType,
CreationDate = mr.CreationDate,
ReceiverUserId = mrt.UserId,
Email = mrt.Email
})
.Where(fullEntry => fullEntry.MoneyRequestId == "acfc8008-4cf7-47ec-a3fe-0fe245af77cc")
.ToList();
I retrieve the data from the database except the column RequesterUserId.
Do you know why?
Thanks
Your query returns a MoneyRequests type, which I belive does not contain RequesterUserId.
You should define a new type with all the properties you need (those returned by the join) and add it to your DbContext.
Also probably you want to mark your new type as keyless
I am attempting to order the results of a Linq query by the length of a property and then by the property itself in order to order a string as an integer but the generated SQL is not ordering as I would expect it to.
I am joining multiple tables, filtering it down, selecting a DTO out with:
query = basequery.Select(s => new HeadersDTO
{
headerid = s.Header.id,
orderno = s.Header.orderno,
customer = s.Header.customer,
dateoforder = s.Header.dateoforder,
consignee = s.Location.name,
city = s.Location.name,
state = s.Location.state
}).Distinct();
Then trying to order by s.Header.orderno
query = query.OrderByDescending(x => x.orderno.Length).ThenByDescending(x => x.orderno)
.Skip(() => offset).Take(() => criteria.per_page);
This still orders it the normal way strings are ordered with first character taking precedence.
But if I select the x.orderno.Length out into it's own property and then order by that it works e.g.
query = basequery.Select(s => new HeadersDTO
{
ordernolength = s.Header.orderno.Length <---- added this
headerid = s.Header.id,
orderno = s.Header.orderno,
customer = s.Header.customer,
dateoforder = s.Header.dateoforder,
consignee = s.Location.name,
city = s.Location.name,
state = s.Location.state
}).Distinct();
query = query.OrderByDescending(x => x.ordernolength).ThenByDescending(x => x.orderno)
.Skip(() => offset).Take(() => criteria.per_page);
Is there a way to do this where I don't have to create a new property in the select list? I can add more information if needed.
Try to create a custom Comparer using IComparer where you do the Int32 check for this field. Here is an example for this:
Use own IComparer<T> with Linq OrderBy
Hope this helps
I have this Join :
var mycust= db.CUSTOMER.Where(x => x.NAME.Contains(nameid)).ToList();
var CancCustomer = (from cust in myCust
join ip in db.IPS on must.ID equals ip.CUSTOMER_ID
select new JoinObj {ID = cust.ID, NAME = cust.NAME, LASTNAME = cust.LASTNAME,
TYPE_ID = ip.TYPE_ID, TYPE2_ID = ip.TYPE2_ID,
SERVICE_ID = ip.SERVICE_ID , REASON = ip.REASON }).ToList();
This code returns the first linq result multiple times? What am I missing? Thanks.
Instead of Where, you should use SingleOrDefault/Single - these would indeed return a single row into your mycust variable.
SingleOrDefault would put a null into the variable if no such customers were found (and assuming a Customer is a reference type - if it were a value type, it would be the default value for the type). Single would throw an exception if no items were found or more than one were found, which could be very useful in finding errors in your data (such as duplicate customer records).
Additionally, it is likely your ip table has multiple matching records for a customer - which is why you would be seeing multiple records being returned from your select.
You have multiple Duplicate Record received Then Try For following quires
var mycust= db.CUSTOMER.Where(x => x.NAME.Contains(nameid)).ToList();
var CancCustomer = (from cust in myCust
join ip in db.IPS on cust.ID equals ip.CUSTOMER_ID
select new JoinObj {ID = cust.ID, NAME = cust.NAME, ASTNAME=cust.LASTNAME, TYPE_ID = ip.TYPE_ID, TYPE2_ID = ip.TYPE2_ID, SERVICE_ID = ip.SERVICE_ID , REASON = ip.REASON }).distinct().ToList();
Other Wise Multiple record then You Get One Record for You following Query
var mycust= db.CUSTOMER.Where(x => x.NAME.Contains(nameid)).ToList();
var CancCustomer = (from cust in myCust
join ip in db.IPS on must.ID equals ip.CUSTOMER_ID
select new JoinObj {ID = cust.ID, NAME = cust.NAME, LASTNAME = cust.LASTNAME, TYPE_ID = ip.TYPE_ID, TYPE2_ID = ip.TYPE2_ID, SERVICE_ID = ip.SERVICE_ID , REASON = ip.REASON }).distinct().FirstOrDefault();
My table has over 15 columns and I only want to retrieve four of those columns to store in memory. However trying the below method, gives the error cannot explicitly convert List to IEnumerable. Is this also the correct approach? Have a feeling I am missing a where clause or something? CompanyID is the index.
IEnumerable<Company> company = _db.Company.Select(a => new
{
CompanyId = Convert.ToString(a.CompanyId),
CompanyType = a.CompanyType,
CompanyName = a.CompanyName,
Email = a.Email
}).ToList();
You create an Anonymous Type with your Select statement and you can not convert List<definedAnonymousType> to IEnumerable<Company>.
Use var instead of IEnumerable<Company>.
var company = _db.Company.Select(a => new
{
CompanyId = Convert.ToString(a.CompanyId),
CompanyType = a.CompanyType,
CompanyName = a.CompanyName,
Email = a.Email
}).ToList();
then your company type will be List<definedAnonymousType>.
you can create CompanyLite dto with the exact properties you want and convert data to that
var companyLite = _db.Company.Select(a => new CompanyLite
{
CompanyId = Convert.ToString(a.CompanyId),
CompanyType = a.CompanyType,
CompanyName = a.CompanyName,
Email = a.Email
}).ToList();