Difficulty typing arguments for System.Linq.Enumerable.Select - c#

I've been staring at this for awhile and not sure how to fix it.
All I'm trying to do is fill in the arptable description property where it matches the address in the device table. I am not trying to create another collection from the arptable.
Error:
The type arguments for method 'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Here is the offending code:
IEnumerable<ARPTABLE> GetARPTable()
{
IpTable arp = new IpTable();
IEnumerable<ARPTABLE> arptable = arp.GetArpTable();
arptable.ToList().ForEach(i =>
{
DeviceTable.Where(j => j.PhysicalAddress == i.MAC)
.Select(y =>
{
i.Description = y.Model ?? y.DeviceName;
});
});
return arptable;
}
where DeviceTable is
public ObservableCollection<Device> DeviceTable { get; set; }
Thanks for any help (or a better way).

The compiler is having trouble because your lambda expression isn't written correctly and so it fails on type inference. Unfortunately, the whole construction of the method is broken and I'm not sure I really understand what you're trying to accomplish here.
But in terms of getting it to compile, your method should look more like this:
IEnumerable<ARPTABLE> GetARPTable()
{
IpTable arp = new IpTable();
IEnumerable<ARPTABLE> arptable = arp.GetArpTable();
foreach (var i in arptable)
{
Device j = DeviceTable.FirstOrDefault(j => j.PhysicalAddress == i.MAC);
if (j != null)
{
i.Description = j.Model ?? j.DeviceName;
}
}
return arptable;
}
As a general note: do not use methods like Select() as a way to simply visit each element in the collection. The expression you give to the Select() method will not actually be evaluated unless you evaluate the IEnumerable<T> that was returned, which you did not in this case. And even if you do evaluate the IEnumerable<T>, it's an inefficient misuse of the method.
Also, while List<T>.ForEach() could be considered convenient by some, it is wasteful to convert an IEnumerable<T> to a List<T> just for the purpose of calling that method. A regular foreach statement works fine and is more efficient.

