The Task object created in C# contains the details for the completion of the Action ascribed to the Task. So even if the Action fails the task is still IsCompleted, unless the Task throws an exception or is cancelled.
What I am trying to do is to create a Task-Like class (easy, done that bit) that can store more information about the job that was done.
So for example I want an API to get some accounts, the results may throw an exception, say SQLConnection Exception for example, fine, but also the results may be empty and I want to know why. Is the Database empty or has a filter restricted the view to nothing, for example?
So I could do:
var t = AccountResposistory.GetAccountsAsync(filter, token);
var results = await t;
if(t.Success)
{
return Results.Ok(results)
}
else
{
return Results.Problem(t.ErrorMessage);
}
I was heading down the path of using a custom Task-Like object something like:
public async ServiceTask<IEnumerable<Account>?> GetAccountsAsync(Filter filter, CancellationToken token)
{
// Get accounts with filtering
if(Accounts.Any())
{
return Accounts;
}
else if(filter != null)
{
return ServiceTask.FromError("Filter has restricted all Accounts");
}
else
{
return ServiceTask.FromError("Database doesn't contain any Accounts");
}
}
I can have ServiceTask.FromError return a default T but there's no way (I can see) to access the ServiceTask that's returned by the method to add in the details.
Alternatively, I figured I could have ServiceTask always return a generic Response class and then work with the properties inside ServiceTask to apply the results or messages, but I can't figure out how to do that, how to restrict ServiceTask that T is always a generic class of ServiceResponse
I don't like the idea of throwing exceptions when an exception hasn't happened. It's not a code exception when the database is empty, or the filter has removed all accounts.
Currently, my GetAccountsAsync is returning a Task<Response> and the Response has Success, ErrorMessage and Results properties. It becomes a bit cumbersome to work around this, to get the results of the awaited results. I'm hoping there's a simple way to code this.
Related
I'm trying to write an async method that returns a single object from EF core in my repository. I have the following method:
public async Task<Administrator> GetOne(Guid guid)
{
var admin = await _ctx.Users.SingleOrDefaultAsync(u => u.Guid == guid);
return admin;
}
However this give the following compile time error:
Cannot convert expression type TResult to return type Administrator
If I change it to the following code (i.e. no OrDefault) I get no issues:
public async Task<Administrator> GetOne(Guid guid)
{
var admin = await _ctx.Users.SingleAsync(u => u.Guid == guid);
return admin;
}
What am I doing wrong?
Also a second point, is the code correct in the first place or should I just be returning the result without an await i.e.
public Task<Administrator> GetOne(Guid guid)
{
return _ctx.Users.SingleOrDefaultAsync(u => u.Guid == guid);
}
How to solve your compilation error :
I change it to the following code (i.e. no OrDefault) I get no issues.
What am I doing wrong?
You should try the following to force the compiler to convert the return Type values and the possible default (null) type to Administrator
ctx.Users.SingleOrDefaultAsync(u => u.Guid == guid) as Administrator;
Note :
The above code return null if there is a user with the provided id but it cannot be converted to an Administrator instance.
If you want an exception to be thrown in this case, you can use a 'classic' cast :
(Administrator) ctx.Users.SingleOrDefaultAsync(u => u.Guid == guid);
About 'await' or 'not await'
Also a second point, is the code correct in the first place or should I just be returning the result without an await
My guess is that you want to await your code here. Ultimately, only you really know, but I can't see any abvious reason not to do so, and many reason to do await it.
If you don't await, then you get a Task out of your function, possibly not completed yet, while the rest of your calling code probably depends on the actual Administrator result.
This method signature : public async Task<Administrator> GetOne(Guid guid) should return a Task<Administrator>.
The ctx.Users.SingleOrDefaultAsync call isn't returning a Task<Administrator>.
As for your second point: If you don't need the result just return the Task.
I am using the following code to update data using the MongoDB C# Driver:
public async Task<bool> UpdateFirstName(string id, string firstName)
{
await Collection.UpdateOneAsync(Builders<User>.Filter.Eq(
"_id", new ObjectId(id)),
Builders<User>.Update.Set("firstName", firstName)
.CurrentDate("lastUpdatedDate"));
}
This method returns "bool", because I want to know if the data has been updated successfully. This would be the pseudocode for checking if the data has been updated successfully:
if (data updated successfully)
{
return true;
}
else
{
return false;
}
Does anyone know how to write the code for checking if the data updated successfully? Thanks!
If the method was executed so the update was done, otherwise the method will throw an exception - In case of async it's important to not forget the await (since using async method without await can't ensure your application stay around long enough to retreive the exception)
UpdateOneAsync has a return value of UpdateResult?, which gives you access to the ModifiedCount. As you you UpdateOne a single greater 0 check is enough.
var response = await Collection.UpdateOneAsync(Builders<User>.Filter.Eq(
"_id", new ObjectId(id)),
Builders<User>.Update.Set("firstName", firstName)
.CurrentDate("lastUpdatedDate"));
if response.ModifiedCount > 0 {
// success
}
// failed
This will throw an Exception if the Update is not acknowledged.
I'm working on an application that embeds JSON within the page. Some simplified example:
public ViewResult Page(IEnumerable<LolCat> lolCats)
{
var json = new
{
localCats = ToJson(lolCats),
};
return View( json ); // this gets serialized somewhere in the ASP pipeline
}
IEnumerable<object> ToJson(IEnumerable<LolCat> lolCats)
{
foreach ( var lolCat in lolCats )
yield return new { name = lolCat.name };
}
The JSON gets automatically serialized somewhere down the line in the ASP.NET pipeline.
In this example assume that sometimes a NULL slips into lolCats, throwing an exception. Problem is that the ToJson function might be called at a lot of different places throughout the application.
How do I find out which call to ToJson was the one responsible for the exception? The call stack ends in the Serializer that is actually consuming this IEnumerable, and therefore you don't see the 'original stacktrace'.
One simple fix would be to call ToList() within Page. But I'm looking for a way that doesn't break the laziness of the method.
Due to the deferred nature, you will never get which call to ToJson() actually produced the exception. The collection was never inspected in the first place until it was first enumerated (when it was serialized).
You need to inject into your enumerator some info about what called it.
e.g.,
IEnumerable<object> ToJson(IEnumerable<LolCat> lolCats, string id)
{
try
{
foreach (var lolCat in lolCats)
yield return new { name = lolCat.name };
}
catch (Exception ex)
{
throw new Exception(id, ex); // use a more appropriate exception
}
}
Then it's just a matter of generating an id that could help identify the caller.
I want to make use of the saveChangesAsync in a synchronous function. The situation I want to use this in is for example.
public string getName(int id)
{
var db = new dbContext();
String name= db.names.find(id);
db.log.add(new Log("accessed name");
db.savechangesAsync();
return name;
}
So basically I dont care when the log is actually saved to the database, I just dont want it to slow down my getName function. I want the getname to return and then the log can be saved to the database any time after / during that.
How would I go about achieving this? Nothing is dependant on the time that the log is submitted, So it can take 2 min for all I care.
I have come up with another solution:
private async void UpdateLastComms(string _id)
{
int id = Int32.Parse(_id);
using (var db = new dbContext())
{
db.Devices.Where(x => x.UserId == id).FirstOrDefault().LastComms = DateTime.Now;
await db.SaveChangesAsync();
}
}
and I then can call this function like so UpdateLastComms("5");
How will the this compare to the first and will it execute as I think?
The problem with "fire and forget" methods like this is error handling. If there is an error saving the log to the database, is that something you want to know about?
If you want to silently ignore errors, then you can just ignore the returned task, as in your first example. Your second example uses async void, which is dangerous: if there is a database write error with your second example, the default behavior is to crash the application.
If you want to handle errors by taking some action, then put a try/catch around the body of the method in your second example.
I'm a IT student, second year. We just learned to program with 3 layers, one for getting data with a class, one for manipulating stuff with requests (all of the methods go in here) and one for the working of the program itself. Seeing as the first two go into classes instead of a form I dont know how to show errors.
Example:
We need to make a login system with a webbrowser and some other stuff behind it. So I make the login in a class, but how to check back for errors? I don't think it's normal or even possible to do MessageBox.Show(error); from a class, I can only return stuff, but I want the username/id to be returned if possible.
So in short, what is the best/most accepted way to report errors that are caused by data, so from a class?
Your framework level API's (eg. your layers) should use Exceptions for real errors, and return values to report non-critical errors.
public class Login
{
public bool AccountExists(string name) {
bool exists;
// do checking
return exists;
}
public LoginResult Login(string name, string password) {
// Try login
// If successful
return LoginResult.Success;
// What if the user does not exist?
return LoginResult.AccountNotFound;
// What about an error?
return LoginResult.Error;
}
}
public enum LoginResult
{
None,
AccountNotFound,
Error,
Success
}
In the example above, you can report the status of operations through return values. For LoginResult this could even be a value type (struct) that contains more information about the result (eg. a string message or something). Because these types of operations on non-critical, there is no necessity for exceptions here. Exceptions are costly and not always necessary to report errors.
Now let's talk about a different type of error. Logical developer errors. These should be handled by throwing exceptions. Take this example (assume we have some type Account that has a Role property).
public class Foo
{
public bool IsAdmin(Account account) {
if (account == null) {
throw new System.ArgumentNullException("You cannot pass a null account!");
}
return account.Role == "Admin";
}
}
We know as a developer that the account should not be null, so we should check for it and throw an exception if it is. If this exception is ever thrown, its a bug in the calling code and should be fixed not to pass in a null value.
Now that I've given two rough scenarios, how does this apply to your question? These are API's. Whatever your UI layer is, whether it be a WinForm, WPF Window, WebForm, or some other UI, the UI only has to use the API. The API is responsible for reporting information that can be usable by the UI, and the UI is responsible for displaying info in whatever way is best suited for that UI.
The framework API layers should never be responsible for reporting an error to the user with a UI. They should only be responsible for reporting errors to the developer who can take the result, and wire it up to the UI layer in some fashion. You would never display a message box or write to a console from a framework API for example. You would return some result that the UI can use to display its own message.
I highly recommend that you read Framework Design Guidelines. It covers a lot of this material and is an extremely great read.
You should have a class which validates the data object and returns error information. Then your front-end code can ask this class to validate the data and show any error messages that get returned.
var username = GetUserName();
var password = GetPassword();
var validationResult = new Validator().ValidateLogin(username, password);
if(validationResult.ErrorMessage != null) {
MessageBox.Show(validationResult.ErrorMessage);
} else {
// Do what you would have done.
}
If any errors occur that are outside of the expected logic flow, they should throw an exception.
Well you can use Exceptions. You Just throw the exception, it is up to the caller on what to do with the exception.
class Login
{
public Login()
{
}
public bool CheckLogin(string userName, string password)
{
// Do your validation here.
If every thing goes fine
return True.
else
throw Exception("custom message.");
}
}
class Input //class which takes input.
{
Login login = new Login();
public void TakeInput(string username, string password)
{
try
{
login.CheckLogin(username, password);
}
catch(Exception ex)
{
MessageBox.show(ex.message);
}
}
}