I have a question related to an Enumerable and list of integers. I have the below code that is throwing an error stating "'int' does not contain a definition for 'Field' and has some invalid arguments." I'm sure this is something easy, but was wondering if anyone could help out. Thanks!
public static IList<Site> GetSiteFromSites(DataTable data)
{
var linqRegions = Enumerable.Empty<int>();
IList<Site> sites = data.AsEnumerable().Select(r =>
{
return new Site()
{
id = r.Field<string>("id"),
name = r.Field<string>("name"),
address_line1 = r.Field<string>("address_line1"),
address_line2 = r.Field<string>("address_line2"),
post_code = r.Field<string>("post_code"),
county = r.Field<string>("county"),
city = r.Field<string>("city"),
phone_number = r.Field<string>("phone_number"),
regions = linqRegions.Where(u => u.Field<int>("regions") == r.Field<int>("regions")).ToList().Select(z => z.Field<int>("regions")).ToList()
//error is thrown for above line of code "regions"
};
}).ToList();
return sites;
}
The variable linqRegions is an empty enumerable of ints.
In the line where the error is thrown, you are calling the Where() method where you try to get the Field property of each element. Because each element is an int, it throws the 'int' does not contain a definition for 'Field' error.
Change your line with the error to this:
regions = linqRegions.Where(u => u == r.Field<int>("regions")).ToList()
This will always return zero elements (because linqRegions is empty), so don't forget to populate it. And even then, the line doesn't make any sense because it will always return a list of the same values (values equal to r.Field<int>("regions")).
Related
I am trying use LINQ in a C# program to get information from a database. I have found a lot of examples showing basic to advanced queries, but i get a error when i try to build. See basic LINQ example below:
The class with the LINQ
public class StdFy
{
public object GetStdFy(DrillholeEntities ddb)
{
try
{
var myList = ((from t1 in ddb.DTM_QAQC_BLK_STD from t2 in ddb.DTM_STANDARDSASSAY.Where(x=> t1.STANDARDID==x.STANDARDID && t1.ASSAYVALUE==x.STANDARDVALUE)
select new
{
CHECKID = t1.CHECKID,
STANDARDID = t1.STANDARDID,
PRIORITY = t1.ASSAY_PRIORITY,
NAME = t1.ASSAYNAME,
ASSAYVALUE = t1.ASSAYVALUE,
STANDARDVALUE = t2.STANDARDVALUE,
STANDARDDEVIATION = t2.STANDARDDEVIATION,
NORMALIZACION = (t2.STANDARDVALUE- t1.ASSAYVALUE)/ t2.STANDARDDEVIATION,
LABJOBNO = t1.LABJOBNO,
LOADDATE = t1.RETURNDATE
})).OrderBy(x => x.LOADDATE).ToList();
return myList;
}
catch (Exception)
{
throw new NotImplementedException();
}
}
}
and then
DrillholeEntities ddb;
StdFy stdFy = new StdFy();
using (ddb = new DrillholeEntities())
{
IOrderedEnumerable<DTM_QAQC_BLK_STD> datosTodos;
datosTodos = stdFy.GetStdFy(ddb);
}
and when i buuild the project i get the error
Severity Code Description Project File Line Suppression State
Error CS0266 Cannot implicitly convert type 'object' to
'System.Linq.IOrderedEnumerable'.
An explicit conversion exists (are you missing a
cast?) Inspinia_MVC5 C:\Users\chachl9\Documents\Visual Studio
2015\Projects\MVC5_Full_Version\Inspinia_MVC5\Controllers\GraphsController.cs 55 Active
You are returning a list of objects of an anonymous type by
select new { ... }
thats may the reason why you use object as return type of your method.
Than you assign the return value to a specific type. Thats the reason why the exception ist thrown.
To get this work I would implement a new class which contains all your needed properties. Use it in
select new MyNewClass { Property1 = ...}
change the return type of your method from object to IEnumerable<MyNewClass>. Aswell the variable you assign the return value to.
You should also use PascalCase properties as mentioned by Gert Arnold in the comments.
Also mixing the select from in where LINQ syntax with the Lambda methods .Where(x => ...) make it hard to read.
I have a query where one property is a path "/Primary/secondary/tertiary/.../.../"
My task is to split this path by the slashes, so every sub-path can be assigned as a property in the query result.
The problem is, that the length varies. Some paths have a post-split array length of 1, some of 7. So I need to have 7 different category columns:
var result = MySource.Where(ms => ...)
.Select(ms => new {
ID = ms.ID,
Name = ms.Name,
Category1 = ms.Path.Split('/')[0],
Category2 = ms.Path.Split('/')[1] //exception
.... //exception
Category7 = ms.Path.Split('/')[6] //exception
});
After the path gets split, the resulting array is of various length (1 - 7) leading into an ArgumentOutOfRangeException. How can I circumvent this exceptions?
I have tried using the nullcoalescence operator ms.Path.Split('/')[1] ?? "N/A", which did not help, because there is no result but an exception thrown. Because of this every shorthand-if statement will fail as well.
Is there a way to catch the exception (wrap in try catch block?) so I can assign a default value if the array is out of bounds?
Your modeling seems a little broken. Instead of a flattened set of properties, populate a single collection. Something like this:
Select(ms => new {
ID = ms.ID,
Name = ms.Name,
Categories = ms.Path.Split('/')
})
Going a step further, you can create an actual (non-anonymous) model to hold this information, encapsulating the logic of category range checking. Something like:
Select(ms => new SomeObject(
ms.ID,
ms.Name,
ms.Path.Split('/')
))
Then in SomeObject you can have all sorts of logic, for example:
In the constructor you can perform input checking on the values, including the count of categories supplied, to ensure the object is valid.
You can keep the collection of categories private and expose properties for 1-7 if you really need to, which internally perform this check. (Though I really don't recommend that. It creates an unnecessary point of change for something that's already handled by a collection, indexing values.) Something like:
public string Category1
{
get
{
if (categories.Length < 1)
return string.Empty;
return categories[0];
}
}
Maybe throw an exception instead of returning an empty string? Maybe do something else? The point is to encapsulate this logic within an object instead of in a LINQ query or in consuming code.
you can do
Category7 = ms.Path.Split('/').ElementAtOrDefault(6) ?? "N/A",
see demo: https://dotnetfiddle.net/4nTBhq
ElementAtOrDefault return the element at index (for example 6, like [6]) but if out of bound return null.
optimized, without calling Split multiple times:
.Select(ms => {
var categories = ms.Path.Split('/');
return new {
ID = ms.ID,
Name = ms.Name,
...
Category7 = categories.ElementAtOrDefault(6),
};
})
enum KnownError
{
[StringValue("CODE-001")]
CODE001,
[StringValue("CODE-002")]
CODE002,
[StringValue("CODE-003")]
CODE003,
[StringValue("CODE-004")]
CODE004,
[StringValue("CODE-005")]
CODE005
}
List<string> errors = {"ahah", "eheh", "CODE-005", "uhuh"};
Let's say i have a list of string errors.
How can I check if any error is "known"?
bool ContainsKnownError(List<string> error)
{
return errors.Where(error => Enum.IsDefined(typeof(KnownError), error) == true).Count() > 0;
}
This doesn't seem to work.
How can I access StringValue inside the linq query without having to compare each string?
EDIT
I tried #AK_ solution, using Intersect, but I'm getting this compilation error:
The type arguments for method 'System.Linq.Enumerable.Intersect<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Collections.Generic.IEnumerable<TSource>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
The real scenario is an Error object with a string field with the code like this
class Error { string code; }
List<Error> errors = GetErrors();
var knownErrors = Enum.GetValues(typeof(KnownError));
bool exists = errors.Select(error => error.code).Intersect(knownErrors).Any();
var knownErrors = Enum.GetValues(typeof(KnownError));
return errors.Contains(error => knownErrors.Contains(error));
//or cooler:
return errors.Intersect(knownErrors).Count() > 0;
Zack's comment is correct:return errors.Intersect(knownErrors).Any is better...
+1 him :-)
Enum.IsDefined expects an object-wrapped instance of the enum. Passing a string name does not produce the desired result.
This should work:
KnownError ignore;
var res = errors.Any(errorCode => Enum.TryParse<KnownError>(errorCode, out ignore));
Note the use of LINQ's Any in place of comparing Count() to zero: this approach is more efficient, because it stops as soon as it finds the first match.
I am returning a statement that gets a number of users to display all in which are already in alphabetical order. I would like to display this result also adding to it the ability to group all names that start with the same letter together so i could create some sort of label or index to it.
Code:
return new UserService().GetUsers(id.Value, pageNumber: _pageNum).GroupBy(x=>x.Name.Substring(0,1));
Problem: the return statement throws an error stating:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<System.Linq.IGrouping<string,ProjectName.Shared.Models.Views.User>>' to 'System.Collections.Generic.IEnumerable<ProjectName.Shared.Models.Views.User>'. An explicit conversion exists (are you missing a cast?)
How can i go about fixing this?
The problem is that the statement creates a grouping, but you are trying to return a plain list of users.
There are two ways of fixing this:
You may want to return an IDictionary<string,IList<User>> instead of IEnumerable<User> (you could use char as the key as well) , or
You may want to order users by name, instead of grouping, if you want to return a plain list.
Here is how you make it an IDictionary<string,IList<User>>:
IDictionary<string,IList<User>> res = new UserService()
.GetUsers(id.Value, pageNumber: _pageNum)
.GroupBy(x=>x.Name.Substring(0,1))
.ToDictionary(
g => g.Key
, g => (IList<User>)g.ToList()
);
EDIT : (in response to this comment: "I want to group so that all names that start with A are under the A header and so on so I am just changing the appearance not really sorting")
In cases like this, you do not need grouping (although you can certainly make use of it if you wish to do so). You could order the data instead, in your LINQ, in your SP, or otherwise, and then provide the headers visually by inserting then at the boundaries between the names that start in different letters.
Here is a very crude example of doing it in a program that prints to console. You should be able to adapt it to your particular visuals with little work:
// I assume that you know how to write the GetUsersSortedByName method
IList<User> sortedUsers = GetUsersSortedByName();
char? lastSeenFirstLetter = null;
// The loop below assumes that there are no users with empty names
foreach (User u in sortedUsers) {
if (u.Name[0] != lastSeenFirstLetter) {
Console.WriteLine("==== Header: users with names in {0}", u.Name[0]);
lastSeenFirstLetter = u.Name[0];
}
Console.WriteLine(u); // Finally, display the user
}
You need to return the appropriate type from the method:
public IEnumerable<IGrouping<string, User>> GetUserIndex()
{
return new UserService().GetUsers(id.Value, pageNumber: _pageNum).GroupBy(x=>x.Name.Substring(0,1));
}
Then you can iterate each group like this:
var userIndex = GetUserIndex();
foreach(var group in userIndex)
{
string currentLetter = group.Key;
foreach(User user in group)
{
//do something
}
}
You said you have a method and you would like it to return something like this:
return new UserService()
.GetUsers(id.Value, pageNumber: _pageNum)
.GroupBy(x=>x.Name.Substring(0,1));
Based on the compiler error you're getting, it is obvious that your method looks something like this:
public IEnumerable<ProjectName.Shared.Models.Views.User> GetTheUsers() {
// do something here...
return new UserService()
.GetUsers(id.Value, pageNumber: _pageNum)
.GroupBy(x=>x.Name.Substring(0,1));
}
You can't do that. Leave it like you designed it initially, without the grouping:
public IEnumerable<ProjectName.Shared.Models.Views.User> GetTheUsers() {
// do something here...
return new UserService().GetUsers(id.Value, pageNumber: _pageNum);
}
and then, when you call it, do the grouping on the spot:
public void Foo() {
// do something here...
var ungroupedUsers = GetTheUsers();
var groupedUsers = ungroupedUsers.GroupBy(x=>x.Name.Substring(0,1))
// and now use the groupedUsers accordingly
}
Problem: the return statement throws an error stating:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<System.Linq.IGrouping<string,ProjectName.Shared.Models.Views.User>>' to 'System.Collections.Generic.IEnumerable<ProjectName.Shared.Models.Views.User>'. An explicit conversion exists (are you missing a cast?)
How can i go about fixing this?
Try this:
return new UserService()
.GetUsers(id.Value, pageNumber: _pageNum)
.GroupBy(x=>x.Name.Substring(0,1))
.Select(g=>g); //<-----
I have this code in my controller:
public ActionResult Details(int id)
{
using (var db = new MatchGamingEntities())
{
var MyMatches = from m in db.Matches
join n in db.MatchTypes on m.MatchTypeId equals n.MatchTypeId
where m.MatchId == id
select new MatchesViewModel
{
MatchType = n.MatchTypeName,
MatchId = m.MatchId,
MatchName = m.MatchTitle,
MatchDescription = m.MatchDescription,
Wager = m.Wager
};
ViewBag.MyMatches = MyMatches.ToList();
return View(MyMatches.ToList());
}
}
I want to be able to make this query only return a single result and I can use MyMatches as a MatchesViewModel object so I do not have to use the ToList() feature thus getting rid of the IEnumberable on the view page #model IEnumerable<MatchGaming.Models.MatchesViewModel> so I can turn it into this: #model MatchGaming.Models.MatchesViewModel
You can call the appropriately named extension method Enumerable.Single().
Here are some other related methods and the differences between them depending on how many elements there are in the collection you are querying:
No elements More than one element
First Throws exception First element returned
FirstOrDefault default(T) First element returned
Single Throws exception Throws exception
SingleOrDefault default(T) Throws exception
.Single() will return the one object. If there's more than one, or none, it'll throw an exception.
.First() will just give you the first, only throwing an exception when there are no items.
.FirstOrDefault() is the same as .First(), but giving you NULL when there are no items.
I usually use .UniqueResult<>(). It returns the single result or a null reference.