What is the best way to check IQueryable result set is null - c#

I just want to know what is the best way to check if an IQueryable result has no values.
eg. if we have a method like
public static IQueryable<Table> DisplayAll()
{
var db = new DataContext();
var list= from data in db.Table select data;
return list;
}
and then we do something like this
var list = DisplayAll();
if(list != null)
{
//do something --- in here even if the result set has no values it will
// go to this line. It just say `enumeration yielded no results`
}
Any possible way to check the result set has content or not??
Thanks

list will never be null with LINQ; it will simply represent an "empty collection" if need be. The way to test is with the Any extension method:
if (list.Any()) {
// list has at least one item
}

An exception will be thrown if IQueryable yeilds no result. I use:
using System.Data.Entity; //for Async support in EF
var tQ = await _tableRepository.DisplayAll();
try { return await tQ.ToListAsync(); }
catch { return null; }
to trap the exception and return null; or an empty List if you prefer,
catch { return new List<Table>(); }

Here is what works for me:
public IQueryable SomeFunc()
{
IQueryable result = Repo.SomeLinqQuery();
if (result.GetEnumerator().MoveNext() == false)
{
throw new Exception("Results empty");
}
return result;
}

Related

How to return either <T> or List<T> in .NET Core 6?

Using generics and reflection to do a POST, then process the JSON, then return the correct type with its data.
Problem is that I won't know in advance if the type will be a single object or a list of objects. So my generic return type needs to be either a single thing, or a list of single things. (This is .NET Core 6, so what's why the ! in some places...)
How do I handle this conundrum?
private static async Task<T> ProcessJsonResponse<TX>(HttpResponseMessage response,
string? returnModel = null)
{
var result = await response.Content.ReadAsStringAsync();
if (returnModel != null)
{
var type = Type.GetType(returnModel);
var obj = (T?)Activator.CreateInstance(type!);
var test = type!.GetMethod("FromJson");
object[] parametersArray = { result };
var tim = test!.Invoke(obj, parametersArray);
if (tim is List<T> nowwhat)
{
//can't return List<t> since the return type is T
}
return (T)tim!;
}
//<snip> other non-relevant code ...
}
There are a few issues with this solution that are making it difficult to reason about.
This method is taking data from a dynamic language (JSON) and converting it into a static language (C#). Although generics may seem like they are dynamic, they are actually not. They need to be known at compile time.
With this in mind, the returnModel argument only confuses things. The method is are better off leaving it out
The best thing to do here is to bridge the gap between the dynamic language BEFORE thinking about types. In this case it is pretty easy, JSON arrays will start with a '[' character so the system can make decisions based on that
As #DiplomacyNotWar suggested, you need a return type that encapsulates both types of output. Sum types are great, but personally I like to leave them in the functional world. Instead, you could return a List<T>, which could handle both cases
// 1. Return a List. This type can encapsulate both use cases
private static async Task<List<T>> ProcessJsonResponse<T>(HttpResponseMessage response, bool parseFromJson)
{
var result = await response.Content.ReadAsStringAsync();
// 2: find out what type you will need BEFORE you do anything type based
// this code is inefficient and buggy, but you get the gist
bool isArray = result.TrimStart()[0] == '[';
if (parseFromJson)
{
// 3. Have two completely separate execution paths for two different types
if (isArray)
{
// 4. Different method to original FromJsonList
var test = typeof(T)!.GetMethod("FromJsonList");
object[] parametersArray = { result };
// I am assuming that this will be a static method invoke
return (List<T>)test!.Invoke(null, parametersArray)!;
}
else
{
var obj = (T?)Activator.CreateInstance(typeof(T));
var test = typeof(T)!.GetMethod("FromJson");
object[] parametersArray = { result };
var tim = (T)test!.Invoke(obj, parametersArray)!;
return new List<T> { tim };
}
}
//<snip> other non-relevant code ... SHOLD
}
This is what I came up with - I marked #Shane's answer as correct because it put me on the right track and made me see what I was doing wrong. This isn't complete, either, as there needs to be a bit more error checking, but this is a lot better than what I originally wrote.
private static async Task<List<T>> ProcessJsonResponse<TX>.
(HttpResponseMessage response, string? returnModel = null)
{
var result = await response.Content.ReadAsStringAsync();
if (result == "[]")
{
var obj = (T)Activator.CreateInstance(typeof(T));
return new List<T> { obj };
}
if (returnModel != null)
{
var token = JToken.Parse(result);
switch (token)
{
case JObject:
{
var returnObject = JsonConvert.DeserializeObject<T>(result);
return new List<T> { returnObject };
}
case JArray:
return JsonConvert.DeserializeObject<List<T>>(result);
}
}
var model = JsonConvert.DeserializeObject<T>(result);
Debug.Assert(model != null, nameof(model) + " != null");
return new List<T> { model };
}

'Sequence contains no elements', happening?

It's a WINDOWSFORM
I have combobox, I use this code for auto textboxvalue but I receive this error
Sequence contains no elements
private void cmbOfficeNumber_SelectedIndexChanged(object sender, EventArgs e)
{
using (UnitOfWork db = new UnitOfWork())
if (cmbOfficeNumber.SelectedValue.ToString() != null)
{
txtOfficeName.Text = db.OfficeRepository.GetOfficeNamebyNumber(cmbOfficeNumber.Text);
}
}
And this is my repository code
public string GetOfficeNamebyNumber(string officeNumber)
{
return db.Office.First(g => g.OfficeNumber == officeNumber).OfficeName;
}
EDIT: When using
return db.Office.FirstOrDefault(g => g.OfficeNumber == officeNumber).OfficeName;
I receive a different error
Object reference not set to an instance of an object
If First() results in
Sequence contains no elements
That means the condition in the lambda expression resulted in no hits. Because First requires you to have atleast one match.
If FirstOrDefault().Property results in
Object reference not set to an instance of an object
It means that the lambda expression resulted in no hits, and it returns a default value of the return type. In the case of a reference object it will be null. You then tried to access a property of null which causes the exception.
Simply put. Your problem is that your comparison is returning no hits.
You need to insert a fail safe for this to not crash
Something like:
public string GetOfficeNamebyNumber(string officeNumber)
{
var result = db.Office.FirstOrDefault(g => g.OfficeNumber == officeNumber);
if(result == null)
return string.Empty;
return result.OfficeName;
}
This can also be shortend to
public string GetOfficeNamebyNumber(string officeNumber)
{
var result = db.Office.FirstOrDefault(g => g.OfficeNumber == officeNumber);
return result?.OfficeName ?? string.Empty;
}
Or even
public string GetOfficeNamebyNumber(string officeNumber)
{
return db.Office.FirstOrDefault(g => g.OfficeNumber == officeNumber)?.OfficeName ?? string.Empty;
}
I hope this step by step explanation gives you the information you need to solve the problem.

How to handle if ExecuteScalar is null

I would like your help on how to handle and exception in C# if ExecuteScalar is null. Am trying it but am getting a null reference exceprion. This is my code
public async Task<int> SumItemAsync()
{
Object data = await db.ExecuteScalarAsync<int>("SELECT
SUM(Amount) FROM Spent");
if (data != null)
{
return Convert.ToInt32(data);
}
else
{
return 0;
}
}
I believe that the problem is that your query returns null but you tell ExecuteScalarAsync that is should be an int. Change it to a nullable int.
var data = db.ExecuteScalarAsync<int?>("SELECT SUM(Amount) FROM Spent")
return data ?? 0;
You can then simplify the return expression a little bit.
I actually didn't solve it by using your code but you mentioned that my query returns null but I tell ExecuteScalarAsync that is should be an int. So from your statement I did it like this and it worked. Thanks anyway
public async Task SumItemAsync()
{
var data = await db.ExecuteScalarAsync<String>("SELECT SUM(TotalAmount) FROM Spent");
if (data != null)
{
return data;
}
else
{
return "0.00";
}

How to convert float to system.Collection.generic.IEnumerable

How to convert float to system.Collection.generic.IEnumerable
The actual problem occurs when I try to iterate through the returned IEnumerable.
Code
private ObservableRangeCollection<ReviewInfo> _rating = new ObservableRangeCollection<ReviewInfo>();
public ObservableRangeCollection<ReviewInfo> Review
{
get { return _rating; }
set { _rating = value; OnPropertyChanged("AvRating"); }
}
public async Task GetReview()
{
var ber_id= berProfile.Id;
ResponseInfo<ReviewInfo> res = await nsManager.Instance.GetReview(ber_id);
Device.BeginInvokeOnMainThread(async () => {
IsBusy = false;
if (res.IsError == true)
{
await _page.Alert(res.Message);
}
else
{
if (res.Data != null && res.Data.Count > 0)
{
var temp = res.Data.Average(x => x.Rating);
Review.AddRange(temp);
}
}
});
}
Review is a collection of GogroomCustomer.Models.ReviewInfo, so you should create ReviewInfo object from temp by constructor or set via Property (ex: reviewInfo), then use Add or AddRange method:
Review.Add(reviewInfo);
or
Review.AddRange(new ReviewInfo[] { reviewInfo });
Your question is quite unclear as you didn´t provide what a ReviewInfo is. However your error is quite clear: you´re trying to add a float to a list of ReviewInfo. Of course this won´t work.
From your code I suppose this class has at least a Rating-property or field. Assuming you have a list of those ReviewInfo-instances you probably want to retrieve that object whose Rating-property is the average of all the elements within your list.
If this is the case your solution is simply this:
var temp = res.Data.Average(x => x.Rating);
Review.Add(res.Data.First(x.Rating == tmp));
However as Average returns a double it is fairly possible that there´s no element with exactly this rating in your list, so it´s better to apply some tolerance. However this goes too far on guessing what you actually want.

Not all code paths return a value

I am having this Linq To SQL query which is taking Customer Category from database.The CustCategory will be defined already.Here is the query.
public IList<string> GetAccountType()
{
using (var db = new DataClasses1DataContext())
{
var acctype = db.mem_types.Select(account=>account.CustCategory).Distinct().ToList();
if (acctype != null)
{
return acctype;
}
}
}
Currently I am getting an error that Not all code paths return a value.If I am always certain that the value is there in the database then do I need to check for null,If I need to check for null then how do I handle this.
Can anyone help me with this.
Any suggestions are welcome.
Since Enumerable.ToList never returns null (see the Return Value section of the documentation), you can safely remove the if.
EDIT: Note that, no matter what your database contains, acctype will never be null:
If no value is found in the database, the return value will be an empty list (which is different than null).
If one record is found and its value is null, the return value will be a valid list with one entry, whose value is null. Still, the list itself is not null.
What happens if:
if (acctype != null)
Is null? What is your method supposed to return?
You need to return something
This is not about LINQ to SQL, the method GetAccountType() must return IList<string>. You should return return acctype; and then check this returned list later using Any(), something like:
if(GetAccountType.Any()){
//not empty
}
How about something like this for a fairly clean and readable solution?:
(Note, updated: removed the check for null, since it would clearly not have any effect).
public IList<string> GetAccountType()
{
var acctype = new List<string>();
using (var db = new DataClasses1DataContext())
{
acctype = db.mem_types.Select(
account=>account.CustCategory).Distinct().ToList();
}
return acctype;
}
You need to return a value from your function:
public IList<string> GetAccountType()
{
using (var db = new DataClasses1DataContext())
{
var acctype = db.mem_types.Select(account=>account.CustCategory).Distinct().ToList();
if (acctype != null)
{
return acctype;
}
}
return acctype;
}

Categories

Resources