LINQ is not ment to be used for filling in data. It's a query language and so the Select method returns a new sequence. Just do it with foreach. I would image it could look like this, although I'm not exactly sure if I got the logic right.
foreach(var table in arptable)
{
var device = DeviceTable.SingleOrDefault(...);
if (device != null)
{
table.Description = device.Model ?? device.DeviceName;
}
}
As for your current form
arptable.ToList().ForEach(i =>
this is really not necessary, why cast the sequence to list if you don't have to? Just to use that ForEach? We can do better.
DeviceTable.Where(j => j.PhysicalAddress == i.MAC)
.Select(y => i.Description = y.Model ?? y.DeviceName);
This returns a new sequence, which you are not storing in any local variable. LINQ queries should not have side effect, it's against the lambda calculus, the idea behind LINQ itself.

i like the other answers. if you still want to use linq, this is how you would:
IEnumerable<ARPTABLE> GetARPTable()
{
IpTable arp = new IpTable();
IEnumerable<ARPTABLE> arptable = arp.GetArpTable();
arptable = arptable.Select(i =>
{
Device device = DeviceTable.SingleOrDefault(j => j.PhysicalAddress == i.MAC);
if (device != null)
{
i.Description = device.Model ?? device.DeviceName;
}
return i;
});
return arptable;
}

Related

Are chained IEnumerable's methods Where and Select slower than doing them separately?

I have some inherited code with the following calls:
First:
var pasajesDelServicio = venta.Pasajes.Where(p =>
p.ServicioPasaje.ClaveSolicitud == servicio.RequestKey &&
p.ServicioPasaje.IndiceServicio == servicio.ServiceIndex);
Then:
DatosPasajeros = pasajesDelServicio.ToList().Select(p => {
p.Pasajero.TipoDePasajero = (Dominio.Entidades.Cliente.TipoDePasajero) solicitudVennta.IdTipoPasajero;
return new DatosPasajeroCompra
{
Butaca = p.NumeroAsiento,
Pasajero = p.Pasajero,
IdVentaDetalle = p.IdVentaDetalle,
SubeEn = p.SubeEn,
MenuABordo = p.MenuABordo?.Id,
Precio = p.PrecioBruto
};
})
Would it be more efficient it the Where and Select calls were chained? Like so:
DatosPasajeros = venta.Pasajes.Where(p =>
p.ServicioPasaje.ClaveSolicitud == servicio.RequestKey &&
p.ServicioPasaje.IndiceServicio == servicio.ServiceIndex)
.ToList()
.Select(p => {
p.Pasajero.TipoDePasajero = (Dominio.Entidades.Cliente.TipoDePasajero) solicitudVennta.IdTipoPasajero;
return new DatosPasajeroCompra
{
Butaca = p.NumeroAsiento,
Pasajero = p.Pasajero,
IdVentaDetalle = p.IdVentaDetalle,
SubeEn = p.SubeEn,
MenuABordo = p.MenuABordo?.Id,
Precio = p.PrecioBruto
};
})
I've tried reading trough some docs, but couldn't find my answer. I'm really new to C#.
The chaining itself wouldn't save you much processing since the exact same code will be executed in either case. However, since you have a pasajesDelServicio and a DatosPasajeros variables you would save some memory by only have one variable. Additionally, where you may be able to make this more efficient is by moving the ToList() call after your Select returning only a subset of the data instead of the entire entity (and doing the filtering after).
HTH
Function chaining compiles the same as calling the functions individually. If you take a look at the interface for the IEnuerableExtensions, you'll see that its just an extension method that takes this IEnumerable source. C# Extension methods are just syntactic sugar and will compile as if the function's were called individually. As others have mentioned, its best to not call .ToList<T>() in your case as its a waste.

Changing all values in a lambda/LINQ

I have a dictionary of a list of objects that looks like this
class Client
{
public int Hash { get; set; }
public bool Active { get; set; }
}
var ClientsDict = new Dictionary<string, List<Client>>();
I have no problem setting a single record value in the list
ClientsDict[userId].First(a => a.Hash == hash).Active = true;
How do I do the inverse of that and set all the others in the list to false?
ClientsDict[userId].All().Active = false; //cannot resolve symbol
Note: I wanna avoid a foreach
EDIT:
ClientsDict[userId].ForEach(a => a.Active = false); //this works
If you're mutating things you shouldn't be expressing it as a LINQ transformation. Just use a regular foreach loop:
foreach(var client in ClientsDict[userId])
{
client.Active = false;
}
You can also use the List<T>.ForEach method, but I would advise against it.
Use the Where LINQ method to express the opposite condition and loop over the resulting set:
foreach(var client in ClientsDict[userId].Where(c => c.Hash != hash))
{
client.Active = false;
}
EDIT: the ForEach() method version for comparison:
ClientsDict[userId]
.Where(c => c.Hash != hash)
.ToList()
.ForEach(c => c.Active = false);
I'm usually not a fan of using the ForEach() method, but I have to admit that it doesn't look too bad here. That said, because the method is only available on List<T>, you're forced to call .ToList().
For a more philosophical discussion on whether its use is a good idea or not, see here: “foreach” vs “ForEach”.

Can't use .ToList() with IQueryable<T> [duplicate]

I'm migrating some stuff from one mysql server to a sql server but i can't figure out how to make this code work:
using (var context = new Context())
{
...
foreach (var item in collection)
{
IQueryable<entity> pages = from p in context.pages
where p.Serial == item.Key.ToString()
select p;
foreach (var page in pages)
{
DataManager.AddPageToDocument(page, item.Value);
}
}
Console.WriteLine("Done!");
Console.Read();
}
When it enters into the second foreach (var page in pages) it throws an exception saying:
LINQ to Entities does not recognize the method 'System.String
ToString()' method, and this method cannot be translated into a store
expression.
Anyone know why this happens?
Just save the string to a temp variable and then use that in your expression:
var strItem = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == strItem
select p;
The problem arises because ToString() isn't really executed, it is turned into a MethodGroup and then parsed and translated to SQL. Since there is no ToString() equivalent, the expression fails.
Note:
Make sure you also check out Alex's answer regarding the SqlFunctions helper class that was added later. In many cases it can eliminate the need for the temporary variable.
As others have answered, this breaks because .ToString fails to translate to relevant SQL on the way into the database.
However, Microsoft provides the SqlFunctions class that is a collection of methods that can be used in situations like this.
For this case, what you are looking for here is SqlFunctions.StringConvert:
from p in context.pages
where p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;
Good when the solution with temporary variables is not desirable for whatever reasons.
Similar to SqlFunctions you also have the EntityFunctions (with EF6 obsoleted by DbFunctions) that provides a different set of functions that also are data source agnostic (not limited to e.g. SQL).
The problem is that you are calling ToString in a LINQ to Entities query. That means the parser is trying to convert the ToString call into its equivalent SQL (which isn't possible...hence the exception).
All you have to do is move the ToString call to a separate line:
var keyString = item.Key.ToString();
var pages = from p in context.entities
where p.Serial == keyString
select p;
Cast table to Enumerable, then you call LINQ methods with using ToString() method inside:
var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
But be careful, when you calling AsEnumerable or ToList methods because you will request all data from all entity before this method. In my case above I read all table_name rows by one request.
Had a similar problem.
Solved it by calling ToList() on the entity collection and querying the list.
If the collection is small this is an option.
IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
Hope this helps.
Upgrading to Entity Framework Version 6.2.0 worked for me.
I was previously on Version 6.0.0.
Hope this helps,
Change it like this and it should work:
var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == key
select p;
The reason why the exception is not thrown in the line the LINQ query is declared but in the line of the foreach is the deferred execution feature, i.e. the LINQ query is not executed until you try to access the result. And this happens in the foreach and not earlier.
If you really want to type ToString inside your query, you could write an expression tree visitor that rewrites the call to ToString with a call to the appropriate StringConvert function:
using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;
namespace ToStringRewriting {
class ToStringRewriter : ExpressionVisitor {
static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
.Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));
protected override Expression VisitMethodCall(MethodCallExpression node) {
var method = node.Method;
if (method.Name=="ToString") {
if (node.Object.GetType() == typeof(string)) { return node.Object; }
node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
}
return base.VisitMethodCall(node);
}
}
class Person {
string Name { get; set; }
long SocialSecurityNumber { get; set; }
}
class Program {
void Main() {
Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
var rewriter = new ToStringRewriter();
var finalExpression = rewriter.Visit(expr);
var dcx = new MyDataContext();
var query = dcx.Persons.Where(finalExpression);
}
}
}
In MVC, assume you are searching record(s) based on your requirement or information.
It is working properly.
[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{
EmployeeContext employeeContext = new EmployeeContext();
string searchby=formcollection["SearchBy"];
string value=formcollection["Value"];
if (formcollection["SearchBy"] == "Gender")
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
return View("Index", emplist);
}
else
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
return View("Index", emplist);
}
}
I got the same error in this case:
var result = Db.SystemLog
.Where(log =>
eventTypeValues.Contains(log.EventType)
&& (
search.Contains(log.Id.ToString())
|| log.Message.Contains(search)
|| log.PayLoad.Contains(search)
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
)
)
.OrderByDescending(log => log.Id)
.Select(r => r);
After spending way too much time debugging, I figured out that error appeared in the logic expression.
The first line search.Contains(log.Id.ToString()) does work fine, but the last line that deals with a DateTime object made it fail miserably:
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
Remove the problematic line and problem solved.
I do not fully understand why, but it seems as ToString() is a LINQ expression for strings, but not for Entities. LINQ for Entities deals with database queries like SQL, and SQL has no notion of ToString(). As such, we can not throw ToString() into a .Where() clause.
But how then does the first line work? Instead of ToString(), SQL have CAST and CONVERT, so my best guess so far is that linq for entities uses that in some simple cases. DateTime objects are not always found to be so simple...
My problem was that I had a 'text' data type for this column (due to a migration from sqlite).
Solution: just change the data type to 'nvarchar()' and regenerate the table.
Then Linq accepts the string comparison.
I am working on retiring Telerik Open Access and replacing it with Entity Framework 4.0. I came across same issue that telerik:GridBoundColumn filtering stopped working.
I find out that its not working only on System.String DataTypes. So I found this thread and solved it by just using .List() at the end of my Linq query as follows:
var x = (from y in db.Tables
orderby y.ColumnId descending
select new
{
y.FileName,
y.FileSource,
y.FileType,
FileDepartment = "Claims"
}).ToList();
Just turn the LINQ to Entity query into a LINQ to Objects query (e.g. call ToArray) anytime you need to use a method call in your LINQ query.

