ASP.NET Core 6 Write value to Page without iteration? - asp.net-core

I am trying to write a specific value to a page in ASP.NET Core 6. I found multiple solutions with iterators but I am not able to write a single value from non-iteratable models / instances (no enumerators & lists) to a page.
public class UserViewModel
{
public string Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
This models purpose is to get filled with values from the Identity Framework.
public UserViewModel umodel { get; set; }
Said model gets instanced, filled without any problems - it holds values (checked via console) in page.cshtml.cs:
var user = await _userManager.FindByIdAsync(id);
UserViewModel UserModel = new UserViewModel();
UserModel.UserName = user.UserName;
UserModel.Email = user.Email;
UserModel.Id = user.Id;
Console.WriteLine(UserModel.UserName);
Console.WriteLine(UserModel.Id);
Console.WriteLine(UserModel.Email);
If I try to access it on the corresponding page (page.cshtml) I can only access its name without any problems:
#Html.DisplayNameFor(model => model.umodel.Email)
When I want to access its content there is no value on the page.
#Html.DisplayFor(model => model.umodel.Email)
How can I access the values in this model on a razor page? All the solutions I found based on some kind of iterator and therefore models that had some kind of enumerator or where instanced and filled as a list.

From the code you posted, you aren't populating the page's UserViewModel property. You instantiated a different UserViewModel instance. You wrote the values of that to the Console, but the actual model property (umodel) has not been populated.
Try this in the OnGet method:
var user = await _userManager.FindByIdAsync(id);
umodel.UserName = user.UserName;
umodel.Email = user.Email;
umodel.Id = user.Id;
When rendering property values, you don't need the DisplayFor helper (unless you are using display templates). You just need to prefix the property with #:
#Model.umodel.UserName

I figured it out. There hast to be an instance of the model / class but in a specific way and naming. See following example:
Model:
public class IdentityUserModel
{
public string Id { get; set; }
public string ?UserName { get; set; }
public string ?Email { get; set; }
}
Reference to model in the Main class of the Page (page.cshtml.cs):
public IdentityUserModel IUserModel { get; set; }
Then the important part in the OnGet/OnGetAsync function(?) (page.cshtml.cs):
IUserModel = new(); // <-- instance
var user = [whatever...]
IUserModel.UserName = user.UserName;
IUserModel.Email = user.Email;
IUserModel.Id = user.Id;
Then to write on the page.cshtml:
#Model.IUserModel.Id
My understanding is that there has to be an instance of the class in die page context with exactly the same name (therefore = new() without instance name). I may have been blind but reading throug microsofts learn pages again this is was not clear at all to me.
Thanks to Mike Brind for sending me in the right direction with his input.

Related

ASP.NET CORE Add easily accessible properties to logged user

In our Asp.Net Core (2.2) MVC project we had to use an existing database (including all user & role related tables) from our previous Asp.Net Web app project.
Retrieving user data in asp.net web app (and having it available throughout the website) was preatty simple: upon login fill a custom user class/object with all the properties you need, save it as a Session variable and you call it wherever you need it (without going to the database).
This seems to me a lot harder to achieve in Asp.Net Core. What I have so far is:
ApplicationUser class:
public class ApplicationUser : IIdentity
{
public int Id { get; set; }
public Uporabnik Uporabnik { get; set; }
public string AuthenticationType { get; set; }
public bool IsAuthenticated { get; set; }
public string Name { get; set; }
}
Login form:
public IActionResult Prijava(PrijavaModel model)
{
// check user credentials
//
// ... validation code here ...
//
if (uporabnik != null)
{
//Create the identity for the user
var identity = new ClaimsIdentity(new[] {
new Claim("Email", model.Email),
new Claim("Id", uporabnik.IdWebUser.ToString()),
new Claim("Name", uporabnik.ImeInPriimek),
new Claim(ClaimTypes.Name, uporabnik.ImeInPriimek),
new Claim(ClaimTypes.PrimarySid, uporabnik.IdWebUser.ToString())
}, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
var login = HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return RedirectToAction("Index", "Home");
}
return View();
}
Now to retrieve the data in a controller I have to do something like this:
// id
int idUser = int.Parse(#User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
// or email
string email = #User.Claims.FirstOrDefault(x => x.Type == "Email").Value;
Well, this all works fine, but it's definitely not practical. To access any other user data I can go to the database (by "ID") and retrieve it, but I don't think this is the right way to do it!?!
Can I expand the identity class in such a way that I can set the extra properties I need at login time and retrive in a fashion similar to something like this:
var property1 = #User.Property1;
var property2 = #User.Property2;
// or
var property1 = #User.MyExtraProperties.Property1;
var property2 = #User.MyExtraProperties.Property2;
Is it possible (and also keeping it simple)?
EDIT: since there are no answers/suggestions, can I do the same thing with a different approach?
Look like you only want to call your properties in a better way?
public class ApplicationUser : IdentityUser
{
public string CustomName { get; set; }
}
Assuming you have done adding your extra properties, you could create an extension method for your properties, so you can later call them like User.Identity.GetCustomName().
namespace Project.Extensions
{
public static class IdentityExtensions
{
public static string GetCustomName(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("CustomName");
return (claim != null) ? claim.Value : string.Empty;
}
}
}
Note that I didn't include the part where you add the claims, because you already have it. In this case, you should have CustomName claim.
Also, #Dementic is right about the session. If a user is removed/disabled, he would still have access to. So, having a db call each time you need to fetch information is correct.

using a class method in MVC4

I just start do work on MVC4 Asp.Net
I have this class in my models
namespace PhoneBook.Models
{
public class User
{
public string Username { get; set; }
public string Password { get; set; }
public static String writeasd(){
return "asd";}
}
I have this method in my controller:
public ActionResult Main()
{
ViewBag.Username = Request.Form["username"];
ViewBag.Password = Request.Form["password"];
var user = new User()
return View(user);
}
However when I tried to call this method from my view like this:
#User.writeasd()
It gives error. What is the problem? Can you help me?
Note : I have #using PhoneBook.Models in the beginning of my view
When using a strongly typed view as you are there, you need two things.
One is a model directive
#model PhoneBook.Models.User
Then you can reference your model using the Model property of the view page.
So in your instance, you would use
#Model.writeasd()
HTH

Pass Url Parameters to Action by Model in ASP.NET MVC 4

I want to assign my url parameters to Model properties, passed as a parameter to the associated Action. For example;
Say, my url is http://www.example.com/Item/Index?color=red&size=50
My action inside the controller is like below:
public class ItemController : Controller
{
public ActionResult Index(MyModel myModel)
{
//
return View(myModel);
}
}
I want to configure the model or whatever necessary so that my model takes the color and size as field values. The following didn't work:
public class MyModel
{
[Display(Name = "color")]
public string Color{ get; set; }
[Display(Name = "size")]
public string Size{ get; set; }
}
What would be the correct way to solve the problem?
Thanks for any suggestion.
Update
Well, yes! The code above would work correctly, because Url parameter names are the same as model property names. I should explain my problem exactly as I encounter for the next time, sorry.
I must correct a part of my question to make it clear. The url should have been: http://www.example.com/Item/Index?c=red&s=50 to detect the problem.
If the url is like that, the code would not work. Because Url parameters don't have the same name as Model properties.
Updated model is below:
public class MyModel
{
[Display(Name = "c")]
public string Color{ get; set; }
[Display(Name = "s")]
public string Size{ get; set; }
}
Try adding [FromUri] in front of the parameter.
public class ItemController : Controller
{
public ActionResult Index([FromUri] MyModel myModel)
{
// do something
return View();
}
}
debugging the issue
Here are some suggestions in debugging the issue, as it should work out of the box.
try binding to primitive types
public class ItemController : Controller
{
public ActionResult Index(string color, string size)
{
// do something
return View();
}
}
Try reading out of the request object directly
var size = this.Request["size"];
If either of those work there is an issue with your model binding.
Update
If you want to have the query string parameters different to the model in MVC you'll need to have a custom model binder. Take a look at Asp.Net MVC 2 - Bind a model's property to a different named value and http://ole.michelsen.dk/blog/bind-a-model-property-to-a-different-named-query-string-field.html which extends the answer a little.
https://github.com/yusufuzun/so-view-model-bind-20869735 has an example with some html helpers that could be useful.

Can I programmatically determine the database "context" to use based on user credentials?

This is a followup to the question here, where the answer seems to refer to an overly-complicated and overly-specific (EF, which I'm not using - not even using an ORM).
There has to be a more straightforward way around this common scenario than the smoke, mirrors, and sorcery hinted at in that answer.
Note: I encased "context" in parenthesis because I'm not using EF, so it is not a literal "dbcontext" that I'm talking about here.
So I got to wondering: Could I set a global variable for each session when the user is authenticated and authorized?
e.g., when the user is authenticated/authorized, I would know which database context/contents should be served up to him.
So it seems I could set a value in Global.asax.cs' Application_Start() method and then either alter the RepositoriesInstaller (implementing IWindsorInstaller) class to conditionally register different concrete Repositories based on the user and what data he should have OR place conditional code in the concrete Repository itself to use this or that database instance.
Is this feasible? Is one way (altering the RepositoriesInstaller / altering the concrete Repositories class) preferred?
Have a look at this answer which will show you how to resolve the correct repository based on a key or value.
If you want to store it with the authorized user, you need to simple serialize the data and store it on the authenticated cookie:
Create a Model to represent the logged in information:
public class AuthenticatedMember
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int SiteNumber { get; set; }
}
Do something like a login in your controller:
var authenticatedMember = MembershipManager.ValidateLogin(model.Email, model.Password);
var cookie = FormsAuthentication.GetAuthCookie(authenticatedMember.Id.ToString(), false);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, authenticatedMember.ToJson(), ticket.CookiePath);
cookie.Value = FormsAuthentication.Encrypt(newTicket);
Response.Cookies.Add(cookie);
Then use a model binder to deserialize the AuthenticatedMember when you require:
public class AuthenticatedMemberModelBinder : IModelBinder
{
#region IModelBinder Members
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.Model != null)
{
throw new InvalidOperationException("Cannot update instances");
}
if (controllerContext.RequestContext.HttpContext.Request.IsAuthenticated)
{
var cookie = controllerContext
.RequestContext
.HttpContext
.Request
.Cookies[FormsAuthentication.FormsCookieName];
if (null == cookie)
return null;
var decrypted = FormsAuthentication.Decrypt(cookie.Value);
if (!string.IsNullOrEmpty(decrypted.UserData))
{
return JsonConvert.DeserializeObject<AuthenticatedMember>(decrypted.UserData);
}
}
return null;
}
#endregion
}

Why does ASP.NET MVC assumes that view will have matching input and output types?

ASP.NET MVC (or rather Html.Helpers and base page implementation) assumes that there will be one type for both rendering and posting (namely Model).
This is a violation of ISP, isn't it?
I am tempted to derive my Edit views (those that have different render-data, and post-data) from a custom EditPageBaseView<TViewModel, TFormData>.
The problem is I want my validation and post work against FormData instance (stored inside ViewModel), but MVC assumes that entire ViewModel will be POSTed back.
Is there an OOB way to facilitate that? (I didn't find one if there is).
Is it a bad idea (in concept) to have separate data types for different operations exposed by a service (a view in this case).
I tend to follow the CQRS model when constructing my view models. All rendering is done with ViewModel classes and all posting back is done with Command classes. Here's a contrived example. Let's say we have a View with a small form for creating users.
The ViewModel and Command classes looks like this:
public abstract class ViewModel {}
public abstract class Command: ViewModel
public class CreateUserViewModel : ViewModel
{
public string Username { get; set; }
public string Password { get; set; }
public string PasswordConfirm { get; set; }
}
public class CreateUserCommand : Command
{
public string Username { get; set; }
public string Password { get; set; }
public string PasswordConfirm { get; set; }
}
The UserController creates a CreateUserViewModel as the model for the Get request and expects a CreateUserCommand for the Post request:
public ActionResult CreateUser()
{
// this should be created by a factory of some sort that is injected in
var model = new CreateUserViewModel();
return View(model);
}
[HttpPost]
public ActionResult CreateUser(CreateUserCommand command)
{
// validate and then save the user, create new CreateUserViewModel and re-display the view if validation fails
}
Model binding takes care of ensuring that the properties of the Posted CreateUserCommand are populated properly, even though the Get View is bound to a CreateUserViewModel.
They don't have to match, but they do match by default.
If you don't want them to match, you can specify a different model in your Form or ActionLink:
Example of a Mismatch using Razor and C#:
Index.chtml:
#model FirstModel
<div>
#using (Html.BeginForm("Action", "ControllerName", new { ParameterName = new SecondModel { First = "First", Second = "Second" } }, FormMethod.Post)) {
<input type="submit" value="Submit Button" />
}
</div>
The Controller:
public class ControllerName : Controller {
public ActionResult Index() {
return View(new FirstModel());
}
public ActionResult Action(SecondModel ParameterName) {
return View() // Where to now?
}