Getting conflicting results in Web API 2, solved by app restart.

Hi all,

I’ve just encountered an issue in a web api application that I’m developing.

I believe the issue is with database reads, perhaps an Entity Framework caching issue. My API works on a subscription system, and when the subscription expired, the user was denied access as expected. When the subscription was renewed, the database was correctly updated to reflect this.

There is a controller called UserSubscriptionController with a method to GET whether the subscription is active. After renewal, this returned “true” as expected.

The issue is that in one of my Action Filters, the user was still being denied access to the API. The filter is called CheckStandardSubscriptionAttribute, and determined that the subscription was not active. This contradicts the UserSubscriptionController. I know this is where the user was being denied access due to the unique error message sent in the response.

The only way I could solve this was by restarting the web app in IIS, which did resolve the issue, but I would like to find a better solution, as this won’t be acceptable in production. Due to this solving the problem, I assume this must be some sort of caching issue.

Code

Here is the UserSubscriptionController. When using a GET request to call IsUserSubscriptionActive, the method returned “true”, because the subscription was active.

[Authorize] [RoutePrefix("api/UserSubscription")] public class UserSubscriptionController : ApiController { UniversalClipboardEntities db = new UniversalClipboardEntities(); public ApplicationUserManager userManager { get { return Request.GetOwinContext().GetUserManager<ApplicationUserManager>(); } } [Route("IsUserSubscriptionActive")] [HttpGet] public async Task<IHttpActionResult> IsUserSubscriptionActive() { string Email = User.Identity.Name; ApplicationUser user = await userManager.FindByEmailAsync(Email); string userid = user.Id; UserSubscription userSubscription = db.UserSubscriptions.FirstOrDefault(x => x.UserId == userid); bool subscriptionActive = AuthenticationChecks.IsUserSubscriptionActive(userSubscription); return Ok(subscriptionActive); } } 

Here is the Action Filter that was returning HTTP 401 Unauthorized, which it does if the subscription is not active.

public class CheckStandardSubscriptionAttribute : ActionFilterAttribute { private UniversalClipboardEntities db = new UniversalClipboardEntities(); public override void OnActionExecuting(HttpActionContext actionContext) { string Email = HttpContext.Current.User.Identity.Name; string userid = db.AspNetUsers.FirstOrDefault(x => x.Email == Email).Id; bool UserHasStandardSubscriptionOrAbove = AuthenticationChecks.DoesUserHaveStandardSubscriptionOrAbove(userid, db); if (!UserHasStandardSubscriptionOrAbove) { actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "You do not have an active subscription. Please purchase a Standard or Pro subscription to access this feature."); } } } 

The above two snippets use the AuthenticationChecks static class to determine whether a subscription is active. The two relevant methods are shown below:

public static bool DoesUserHaveStandardSubscriptionOrAbove(string userid, UniversalClipboardEntities db) { UserSubscription userSubscription = db.UserSubscriptions.FirstOrDefault(x => x.UserId == userid); if (userSubscription != null) { if (userSubscription.Type == (int)UserSubscriptionType.Pro || userSubscription.Type == (int)UserSubscriptionType.Standard) { return IsUserSubscriptionActive(userSubscription); } } return false; } public static bool IsUserSubscriptionActive(UserSubscription userSubscription) { if (userSubscription.Subscription_End_Date >= DateTime.UtcNow.Date) return true; return false; } 

Reading this, you may say that the problematic Action Filter relied on more logic, as the DoesUserHaveStandardSubscriptionOrAbove method checks the Type property of the subscription. This check should have passed, as the Type property in the DB was “1”, which is the same as (int)UserSubscriptionType.Standard. This was the same before and after the subscription renewal. The only thing that changed in the DB was the subscription end date.

You may also have noticed that in one case, I am accessing the user though the UserManager, and in the other case I am querying it myself. AFAIK, these should fetch the same user. I’ve not known these to give me trouble before.

If you got this far, then would you happen to be able to tell me what I have done wrong here? I’m really not sure. The DB was correct, but it seems that the Filter may have been reading cached data from before the subscription renewal, which would explain why it was giving me trouble.

Looking at the code, is this possible? I know EF keeps some things in memory for some queries, but if you use .FirstOrDefault, doesn’t that always go to the DB? If both methods are doing this, then why would one fail?

submitted by /u/Liam2349
[link] [comments]

Leave a Reply