Asp.net MVC LINQ error message - asp.net

Using the ASP.NET Web-Api, I have the following POST setup in my controller. When posting to it from Fiddler, I get the error message:
The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities.
...when it gets to the var auth = dba.ApiMembers... line
// POST api/Avail
[BasicAuthentication]
public HttpResponseMessage PostAvail(Avail[] avail)
{
if (ModelState.IsValid)
{
// Check if authorised
var auth = dba.ApiMembers.Where(a => a.hotel_id ==
avail[0].HID && a.UserName == User.Identity.Name)
.FirstOrDefault();
Can anyone see anything wrong with this line?

LINQ2SQL queries run in database, and it's not possible to translate User.Identity.Name and avail[0] into database commands. You should initialize those values as parameters and pass simple types to LINQ query.
var hid = avail[0].HID;
var userName = User.Identity.Name;
var auth = dba.ApiMembers.Where(a => a.hotel_id == hid && a.UserName == userName).FirstOrDefault();

Related

How do you store the current user as a property on a web api model

I am using a web api 2 and creating a message object to save to the database. This message object needs to have the current user stored on it as an application user type.
My code looks like this:
var UserManager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
var currentUser = RequestContext.Principal;
var currentUserName = currentUser.Identity.Name;
var currentApplicationUser = UserManager.FindByName(currentUserName);
// I perhaps want to dispose of the user context?
// UserManager.Dispose();
globalMessage.sentBy = currentApplicationUser;
db.GlobalMessages.Add(globalMessage);
The last line is throwing the error: An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
Is there another way around this. Otherwise, I imagine, I could call an action which gets the current user, redirect to another action from the action with this user as an arg and then perform the update?
I worked it out:
var currentUser = RequestContext.Principal;
var currentUserName = currentUser.Identity.Name;
var sender = db.Users.Where(u => u.UserName == currentUserName).FirstOrDefault();
globalMessage.sentBy = sender;
db.GlobalMessages.Add(globalMessage);
db.SaveChanges();
The problem is you're attaching to your db context an entity (currentApplicationUser) that already is attached to another context instance that is alive too, so a solution could be to find the user using the same context, in your case it would be something like:
var UserManager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
var currentUser = RequestContext.Principal;
var currentUserName = currentUser.Identity.Name;
var currentApplicationUser =db.AspNetUsers.FirstOrDefault(u=>u.UserName==currentUserName);
globalMessage.sentBy = currentApplicationUser;
db.GlobalMessages.Add(globalMessage);

Get controller and action name from AuthorizationHandlerContext object

Hi I have a custom requirement handler with accepts the AuthorizationHandlerContext context parameter
When i debug, i can see that the context object contains
Context.Resources.ActionDescription.ActionName
But when writing the code i cant go beyond
Context.Resources
Seems the lower levels are not exposed. I want to get the action name and controller name that called the handler. How do i do this?
var mvcContext = context.Resource as AuthorizationFilterContext;
var descriptor = mvcContext?.ActionDescriptor as ControllerActionDescriptor;
if (descriptor != null)
{
var actionName = descriptor.ActionName;
var ctrlName = descriptor.ControllerName;
}
After upgrading to dotnet 5, the solution I was successfully using from Carsten above stopped working. The following workaround now works for me:
var routeValues = (context.Resource as HttpContext).Request.RouteValues;
var controllerName = routeValues["controller"].ToString();
var actionName = routeValues["action"].ToString();
Note this should include some null checks etc. the above is a barebones example.
Even though the question is tagged for asp.net-mvc, I wanted to add that the answer by #AdemCaglin does not work for Web API controllers. The following code works for both, API and MVC controllers:
var endpoint = context.Resource as RouteEndpoint;
var descriptor = endpoint?.Metadata?
.SingleOrDefault(md => md is ControllerActionDescriptor) as ControllerActionDescriptor;
if (descriptor == null)
throw new InvalidOperationException("Unable to retrieve current action descriptor.");
var controllerName = descriptor.ControllerName;
var actionName = descriptor.ActionName;

Extra info when using Web Api/OWIN Auth Token

I'm using out-of-the-box auth with Individual User Accounts that comes with the Visual Studio template for Web Api. I consume the api in an Angular.js front end.
Not surprisingly, I need to store extra information about the user like first and last names. I would like to set set these extra values in the Register method.
Q. Should I extend the user model or should I store this information as claims?
It would be a bonus if this information was 'automatically' returned in the response from /Token without adding extra code.
I decided to return the extra info in the response to the /Token call.
In ApplicationOAuthProvider (which is part of the template project) I changed CreateProperties and adjusted calls to CreateProperties in 2 places to pass the user, not just the username:
public static AuthenticationProperties CreateProperties(ApplicationUser user)
{
var firstNameClaim = user.Claims.FirstOrDefault(c => c.ClaimType == ClaimTypes.GivenName);
var lastNameClaim = user.Claims.FirstOrDefault(c => c.ClaimType == ClaimTypes.Surname);
var roles = user.Claims.Where(c => c.ClaimType == ClaimTypes.Role).Select(c => c.ClaimValue);
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", user.UserName },
{"firstName", firstNameClaim != null ? firstNameClaim.ClaimValue : "" },
{"lastName", lastNameClaim != null ? lastNameClaim.ClaimValue : "" },
{"roles", string.Join( ",", roles) }
};
return new AuthenticationProperties(data);
}

How to use JSON.NET to serialize an array of objects

I remember that one of my friends told me that I can throw anything into JSON.NET and serialize them into JSON format.
public string GetRecords(string apiKey, DateTime start, DateTime end)
{
var user = db.Users.SingleOrDefault(u => u.Id == apiKey);
if (user == null)
{
return string.Empty;
}
var records = db.Histories.Where(h => h.Date >= start && h.Date <= end);
JavaScriptSerializer s = new JavaScriptSerializer();
return JsonConvert.SerializeObject(records);
}
But now I got an exception:
There is already an open DataReader associated with this Command which
must be closed first.
What can I do to resolve this?
Call .ToList() on records, before passing it to JsonConvert.SerializeObject
You probably didn't enable multiple active result sets (MARS) in your config file.
Follow this link
Basically need to add
"MultipleActiveResultSets=True"
Or you could eager load as suggested by 3dd
More help here There is already an open DataReader associated with this Command which must be closed first

ASP.NET MVC 5 Default WebApp Forgot Password module missing?

In MVC 4 with SimpleMembership all these functions come with the default webbapp that you create in Visual Studio.
I was wondering where I can find the same for MVC 5 using the new ASP.NET Identity membership system? Is there some official blog or something that is beeing hidden from me in google search results?
UPDATE1: http://blogs.msdn.com/b/webdev/archive/2013/12/20/announcing-preview-of-microsoft-aspnet-identity-2-0-0-alpha1.aspx
UPDATE2: ASP.NET Identity 2.0 RTM has been released. Forgot Password is included in the samples/templates. http://blogs.msdn.com/b/webdev/archive/2014/03/20/test-announcing-rtm-of-asp-net-identity-2-0-0.aspx
We are working on adding these features to the ASP.NET Identity system and the MVC 5 templates.
I ran into this as well. To fix it, I created some controller actions in AccountController.cs (and corresponding views) to handle it.
Here are the actual lines that reset the user's password:
[AllowAnonymous]
[HttpPost]
public ActionResult ResetForgottenPassword(string key, ManageUserViewModel model)
{
var user = db.Users.SingleOrDefault(u => u.ForgotPasswordCode != null && u.ForgotPasswordCode == key);
if (user == null || !user.ForgotPasswordDate.HasValue || user.ForgotPasswordDate.Value.AddDays(1) < DateTime.UtcNow)
return new HttpUnauthorizedResult();
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
if (UserManager.HasPassword(user.Id))
UserManager.RemovePassword(user.Id);
IdentityResult result = UserManager.AddPassword(user.Id, model.NewPassword);
if (result.Succeeded)
{
//Clear forgot password temp key
user.ForgotPasswordCode = null;
user.ForgotPasswordDate = null;
db.SaveChanges();
//Sign them in
var identity = UserManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, identity);
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
}
else
{
AddErrors(result);
}
}
ViewBag.ForgotPasswordCode = key;
return View(model);
}
Some custom items are the new fields on the user object:
ForgotPasswordCode and ForgotPasswordDate to keep track of the user throughout the "reset password email" process.
I pass the key around in in the ViewBag once the user arrives from the email link.
The db variable is a property of my database context class inherited from a base controller.
I use UTC DateTimes in my database. Change DateTime.UtcNow to DateTime.Now if you do not.
Probably not the best solution, but it's a fairly quick and simple patch.
You can build a reset password by yourself (not sure that is the better choice, but is better than nothing)
Generate the hash with:
var newPwdHash = new PasswordHasher().HashPassword(newPasswordPlain)
And replace to the user's passwordhash property
If you cannot wait for the ASP.NET Identity Team to add this feature you can get an implementation of password reset from the open source project SimpleSecurity. Just take a look at the ResetPassword action on the AccountController. You can read about how the password reset was implemented here. Although the article references SimpleMembership, SimpleSecurity uses the same API to support either SimpleMembership or ASP.NET Identity in your MVC application.

Resources