Change variable in linq

I have a query something like this
function List<CustomObject2> GetDataPoint(List<CustomObject> listDataPoints)
{
if(listDataPoints.Count == 0)
return;
var startPoint = new CustomObject();
startPoint = listDataPoint.First();
List<CustomObject2> cObjList = from r in listDataPoints
where r != null && r.GetDistance(startPoint) > 100
select new CustomObject2
{
Var1 = r.Var1
}.ToList()
}
The problem here is that, in the beginning the startPoint is set to the first object in listDataPoint. However, after the comparison in the query (GetDistance) I want to reassign startPoint to the value of "r" if the Distance is greater than 100.
Is there any way to do so?
Thanks in advance
No, there is no clean way to do that.
LINQ is essentially a piece of functional programming that has been brought into C#. In functional programming values are immutable (they cannot be changed). Thanks to being functional and using immutality, LINQ queries can be lazily evaluated. It is not uncommon for a LINQ query to be only partly run, or for some parts of the sequence to be evaluated several times. That is safe to do thanks to immutability.
As soon as you want to change a value, you are working against LINQ. In this case you are much better off with a for loop.
Of course there are ways to solve this in a functional manner, as it is possible to solve this in a purely functional language. But in C# it is much cleaner to use a for loop.
You can use a fold:
var cObjList = listDataPoints.Where(r => r != null)
.Aggregate(Tuple.Create(startPoint, new List<CustomObject2>()), (acc, r) => {
if(r.GetDistance(acc.Item1)) {
acc.Item2.Add(new CustomObject2 { Var1 = r.Var1 });
return Tuple.Create(r, acc.Item2);
}
else return acc;
}).Item2;
Since you were not-null checking the elements from listDataPoints, so I assume it may contain null objects. In this case, your code may be vulnerable when the First() element from the list is empty.
//there is no function or procedure in c#;
//function List<CustomObject2> GetDataPoint(List<CustomObject> listDataPoints)
List<CustomObject2> GetDataPoint(List<CustomObject> listDataPoints)
{
var dataPoints = listDataPoints.Where(r => r != null);
if (dataPoints.Empty())
//return; you cant not return anything in a function
return null; //or return an empty list
//return new List<CustomObject2>();
var cObjList = dataPoints.Aggregate(
new Stack<CustomObject>(),
(results, r) =>
{
if (r.GetDistance(results.Peek()) > 100)
results.Add(r);
return results;
})
.Select(r => new CustomObject2(){ Var1 = r.Var1 })
.ToList();
//return directly the line above or do more work with cObjList...
}
Yet, this is still messy and not easily maintained. Like Anders Abel suggests, you are best to go with the for loop for this case :
var cObjList= new List<CustomObject2>();
foreach(var r in dataPoints)
{
if (r.GetDistance(results.Peek()) > 100)
results.Add(new CustomObject2(){ Var1 = r.Var1 });
}
//...
return cObjList;

