I am working on one project and I have a question regarding the converting type. I want to create simple search for my project, but it can't return result with this message :
Error 1 Cannot implicitly convert type 'System.Collections.Generic.List' to 'EmployeeDataAccess.TimeWorkMonthly'
public TimeWorkMonthly Get(int id)
{
using (EmployeeDbEntities Entities = new EmployeeDbEntities())
{
List<TimeWorkMonthly> persons = new List<TimeWorkMonthly>();
var result = Entities.TimeWorkMonthlies
.Where(e => e.KartNo == id)
.Select(e => e)
.ToList();
return result.ToList();
}
}
The return type of your method is TimeWorkMonthlies but inside the method body return List<TimeWorkMonthlies>
You should either
change your method return type to IEnumerable<TimeWorkMonthlies>
(You could use List<TimeWorkMonthlies> but using an interface to abstract a collection type is better for many reasons)
Use FirstOrDefault, First, SingleOrDefault or Single extension methods of IEnumerable if you aim to return only one element and you do not care about anything except for the first element
Which of those methods is better depends on your data and search criteria - i.e. whether you expect this ID to be unique or not.
From your semantics it looks like you're doing a sort of repository like ID lookup, so my guess would be solution 2) and using Single or SingleOrDefault
The last choice is how you want your program to behave if nothing is found by ID
If you want an exception, use Single
If you want a null use SingleOrDefault
In Summary, all you have to do is change your last line of code to
return result.Single();
(And ofcourse, you don't need a call to ToList() just before that)
Your method signature indicates you just want to return a single object. But you're returning a List of objects. Using .ToList() is not appropriate when you just want to return one object. There are four appropriate extension methods:
First - will return the first item from the collection and throw an exception if the collection is empty.
FirstOrDefault - will return the first item in the collection, or the default of the type if the collection is empty.
Single - if there is one item in the collection, it will return it. If there are no items in the collection an exception is thrown. If there are multiple items in the collection, an exception is thrown.
SingleOrDefault - if there is one item in the collection it will return it. If there are no items in the collection it will return the default value for the type. If there are multiple items in the collection it will thrown an exception.
Since you're searching by ID, you probably don't ever to expect to match two or more elements. So that rules out First and FirstOrDefault. You should use Single or SingleOrDefault depending on what you want the behavior to be if there is no item found that has the matching ID.
public TimeWorkMonthly Get(int id)
{
using (EmployeeDbEntities Entities = new EmployeeDbEntities())
{
var result = Entities.TimeWorkMonthlies.Where(e => e.KartNo == id).Single();
return result;
}
}
Note I eliminated the persons variable because you never did anything with it. And your usage of the .Select extension method was superflous since you just selected the same object already being iterated over. Select is for when you want to transform the object.
The problem is your qry only . If you want to convert it with Tolist() function you have to change your qry
like this
public TimeWorkMonthly Get(int id)
{
using (EmployeeDbEntities Entities = new EmployeeDbEntities())
{
var result = from x in Entities.TimeWorkMonthlies
Where x.KartNo == id
Select x;
return result.ToList();
}
}
You can now convert it to list by tolist() and use it according to your need.
Related
So currently, you can imagine I have 1 method that is the constructor that funcitons like
info.PersonalInfo=getPersonalInfo(Id);
info.MedicalInfo=getMedicalInfo(Id);
Thing is, all of those get data and get binarys are repeating 95% of the code
using (CVDataEntities data = new CVDataEntities())
{
var temp = data.PersonalInfo.Where(m => m.Id == Id).FirstOrDefault();
return temp;
}
The only thing that changes is instead of PersonalInfo its MedicalInfo.
I thought of using a switch and just sending a number as the selector for which specific object I would want.
But the problem is the method is made so that it can only return
public IEnumerable<PersonalInfo> getPersonalInfo (string Id)
Is there any way for me to make a IEnumerable that lets me return any object, or is there a better way to go about this. I want to do it mostly to reduce the code from 400 lines down to 200 at most.
Try using generic methods, you will be able to specify the return type of your function when you call it. This could make your code look like this :
public IEnumerable<T> getInfo<T>(string id)
{
// Some code
}
// Calling the function
info.PersonalInfo = getInfo<PersonalInfo>(Id);
info.MedicalInfo = getInfo<MedicalInfo>(Id);
But be careful while using it, cause the compiler won't know what type T is (it is only defined at runtime) so it could lead to some errors while processing the data (like missing properties / methods exclusive to a specific type)
EDIT : Johnathan Barclay made a good point by commenting that the // some code bit is relevant and asked "How would the correct property be selected on data? How do you access an Id property on T?"
To get the correct property and access an Id property, you could use System.Reflection and add a string parameter to get the name of the property you want to use, and another to give the Id property name to the function:
public IEnumerable<T> getInfo<T>(string id, string propertyToReadName, string propertyToCompareName)
{
using (CVDataEntities data = new CVDataEntities())
{
// Getting the enumerable not filtered first
IEnumerable<T> unfilteredList = (IEnumerable<T>)data.GetType() // Get the type
.GetProperty(propertyToReadName, typeof(T)) // Get the property (PersonalInfo or MedicalInfo)
.GetValue(data); // Get the value of this property in the `data` instance
// Filtering the list
IEnumerable<T> filteredList = unfilteredList.Where(m =>
typeof(T).GetProperty(propertyToCompareName) // Get the "id" property using parameter
.GetValue(m) // Get the "id" value of m instance
.Equals(id)); // Check if it equals the id given as parameter
return filteredList;
}
}
// Calling the function
info.PersonalInfo = getInfo<PersonalInfo>(Id, "PersonalInfo", "Id");
info.MedicalInfo = getInfo<MedicalInfo>(Id, "MedicalInfo", "Id");
If you want to return a single element instead of an IEnumerable don't forget to change the return type of the function from IEnumerable<T> to T and add .FirstOrDefault() at the return line
Note that you could also give another value to the parameter propertyToCompareName and make a comparison to another property of the T class
This code snippet returns me an error,
public List<auto> autoSelect()
{
return autoSelect(DateTime.Today);
}
public List<auto> autoSelect(DateTime date)
{
var onderhoudAuto = (from onderhoud in db.onderhouds
where onderhoud.uitvoerdatum != DateTime.Today
select onderhoud)
.FirstOrDefault();
List<string> autos = (from auto in db.autos
where auto.autoId.Equals(onderhoudAuto)
select auto)
.FirstOrDefault();
return autos;
}
I tried convert the var to a list with .ToList(); although this doesn't work. Does anyone have any suggestions?
I tried convert the var to a list
No, you do not. var is not actually a data type - it is resolved by the compiler. A tooltip should show you the real type.
Your problem is different:
Looking at your code, we can see:
The method autoSelect signature states that the return type is List<auto>
public List<auto> autoSelect(DateTime date)
The variable autos type is List<string>
List<string> autos = [...etc...]
return autos;
You return autos, but it is not possible to return a List<string> when a List<auto> is expected.
So it has nothing to do with var - it is simply you selecting as single property and returning a list of strings, but that is not the type the method is supposed to return.
If you use FirstOrDefault() after your linq query, you are saying you want the first element (or the default -usually null- for the datatype if none matches) in the LINQ query, not a list of elements.
If you want a list of elements, use ToList() on the linq query, not try to convert a single entity to a list.
If you, for some reason, want a list of a single entity, then create a list (with new List<type>()) and then add your entity (of the same type as your list) to the list.
We have a data bound control that is populated with a list of banks.
I can figure out how to Read, Insert and Delete real accounts.
On the form, however, our client needs a way to select a "None" option (some customers use cash).
I cannot insert a "None" entry into the banks data table because of how the data tables are linked to other data tables.
I attempted to insert a "None" record before returning the DataContext so that this option would always appear at the top of the list, but there doesn't appear to be any way to get the data into the collection.
public System.Data.Linq.Table<bank> Banks
{
get
{
return this.GetTable<bank>();
}
}
I was able to insert a fake record into an IQueryable list:
internal IQueryable<bank> GetBankInfo(bool insertBlank = false)
{
var list = (from item in banks
orderby item.bank_name
select item).ToList();
if (insertBlank)
{
var item = new bank()
{
bank_name = String.Empty,
contact_name = String.Empty,
contact_phone_num = String.Empty,
routing_num = "None",
source = String.Empty,
fact_routing_num = String.Empty,
note_id = (int?)null,
};
list.Insert(0, item);
}
return (IQueryable<bank>)list;
}
But, I can't use this because it throws a TargetInvocationException:
Unable to cast object of type 'System.Collections.Generic.List1[Billing.DataDB.bank]' to type 'System.Linq.IQueryable1[Billing.DataDB.bank]'.
How do I insert a blank record for the DataContext display?
I do not want the blank record in the database.
You can fix this two ways:
First, change the return type of the method to IEnumerable<T>...
internal IEnumerable<bank> GetBankInfo(bool insertBlank = false)
...and leave out the cast in the return statement. It'll be redundant, as List<T> implements IEnumerable<T>
return list;
Based on what you mentioned in your question, this should work.
Second way:
If you're doing something else with that list/queryable/whatever, such that you really need it to really be IQueryable<T>, that's even easier. Leave the function return type as it was:
internal IQueryable<bank> GetBankInfo(bool insertBlank = false)
And return this:
return list.AsQueryable();
Here's the problem:
I was able to insert a fake record into an IQueryable list:
Actually, you weren't. You enumerated the results of an IQueryable into a new List<T> instance: That's what happened in your ToList() call. It's not a cast, it creates a new collection object. Then you inserted a fake record into the List<T>.
The thing you were casting to IQueryable<bank> wasn't the IQueryable<bank> that you started with; it was a completely different object which contained all the items that were enumerated by the IQueryable<bank>.
The purpose of IQueryable is so the actual query can be executed remotely, as with Entity Framework for example. However, once you materialize the query in the ToList() call, the remote ship has sailed the barn out of the station. Since IQueryable<T> inherits from IEnumerable<T>, I'd tend to use the latter for a general "reference to sequence of things" type. Any method that takes IEnumerable<T> for a parameter will happily consume an IQueryable<T> instead.
I have a simple LINQ issue. I'm trying to query a list and match a member ID with a lists ID and then return some text:
var IDtext = IDMethod(MembID);
var t = from ctext in cIDtext
where ctext.cid.ToString() == MembID
select cxt.C_ID;
ViewBag.thisID = t.ToString();
All this returns is:
System.Linq.Enumerable+WhereSelectArrayIterator`2[pu15.Models.C_ID,System.Int32]
But this never changes or shows what I want it to show.
What am I doing wrong?
You want to use Single or SingleOrDefault or First or FirstOrDefault, depending on what you want exactly, e.g.:
ViewBag.thisID = t.Single();
Single will throw an exception if there is more than one result or if there are no results.
If you'll use SingleOrDefault instead it will return null in case of more than one result or no result.
First on the other hand will fail only for an empty collection - when there is more than one element it will return the first one.
The most tolerant, FirstOrDefault will return null for empty result list, and the first element if there is more than one.
You have to remember that a where clause returns an IEnumerable<T> and not just one value. You have to take the first or a specific element of that collection and show it or if it is a collection of objects, show a property from that element.
In your case this would be:
var IDtext = IDMethod(MembID);
var allIDs = from ctext in cIDtext
where ctext.cid.ToString() == MembID
select cxt.C_ID;
var single = allIDs.SingleOrDefault();
//or:
var first = allIDs.FirstOrDefault();
ViewBag.thisID = single.ToString();
//or
ViewBag.thisID = first.ToString();
you can use method syntax, skip the Where clause, and just use First()
ViewBag.thisID = cIDtext
.First(c => c.cid.ToString() == MembID)
.C_ID.ToString();
t is an IEnumerable that may return many items. If you only want to use one of these items, you need to call t.First() or t.Single() ,eg:
ViewBag.thisID = t.First().ToString();
The difference between the two methods is that Single will throw an exception if there are more than 1 items in the results, while First will only throw if there are no matching results.
If you want to check that a result actually exists, you can use FirstOrDefault or SingleOrDefault, eg:
var id=t.FirstOrDefault();
if (id!=null)
ViewBag.thisID = id.ToString();
You can condense the call if you pass the condition as the predicate to First, FirstDefault etc:
var id=cIDtext.FirstOrDefault(ctext=> ctext.cid.ToString() == MembID);
The issue is that t is an IEnumerable and ToString will just give you the name of the type. You need to turn your enumeration into something like a comma separated list
ViewBag.thisID = string.Join(",", t.Select(v => v.ToString()));
Or more likely you only want one value and you need to use either First or Single.
ViewBag.thisID = t.First().ToString();
or
ViewBag.thisID = t.Single().ToString();
Use First if there are multiple return values and you only want the first, or you can use Last if you wan the last one. If the query should return only one value then you should use Single. There are also FirstOrDefault, LastOrDefault, and SingleOrDefault that will return a default value if the enumeration is empty or in the case of Single if the enumeration has more than one value. Single, First, and Last will throw an exception if the enumeration is empty or in the case of Single if the enumeration has more than one value. Just choose the appropriate one for your use case.
I have client code that is virtually identical in four different methods (the difference being the particular Web API RESTful method being called and the corresponding manipulated generic list).
In three of the four cases, I can break out of the while loop (see How can I safely loop until there is nothing more to do without using a "placeholder" while conditon?) like so:
if (arr.Count <= 0) break;
...but in one case, that causes an NRE once there is no more data returned from the RESTful method. In that method, I have to use:
if (null == arr) break;
I now know why, thus this:
UPDATE
The reason for the different behavior was because the Repository code differs. Therefore, I am changing the question from "Why would checking JArray.Count work in most instances, but cause an NRE in one specific case?"
Here is how it's done in the three methods where checking for array count works:
public IEnumerable<Subdepartment> Get(int ID, int CountToFetch)
{
return subdepartments.Where(i => i.Id > ID).Take(CountToFetch);
}
...and here's the "alternate version" contained in RedemptionRepository:
public IEnumerable<Redemption> Get(int ID, int CountToFetch)
{
IEnumerable<Redemption> redempts = null;
if (redemptions.Where(i => i.Id > ID).Take(CountToFetch).Count() > 0)
{
redempts = redemptions.Where(i => i.Id > ID).Take(CountToFetch);
}
return redempts;
}
So, to be consistent with all four methods, I can either make all the other Repository methods like the above (returning null when no data is found), and change the test condition in the client to nullification, OR I can revert the Redemption repository code to be like it was formerly/like the others.
So the question: Which is the preferred method (no pun intended)?
You should definitely change the last method to match the previous ones:
public IEnumerable<Redemption> Get(int ID, int CountToFetch)
{
return redemptions.Where(i => i.Id > ID).Take(CountToFetch);
}
And NullReferenceException is not the only reason. Because LINQ is lazy and execution is deferred the other approach executes the query twice! Once to get Count() and second one to get actual collection of results. If you really want to return null instead of empty collection use should get following:
public IEnumerable<Redemption> Get(int ID, int CountToFetch)
{
var redempts = redemptions.Where(i => i.Id > ID).Take(CountToFetch).ToList();
if (redemptions.Any())
{
return redempts;
}
return null;
}
You should return an empty collection.
All Linq methods (that I am aware of) that have an IEnumerable-based return type return empty collections rather than null. Returning null from a method prevents you from chaining method calls since you now need to check for null to avoid an NullReferenceExcpetion (as you've discovered).