linq - format string from two fields in select new context - c#

I have foreign table with one to many relation. I write linq query as left join equivalent and implement group by relevant id field.
from p in db.personal join pn in
(from t in db.phoneNumbers
group t by t.personID into g
select new { id = g.Key,
number = g.Select(t => t.number),
prefix = g.Select(t => t.prefix)
}).AsEnumerable()
on p.ID equals pn.id
into lPN from lpn in lPN.DefaultIfEmpty()
//join wsd in db.basicOperations on p.ID equals wsd.personID
where p.ID == id.Value
select new partialPersonDetailsViewModel()
{
id = id.Value,
genderType = p.genderType,
sPhoneNumbers="(" +lpn.prefix+") "+lpn.number
}).FirstOrDefault();
But at sPhoneNumbers="(" +lpn.prefix+") "+lpn.number this place VS notify me about error:
Error 1 Operator '+' cannot be applied to operands of type 'System.Collections.Generic.IEnumerable' and 'System.Collections.Generic.IEnumerable'
Please help me pass this error and solve problem.

Both of lpn.prefix and lpn.number are evaluated as g.Select(...), so in fact they are IEnumerable<T> (exactly as error message declares it).
Evaluating them as g.Select(...).FirstOrDefault() should help you since it will extract value of type T from IEnumerable<T>.
Update:
In your case, when lpn.prefix and lpn.number are actually lists of values and you need to concatenate these lists - you can use something like:
sPhoneNumbers = String.Join("; ",
lpn.prefix.Select((p, i) =>
String.Format("({0}){1}",
p,
lpn.numbers.Skip(i).Take(1).FirstOrDefault())));
Or you can use Enumerable.Zip method, as it was suggested in comments by #Chris:
sPhoneNumbers = String.Join("; ",
lpn.prefix.Zip(lpn.numbers,
(s, s1) => string.Format("({0}){1}", s, s1)));

Related

Type inference failed in the call to 'Join'

