I have the following action:
public async Task<ActionResult> Cart(bool refresh = false, bool mobile = false)
{
var user = await Auth.GetUserAsync();
//rest of the code
}
Which is being called twice by 2 AJAX calls at the same time (one to render mobile partial, other normal page).
$(document).ready(function () {
$("basket").html('<i class="fa fa-spinner fa-spin"></i>');
$('basket').load(PartialURL + "/Cart");
$('MobileBasket').load(PartialURL + "/Cart?mobile=true");
});
The real problem occurs in Auth.GetUserAsync() function.
Code:
public static async Task<User> GetUserAsync()
{
if (HttpContext.Current == null || HttpContext.Current.Request == null
|| HttpContext.Current.User == null || HttpContext.Current.Request.IsAuthenticated == false)
return null;
//if session does not exist, but user is logged in, fill session information
if (HttpContext.Current.Session[sessionKey] == null && HttpContext.Current.Request.IsAuthenticated)
using (var dal = new DAL.DAL())
{
//load user from DB
var user = await dal.SingleOrDefaultAsync<User>(m => m.Email == HttpContext.Current.User.Identity.Name);
HttpContext.Current.Session[sessionKey] = user;
}
return HttpContext.Current.Session[sessionKey] as User;
}
In one of those calls, the function returns user normally, but the other call produces following error (screenshot from fiddler). Notice how the first call was successful.
Code for SingleOrDefaultAsync is following:
public Task<T> SingleOrDefaultAsync<T>(Expression<Func<T, bool>> predicate) where T : class
{
return _context.Set<T>().SingleOrDefaultAsync(predicate);
}
I have checked while debugging, and email is present in both of the requests, _context is not null, and the user with requested email exists, but one of those always returns an error. Error is returned randomly. Sometimes in first, sometimes in second AJAX call.
Can someone please tell me what is causing this error? Any help is greatly appreciated.
I would assume, because of the other checks you have in place, that HttpContext.Current.User.Identity.Name (or even HttpContext.Current.User.Identity) is null, and that that's what's causing the error.
This might be so if the first request has caused the authentication process to start, but that process is not yet complete - so that the null checks you have succeed, but the subsequent member access fails. In other words, you have a timing issue / race condition. That's just a guess, though.
Update: following my suggestion to store the name in a variable and use the variable in the lambda, everything worked. But why?
My theory is that the expression passed to your SingleOrDefaultAsync method included the expression HttpContext.User.Identity.Name, rather than the value of that expression.
This expression was then evaluated in your DAL, where HttpContext.Current was (presumably) null (assuming your DAL is not in your web project). QED?
Related
I have a method in my controller class that returns a tuple and runs right before the rest of the controller executes:
private async Task<(int userId, int userRoleId, bool accountVerified)> GetUserRole()
{
var identifier = User.Identity.Name;
var user = await _context.User.Where(x => x.UserEmail == User.identifier).FirstOrDefaultAsync();
//this is an oddly specific condition that differs for every controller class. Causes a compiler error as written
if (!user.Approved && (user.RoleId == 1 || user.RoleId == 7))
{
return RedirectToAction("Index", "Notifications");
}
//this returns data that is used in the controller methods
else
{
return (user.UserId, user.RoleId, user.Approved);
}
}
I have several controller classes where this method is declared. Currently I have the above mentioned redirect declared in each view method (Index(), Create(), Edit(), etc). It seems a bit of a waste to declare a new authorization policy for every controller that needs this when the logic is already there.
Is there a way to redirect the user without declaring return RedirectToAction(...)?
Or am I missing something entirely?
I've got an issue calling a method that may return a Task<T> or null depending on the result of an initial synchronous lookup call (this itself might be an anti-pattern so please let me know).
I kind of want to return null if a trivial exit condition occurs but this is causing the calling (outer) method to fail because the outer call expects a Task<T> response (trivial exit) which gets pushed through to ConfigureAwait(true) which subsequently produces a NullReferenceException.
Outer calling method:
var res = await MyService.GetUserCourseStatusAsync(userID, productID).ConfigureAwait(true);
Intermediate method:
public Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
// Look up User's ID (if exists)
var userCredentials = GetUserCredentials(userID);
if (userCredentials?.UserID == null)
return null; // Trivial return of null ("User not registered"). *** This causes exception on ConfigureAwait(true) above ***
// Another synchronous call
var courseId = GetCourseID(productID);
if (courseId == null)
throw new InvalidOperationException($"Product #{productID} is not a course");
// Asynchronous call to inner method (this bit works fine)
return GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);
}
So my thought then is that we should always return a Task<T> instead of null.
However, all of these cause compile errors:
//return null; // Trivial return of null ("User not registered"). *** This causes exception
// Compile error: CS0029: Cannot implicitly convert type 'GetUserCourseInner' to 'System.Threading.Tasks.Task<IGetUserCourseResponse>'
return new GetUserCourseInner(); // Not registered
// Compile error: CS1503 Argument 1: cannot convert from 'GetUserCourseInner' to 'System.Func<IGetUserCourseResponse>'
return new Task<IGetUserCourseResponse>(new GetUserCourseInner()); // Not registered
How do I return a dummy Task<T> that isn't a result of a async call?
Is this even the correct approach?
It would be better, as you suggested, to return a Task<IGetUserCourseResponse> which contains null (or some other sentinel value). You can create such a completed Task with Task.FromResult((IGetUserCourseResponse)null):
public Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
// Look up User's ID (if exists)
var userCredentials = GetUserCredentials(userID);
if (userCredentials?.UserID == null)
return Task.FromResult((IGetUserCourseResponse)null);
// Another synchronous call
var courseId = GetCourseID(productID);
if (courseId == null)
throw new InvalidOperationException($"Product #{productID} is not a course");
// Asynchronous call to inner method (this bit works fine)
return GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);
}
Alternatively, you could make your outer method async. Note however that this changes its behaviour in the case where it throws an InvalidOperationException: instead of the method throwing this exception directly, it will instead return a Task which contains this exception. This may or may not be what you want:
public async Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
// Look up User's ID (if exists)
var userCredentials = GetUserCredentials(userID);
if (userCredentials?.UserID == null)
return null;
// Another synchronous call
var courseId = GetCourseID(productID);
if (courseId == null)
throw new InvalidOperationException($"Product #{productID} is not a course");
// Asynchronous call to inner method (this bit works fine)
return await GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);
}
Just return a Task that holds a null value as result
return Task.FromResult<IGetUserCourseResponse>(null);
Using NopCommerce 3.8, Visual Studio 2015 proff.
I have created a plugin that is responsible for making restful calls to my Web API that exposes a different DB to that of Nop.
The process is run via a nop Task, it successfully pulls the data back and i can step through and manipulate as i see fit, no issues so far.
Issue comes when i try to update a record on the product table, i perform the update... but nothing happens no change, no error.
I believe this is due to the Context having no idea about my newly instantiated product object, however I'm drawing a blank on what i need to do in relation to my particular example.
Similar questions usually reference a "model" object that is part of the parameter of the method call, "model" has the method ToEntity which seems to be the answer in similar question in stack.
However my example doesn't have the ToEntity class/method possibly because my parameter is actually a list of products. To Clarify here my code.
Method in RestClient.cs
public async Task<List<T>> GetAsync()
{
try
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(ApiControllerURL);
var taskModels = JsonConvert.DeserializeObject<List<T>>(json);
return taskModels;
}
catch (Exception e)
{
return null;
}
}
Method in my Service Class
public async Task<List<MWProduct>> GetProductsAsync()
{
RestClient<MWProduct> restClient = new RestClient<MWProduct>(ApiConst.Products);
var productsList = await restClient.GetAsync();
InsertSyncProd(productsList.Select(x => x).ToList());
return productsList;
}
private void InsertSyncProd(List<MWProduct> inserted)
{
var model = inserted.Select(x =>
{
switch (x.AD_Action)
{
case "I":
//_productService.InsertProduct(row);
break;
case "U":
UpdateSyncProd(inserted);
.....
Then the method to bind and update
private void UpdateSyncProd(List<MWProduct> inserted)
{
var me = inserted.Select(x =>
{
var productEnt = _productRepos.Table.FirstOrDefault(ent => ent.Sku == x.Sku.ToString());
if(productEnt != null)
{
productEnt.Sku = x.Sku.ToString();
productEnt.ShortDescription = x.ShortDescription;
productEnt.FullDescription = x.FullDescription;
productEnt.Name = x.Name;
productEnt.Height = x.Pd_height != null ? Convert.ToDecimal(x.Pd_height) : 0;
productEnt.Width = x.Pd_width != null ? Convert.ToDecimal(x.Pd_width) : 0;
productEnt.Length = x.Pd_depth != null ? Convert.ToDecimal(x.Pd_depth) : 0;
productEnt.UpdatedOnUtc = DateTime.UtcNow;
}
//TODO: set to entity so context nows and can update
_productService.UpdateProduct(productEnt);
return productEnt;
});
}
So as you can see, I get the data and pass data through to certain method based on a result. From that list in the method I iterate over, and pull the the entity from the table, then update via the product service using that manipulated entity.
So what am I missing here, I'm sure its 1 step, and i think it may be either be because 1) The context still has no idea about the entity in question, or 2) Its Incorrect calls.
Summary
Update is not updating, possibly due to context having no knowledge OR my methodology is wrong. (probably both).
UPDATE:
I added some logger.inertlog all around my service, it runs through fine, all to the point of the call of update. But again I check the product and nothing has changed in the admin section.
plugin
I have provided the full source as i think maybe this has something to do with the rest of the code setup possibly?
UPDATE:
Added the following for testin on my execute method.
var myprod = _productRepos.GetById(4852);
myprod.ShortDescription = "db test";
productRepos.Update(myprod);
This successfully updates the product description. I moved my methods from my service into the task class but still no luck. The more i look at it the more im thinking that my async is killing off the db context somehow.
Turned of async and bound the getbyid to a new product, also removed the lambda for the switch and changed it to a foreach loop. Seems to finally update the results.
Cannot confirm if async is the culprit, currently the web api seems to be returning the same result even though the data has changed (some wierd caching by deafult in .net core? ) so im creating a new question for that.
UPDATE: It appears that the issue stems from poor debugging of async. Each instance I am trying to iterate over an await call, simply put im trying to iterate over a collection that technically may or may not be completed yet. And probably due to poor debugging, I was not aware.
So answer await your collection Then iterate after.
I'm not really sure what is the prefered way when dealing with record not found in the database. Is it better to write Find method which returns null or Get method which returns RecordNotFoundException?
[AuthenticateFilter(UsernameAndSecretKey)]
[Route("api/v1/activities/emails/{id}")]
[HttpGet]
public IHttpActionResult GetEmailActivity(int id)
{
try
{
// business logic service, could use only db service but this way we can do unit tests (just fill bl service method with fake objects)
var service = new EmailActivityBlService();
// 1. use Find method which returns null in case record with provided id does not exist in db
var model = service.FindActivity(id);
if( model != null )
return Ok(model);
return NotFound();
// 2. or is this approach better
// throws RecordNotFoundException in case row by id is not found in database
return Ok(service.GetActivity(id));
}
catch(RecordNotFoundException e) { return NotFound(); }
catch(Exception e) { return InternalServerError(e); }
}
EmailActivityBlService has next code in case anyone interested (showing only the important part):
private EmailActivityDbService _dbService;
public EmailActivityModel GetActivity(int id)
{
var model = this._dbService.GetActivity(id);
if( model == null )
throw new RecordNotFoundException(); // I suppose System.Data.ObjectNotFound is also suitable
return model;
}
public EmailActivityModel FindActivity(int id)
{
// typical entity framework query
// using(var context = new ..) { return contect.EmailActivity.Where()..SingleOrDefault().ConvertToModel();
return this._dbService.GetActivity(id);
}
UPDATE
Talked with my colleagues, we decided to go with this solution. As why GetActivity returns null instead of throwing Exception, I prefer answer from rboe:
So return null if it is can happen in your domain, that records do not exist (in my experience this is most often the case). If you expect a record to exist and it is not there, then it is valid to throw an exception.
[AuthenticateFilter(UsernameAndSecretKey)]
[Route("api/v1/activities/emails/{id}")]
[HttpGet]
public IHttpActionResult GetEmailActivity(int id)
{
var service = new EmailActivityBlService();
var model = service.GetActivity(id); // returns null in case activity is not found
if( model != null )
return Ok(model);
return NotFound();
}
We avoided any try-catch in the methods and put global filter when Exception occurs:
File: App_Start\WebApiConfig.cs
public class WebApiExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, actionExecutedContext.Exception.Message, actionExecutedContext.Exception);
}
}
Both ways are valid ones.
It is a different emphasis whether you use exceptions or the return value null to indicate non existing records.
Exceptions exist to signal an error state (something happened that is abnormal). The code in the catch-handler is focused on how to deal with an error and not to contain business logic.
If you return null then it will be a normal and 'non exceptional' state in your model.
So return null if it is can happen in your domain, that records do not exist (in my experience this is most often the case). If you expect a record to exist and it is not there, then it is valid to throw an exception.
I disagree with the other answer. In the case of a GetyById method, I wouldn't say to return null instead of throwing because you could argue it was "expected" that there might not be a record with the requested id. This "exceptions for exceptional situations," while often stated, I don't really think is the best way to think about the method's contracts. APIs should make semantic sense, ideally.
Instead, I suggest to throw exceptions whenever the method cannot do what it was told to do. So GetById methods should thrown an exception in the event there is no such record with the requested id in the system. The Find method should probably return an enumerable, which of course could be empty in the event no records match the criteria given.
An API which has a FindById method strikes me as odd; if you are giving the API an ID, that implies the caller could somehow have learned the ID in a previous API call, and so the API shouldn't need to "find" an already known to exist record. It should provide a way to get the record directly by its id. Instead Find should be for locating records when you aren't sure they exist, and using some other criteria.
Given the web service call, I would go with the service calling the GetById method, as the web service caller also learned the id somehow. If the id turns out not to exist, the library can throw the RecordNotFoundException, which causes the service call to return 404.
On one of my ASP.NET MVC websites I occassionally get a NPE in production that I'm having trouble understanding root cause. I'm using ELMAH for tracing errors.
The full stacktrace is here (not much to it:
System.NullReferenceException: Object reference not set to an instance of an object.
at PG.Controllers.FooController.FooActionName(FooModel model) in
{REMOVED}\web\www\Controllers\FooController.cs:line 622
Here's line 622:
if(UserCanEditFoo(out result))
Here's the "UserCanEditFoo listing:
private bool UserCanEditFoo(out ActionResult result)
{
String email = User != null && User.Identity != null ? User.Identity.Name : String.Empty;
UserAccount user = String.IsNullOrEmpty(email) ? null : this.accountServices.GetUserAccount(email);
bool canEditFoo = false;
result = null;
// Don't know this user
if(user == null)
{
FormsAuthentication.SignOut();
Session.Abandon();
result = RedirectToAction(Routes.Actions.Index, Routes.Controllers.Home);
}
// Only admins and foo profile can edit foo
else if(user.Role != Types.UserRole.Admin && user.Role != Types.UserRole.Foo)
{
result = new HttpUnauthorizedResult("Oops! Only administrators and foo users can edit foo.");
}
else
{
result = null;
canEditFoo = true;
}
return canEditFoo;
}
I'm assuming the exception is actually raised in the "UserCanEditFoo" private method but I don't understand why the line number is not being reported as such. On the flipside, if the exception is truly being raised at line 622 I don't understand how that can be given it is a simple function call.
Any help will be much appreciated. Thanks in advance.
Assuming this.accountServices cannot be null, your code looks fine to me, so I would suggest that the exception is actually happening elsewhere (perhaps due to inlining).
Take a look at this question for details on displaying correct line numbers in Release mode.