Linq Method Error IOrderedQueryable

I have a database with a specific id with recorded Time's, I need help on trying to figure out time gap's between an ID's time's e.g 13:05:15 and 13:05:45 though if the time gap is over 10/15 seconds it needs to be recorded so it can be used in say a text file/other data etc. I previously asked a similar question on here, here is what my code looks like so far:
This class is used to manipulate data through the linq var being queried/looped
public class Result
{
public bool LongerThan10Seconds { get; set; }
public int Id { get; set; }
public DateTime CompletionTime { get; set; }
}
This is the foor loop within a separate class which was my original idea
using (var data = new ProjectEntities())
{
Result lastResult = null;
List<Result> dataResults = new List<Result>();
foreach(var subResult in data.Status.Select(x => x.ID).Distinct().Select(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time)))
{
if (lastResult != null)
{
if (subResult.CompletionTime.Subtract(lastResult.CompletionTime).Seconds > 10)
dataResults.Add(subResult);
}
lastResult = subResult;
}
Which I got the error:
Linq.IOrderedQueryAble does not contain a definition for 'CompletionTime' and no Extension method 'CompletionTime' accepting a first argument of type 'System.Linq.IOrderedQueryable.
I changed the for loop to use an object of the manipulation class
foreach(Result subResult in data.AssetStatusHistories.Select(x => x.ID).Distinct().SelectMany(Id => data.AssetStatusHistories.Where(x => x.ID == Id).OrderBy(x => x.TimeStamp)))
{
if (lastResult != null)
{
if (subResult.CompletionTime.Subtract(lastResult.CompletionTime).Seconds > 10)
{
vehicleResults.Add(subResult);
}
}
lastResult = subResult;
}
Though now I get the error: Cannot convert type 'Project.Status' to 'Project.Result'
Does anyone possibly have a solution to get around this I have looked through a few resources but haven't been able to find anything of helps also even on Microsoft's Linq Forum. Any help is much appreciated ! :)
Try adding .ToList() to the end of your LINQ statement, after OrderBy:
var results = data.Status.Select(x => x.ID).Distinct()
.Select(Id => data.Status.Where(x => x.ID == Id)
.OrderBy(x => x.Time)
.ToList();
foreach(var subResult in results))
{
...
}
Also, I think you could modify your LINQ to do a GroupBy of the ID column, but that's something you could do research on if you wish. (Tutorial)
Your linq query (in the second try) will return an IEnumerable of whatever is the element type of data.AssetStatusHistories. I assume this is some kind of IEnumerable<Project.Status>, so you're in fact trying to assign Project.Status objects to an iterator variable (subResult) of type Result, which is why you're getting the error Cannot convert type 'Project.Status' to 'Project.Result'.
So your problem is not really in the linq query, you just need a way to convert your Project.Status objects to Project.Result objects.

Categories

Resources