I am getting the following error on the word "join" in the code below.
The type of one of the expressions in the join clause is incorrect.
Type inference failed in the call to 'Join'.
var organisationQuery = ClientDBContext.Organisations.Where(x => true);
var orderGrouped = from order in ClientDBContext.Orders.Where(x => true)
group order by order.OrganisationId into grouping
select new { Id = grouping.Key.Value, OrderCount = grouping.Count() };
var orders = from og in orderGrouped
join org in organisationQuery on og.Id equals org.Id
select(x => new OrganisationOrdersReportPoco()
{
OrganisationNameThenCode = org.Name,
TotalOrders = og.OrderCount
});
I don't see a problem with the join clause? Can anyone please advise?
Edit:
This is the piece of SQL I am attempting to write as LINQ.
SELECT grp.OrganisationId,
grp.OrderCount,
organisations.Name
FROM (select OrganisationId,
count(*) as OrderCount
from orders where 1 = 1 group by OrganisationId) grp
LEFT OUTER JOIN organisations on grp.OrganisationId = organisations.OrganisationId
WHERE 1 = 1
I have complicated where clauses on both orders and organisations... simplified for this example.
You are selecting into an anonymous type in the first query:
var orderGrouped = ..
select new { Id = grouping.Key.Value, OrderCount = grouping.Count() };
This 'breaks' the connection with order.
The join looks like it should work for Linq-to-Objects but it can't be converted into SQL.
You'll have to eliminate the anonymous type and somehow make a more direct connection.
I wonder why you don't simply go from Organisations? With a proper mapping using nav-properties it should look like:
from org in ClientDBContext.Organisations
select(x => new OrganisationOrdersReportPoco()
{
OrganisationNameThenCode = org.Name,
TotalOrders = org.Orders.Count
};
using the Id properties should be a little more involved but follow the same pattern.
(Credit to Giorgi Nakeuri)
I was confusing LAMBDA with LINQ expressions.
Replacing my select with this solved it.
select new OrganisationOrdersReportPoco()
{
OrganisationNameThenCode = org.Name,
TotalOrders = og.OrderCount
};

Linq to SQL OrderBy issue

I know this question has been asked many times and I know that the Distinct call destroys the previous order so I must use the OrderBy afterwards, but in this case I must be doing something else wrong.
int[] resources = (from a in context.Beamline_Requests
join b in context.Technique_Requests on a.Technique_Request_ID equals b.ID
where b.Beamtime_Request_ID == id
select a.Beamline_ID).Distinct().OrderBy(a => a.ID).ToArray();
I receive the:
Cannot convert lambda expression to type 'System.Linq.Expressions.LambdaExpression' because it is not a delegate type
error message on the OrderBy. It also says:
'int' does not contain a definition for 'ID' and no extension method 'ID' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?)
So apparently the 'a' is no longer part of the context.
I have done similar things successfully but in those cases I am projecting the Linq into a ViewModel so in this case it must have something to do with attempting to just make it an array.
You are only selecting field Beamline_ID and later you are trying to OrderBy ID, Your intermediate result from select doesn't have field ID it is just a projection of int numbers. As #GrantWinney suggested you can do OrderBy(a=> a) like:
int[] resources = (from a in context.Beamline_Requests
join b in context.Technique_Requests on a.Technique_Request_ID equals b.ID
where b.Beamtime_Request_ID == id
select a.Beamline_ID).Distinct().OrderBy(a => a).ToArray();
For the comment:
My issue though is that I actually do need to order by the ID, not the
Beamline_ID even though I only need the Beamline_ID in the array.
int[] resources = (from a in context.Beamline_Requests
join b in context.Technique_Requests on a.Technique_Request_ID equals b.ID
where b.Beamtime_Request_ID == id
select new
{
Beamline_ID = a.Beamline_ID,
ID = b.ID
})
.OrderBy(a => a.ID)
.Select(r=> r.Beamline_ID)
.Distinct()
.ToArray();
Or in a simpler version you can achieve:
int[] resources = (from a in context.Beamline_Requests
join b in context.Technique_Requests on a.Technique_Request_ID equals b.ID
where b.Beamtime_Request_ID == id
orderby b.ID
select a.Beamline_ID)
.Distinct()
.ToArray();

C# Linq : Group By different columns of different entity

I have three entities ClassC, ClassS and ClassSA. I want to apply group by using LINQ.
using (var db = new SEntities())
{
var result = from c in db.ClassC
join s in db.ClassS on c.ID equals s.CID
join sa in db.ClassSA on s.SAID equals sa.ID
group sa by
new { c.Type, s.Date.Year}
into g
select new ClassSAY { Year = g.Key.Year, CI = g.Key.CIType, Count = g.Count(sa => sa.ID)};
}
I want to join ClassC, ClassS and ClassSA; group by two different properties of two different classes and store some part of result in another class's properties. I have searched by found group by only one class' properties.
It's giving error:
Cannot convert lambda expression to delegate type 'System.Func<SG.DAL.SA,bool>' because some of the return types in the block are not implicitly convertible to the delegate return type
Cannot implicitly convert type 'long' to 'bool'
Thanks.
Try to use like
Count = g.Count()
instead of
Count = g.Count(sa => sa.ID)

LINQ-to-entities generic == workaround

I have a following LINQ-to-entities query
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var q = (from h in ItemHistory
where h.OperationId ==
(from h1 in ItemHistory
where h1.GenericId == h.GenericId
select h1.OperationId).Min()
select h);
return q;
}
ItemHistory is a generic query. It can be obtained in the following way
var history1 = MyEntitiySet1.Select(obj =>
new History<long>{ obj.OperationId, GenericId = obj.LongId });
var history2 = AnotherEntitiySet.Select(obj =>
new History<string>{ obj.OperationId, GenericId = obj.StringId });
In the end of all I want a generic query being able to work with any entity collection convertible to History<T>.
The problem is the code does not compile because of GenericId comparison in the inner query (Operator '==' cannot be applied to operands of type 'T' and 'T').
If I change == to h1.GenericId.Equals(h.GenericId) I get the following NotSupportedException:
Unable to cast the type 'System.Int64' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types.
I've tried to do grouping instead of subquery and join the results.
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var grouped = (from h1 in ItemHistory
group h1 by h1.GenericId into tt
select new
{
GenericId = tt.Key,
OperationId = tt.Min(ttt => ttt.OperationId)
});
var q = (from h in ItemHistory
join g in grouped
on new { h.OperationId, h.GenericId }
equals new { g.OperationId, g.GenericId }
select h);
return q;
}
It compiles because GenericId's are compared with equals keyword and it works but the query with real data is too slow (it has been running for 11 hours on dedicated postgresql server).
There is an option to build a whole expression for the outer where statement. But the code would be too long and unclear.
Are there any simple workarounds for equality comparison with generics in LINQ-to-entities?
Try this, I think it should accomplish what you want without the extra query/join
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var q = from h in ItemHistory
group h by h.GenericId into tt
let first = (from t in tt
orderby t.GenericId
select t).FirstOrDefault()
select first;
return q;
}
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var grouped = (from h1 in ItemHistory
group t by h1.GenericId into tt
select new
{
GenericId = tt.Key,
OperationId = tt.Min(ttt => ttt.OperationId)
});
var q = (from h in ItemHistory
join g in grouped
on new { h.OperationId, h.GenericId }
equals new { g.OperationId, g.GenericId }
select h);
return q;
}
You could also set a generic constraint on T for an IItemHistory inteface that implements the GenericId and OperationId property.
My question already contains a solution. The second method with group + join works well if the table is properly indexed. It takes 3.28 seconds to retrieve 370k rows from the database table. In fact in non-generic variant the first query is slower on postgresql than the second one. 26.68 seconds vs 4.75.

Where clause in LINQ - C#

I have the following which works in SQL Query Analyzer.
select oh.*
from order_history oh
join orders o on o.order_id = oh.order_id
where oh.order_id = 20119 and oh.date_inserted = (
select max(date_inserted) from order_history where order_id = oh.order_id
group by order_id
)
How would I go about converting to LINQ? From test code, the compiler complained:
Error Operator '&&' cannot be applied to operands of type 'int' and 'System.DateTime'
My LINQ code:
var query = from ch in cdo.order_histories
join c in cdo.orders on ch.order_id equals c.order_id
where (ch.order_id.equals(1234)) &&
(ch.date_inserted == (cdo.order_histories.Max(p => p.date_inserted)))
select new OrderData() { };
Update: I was not using '==' for comparing.
Item remaining is this from my SQL query:
oh.date_inserted = (
select max(date_inserted) from order_history where order_id = oh.order_id
group by order_id)
How do I do this in LINQ?
It look like you are missing an equals sign somewhere when filtering on the order_id field. You probably have:
oh.order_id = 20119 && ...
Whereas you should have:
oh.order_id == 20119 && ...
Note the equality operator vs. the assignment operator. The result of an assignment operator is the value that was assigned, which is why your error says you can't compare operands of int and System.DateTime.
I also assume you have the same problem on the check against the value of date_inserted as well.
For the second part of your question, you are close in the conversion of the correlated sub query.
In SQL you have:
oh.date_inserted = (
select max(date_inserted) from order_history where order_id = oh.order_id
group by order_id)
And in LINQ-to-SQL you have
ch.date_inserted == (cdo.order_histories.Max(p => p.date_inserted))
You just have to add the filter for the order_histories which takes advantage of closures to capture the order_id value on the ch instance like so:
ch.date_inserted == (cdo.order_histories.
Where(ch2 => ch2.order_id == ch.order_id).
Max(p => p.date_inserted))
You could translate the SQL into LINQ... or you could write the LINQ for what you want.
var result = cdo.order_histories
.Where(oh => oh.order_id == 20119)
.OrderByDescending(oh => oh.date_inserted)
.Take(1)
.Select(oh => new {history = oh, order = oh.order}
.Single();
Agreed, some C# code is needed here, but off the top of my head- you're using "==" (evaluation) rather than "=" (assignment), correct? C# makes a distinction here where SQL does not.

Categories

Resources