How to get Asp.net Core Identity User in View - asp.net-core-identity

In Asp.net core mvc project, how to get the current IdentityUser information in View ?

What #rdhainaut said works OK if you just want to display the username on the View. If that's all you need, you can even use the following without the needs of injecting anything!
#if (User.Identity.IsAuthenticated)
{
<div>Hello, #User.Identity.Name</div>
}
The reason is, with the default configuration (assuming you're using username), the username is stored in #User.Identity.Name after successful signin.
Now if you do want to display additional information about the logged in user, such as display name on your site top navigation, that's when I think ViewComponent` comes into play!
For example, let's say you have defined an Admin area in your MVC project and you want to create a top navigation bar.
You create a folded called ViewComponents under Admin folder.
Create a view component called TopNavbarViewComponent.cs.
public class TopNavbarViewComponent : ViewComponent
{
private readonly UserManager<IdentityUser> _userManager;
// You can inject anything you want here
public TopNavbarViewComponent(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var appUser = await _userManager.FindByNameAsync(
HttpContext.User.Identity.Name);
var vm = new TopNavbarViewModel
{
// Just as an example... perhaps you have additional
// property like FirstName and LastName in your IdentityUser.
DisplayName = appUser?.DisplayName,
Email = appUser?.Email,
Phone = appUser?.PhoneNumber
};
return View(vm);
}
}
Define the model behind the view undler ViewComponents\Models.
public class TopNavbarViewModel
{
public string DisplayName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
Define the view for the view component, which needs to be under Areas\Admin\Views\Shared\Components\TopNavbar\Default.cshtml by convention.
#model Web.UI.Areas.Admin.ViewComponents.Models.TopNavbarViewModel
<nav class="navbar navbar-light">
<ul class="navbar-nav">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown">
Hello, #Model.DisplayName
</a>
</ul>
</nav>

In a web application asp.net core mvc 2.0, You can have the IdentityUser object directly available in the view with the following code :
#using Microsoft.AspNetCore.Identity
#inject SignInManager<IdentityUser> SignInManager
#inject UserManager<IdentityUser> UserManager
#if (SignInManager.IsSignedIn(User))
{
<div>Hello #UserManager.GetUserName(User)!</div>
}

Related

A view component named 'Search' could not be found

I have a runtime error with this message
InvalidOperationException: A view component named 'Search' could not be found. A view component must be a public non-abstract class, not contain any generic parameters, and either be decorated with 'ViewComponentAttribute' or have a class name ending with the 'ViewComponent' suffix. A view component must not be decorated with 'NonViewComponentAttribute'.
Controller:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
}
The code you provide has nothing done with the ViewComponent. Have you created the Search ViewComponent?
For how to create a viewcomponent, usually you can follow the below steps.
1.Create a folder named ViewComponents in the root path, then create the ViewComponent class (SearchViewComponent). Note, it must end with ViewComponent.
public class SearchViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
return View();
}
}
2.Add a folder named Components under Views > Shared folder, and add a folder named Search(corresponding to your ViewComponent class) under Components folder, then create the View, you can simply name it as Default.cshtml.
Default.cshtml:
For test, I simply set this in the View:
<h2>Search Content</h2>
3.Use the following to call ViewComponent:
#await Component.InvokeAsync("Search")
Example:
Home Index View:
#{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about building Web apps with ASP.NET Core.</p>
</div>
#await Component.InvokeAsync("Search")
Result:
For more details, refer to the offical document.

ASP .NET Core Razor: Model bound complex types must not be abstract or value types and must have a parameterless constructor

If I have a property like this in my model:
[BindProperty]
public IPagedList<Product> Products { get; set; }
then when I try to post, I get this error:
An unhandled exception occurred while processing the request.
InvalidOperationException: Could not create an instance of type 'X.PagedList.IPagedList`1[Data.Models.Product, Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, set the 'Products' property to a non-null value in the 'Areas.Catalog.Pages.ProductListModel' constructor.
The error says I can set the property to a non-null value in the constructor, so I try to do this in the constructor:
Products = new PagedList<Product>(Enumerable.Empty<Product>(), 1, 10);
But I get the same error.
When I remove [BindProperty] it works. I was under the impression I needed that to bind a property on a Razor page, but I guess not?
If a new Razor Pages project is created and the following amends made it works fine:
Product.cs:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
Index.cshtml:
#page
#using X.PagedList;
#using X.PagedList.Mvc.Core;
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about building Web apps with ASP.NET Core.</p>
</div>
#{
foreach (var item in Model.Products)
{
<div> #item.Name</div>
}
}
#Html.PagedListPager((IPagedList)Model.Products, page => Url.Action("Index", new { page }))
Index.cshtml.cs
public class IndexModel : PageModel
{
public IndexModel()
{
Products = new PagedList<Product>(Enumerable.Empty<Product>(), 1, 10);
}
[BindProperty]
public IPagedList<Product> Products { get; set; }
public void OnGet()
{
}
}
As such I suspect the issue to be with complexity in your Product class which you have not provided the code for.
To verify that, use a temporary simple Product class (like that in my example) as a test.
Once confirmed, try projecting the product class to a simpler class using Automapper or linq's Select method and see if that helps:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/basic-linq-query-operations#selecting-projections
http://docs.automapper.org/en/stable/Projection.html

asp.net mvc3 dynamic validation and entity framework

Thanks for any thoughts.
while I am working my way through some custom validationAttributes, I have come across a problem which should be simple, but has me stumped.
An authorized user will have a UserProfile which includes a key to the site they work in. This site is a record set within a database. 1 field in this site record set is a regular expression which denotes what would be a valid format for a field in a completely separate table. The data entered into this other table will be common to all registered users, but a particular field relates to the ID format used at their institution.
Is there a clean way I can dynamically add a regular expression validator to a property?
Thank you as always.
This is what I came up with, but keen to know if there are better solutions:
Naming conventions are to allow automapper to flatten the model (each StudyCentre has a many to 1 relationship with the RecordSystem (some systems share the patient indexing system)
Mapper.CreateMap<StudyCentre, ParticipantRegistration.StudyCentreViewData>();
As a nested class within the ViewModel for an indidual TrialParticipant
public StudyCentreViewData ViewData { get; set; }
public class StudyCentreViewData
{
public string Abbreviation { get; set; }
public string RecordSystemName { get; set; }
public string RecordSystemHospitalNoRegEx { get; set; }
public string RecordSystemNotationDescription { get; set; }
public IDictionary<string, object> HospitalNoRegEx()
{
return DynamicClientValidation.RegEx(errorMessage:String.Format("{0} must consist of {1}",
RecordSystemName,
RecordSystemNotationDescription),
regExPattern: RecordSystemHospitalNoRegEx);
}
}
The other properties (such as StudyCentre.Abbreviation are for the labels)
The function RegEx is simply:
public static class DynamicClientValidation
{
public static IDictionary<string, object> RegEx(string errorMessage, string regExPattern)
{
var returnVal = new Dictionary<string, object>(3);
returnVal.Add("data-val-regex", errorMessage);
returnVal.Add("data-val-regex-pattern", regExPattern);
returnVal.Add("data-val", "true");
return returnVal;
}
}
The Controller sets up the viewmodel like so:
model.ViewData = Mapper.Map<StudyCentre, ParticipantRegistration.StudyCentreViewData>(_studyCentre.GetCentreByUser(_currentUserName));
and in the view (LabelDetailsfor is a custom helper):
<div class="editor-label">
#Html.LabelDetailsFor(model => model.HospitalID,Model.ViewData.Abbreviation + " ID", Model.ViewData.RecordSystemName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.HospitalID, Model.ViewData.HospitalNoRegEx())
#Html.ValidationMessageFor(model => model.HospitalID)
</div>

How to add images as details in a ASP MVC 3 Razor View using Entity Framework

For an ongoing project i have (amongst other classes) the following:
public class Page
{
[Key]
public int PageId { get; set; }
public string Name { get; set; } //eg. "AboutUs", "Location"
[Column(TypeName = "ntext")] //force Entity Framework to create a ntext column
public string Title { get; set; }
public string Subtitle { get; set; }
public string Content { get; set; }
//navigational properties
public virtual ObservableCollection<Image> Images{ get; set; } //one Page has many Images
public Page()
{
Images= new ObservableCollection<Image>();
}
}
I'm using Entity Framework code first approach in this ASP MVC 3 project (using Razor) and do not have any problem inserting and updating objects of this type.
BUT: how can i have a master detail view in which the detail part is composed by images only (see class definition).
So how is it possible to add an image, if the user doesn't want it to have it deleted and of course how to show all the images in a list?
Any hint is deeply appreciated!
Look at this post: Display image from database in asp mvc
If you are trying to render from the database, create a method in your controller to get the image and use that as your image source like in the example above. The only difference is yours will be contained in a
<ul>
#foreach(var image in Model.Images) {
<li><img src="#Url.Action("Show", "Image", new {id = image.Id})" /></li>
}
</ul>
Then your model would contain the Id's of the images, and it would be the job of the action method to retrieve the image.

asp.net mvc3 and multi-level navigation

i am writing new asp.net mvc application and i have question about creating multi-level navigation system.
For example i have web with main navigation (Cpu --- Gpu ---- Ram)
and sub-navigation with (intel,amd --- ati,nvidia --- DDR2,DD3)
Well, my first implementation is here :
public class NavigationItem
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual string Controller { get; set; }
public virtual string Action { get; set; }
public virtual string Url { get; set; }
public virtual string Section { get; set; }
}
public class NavigationController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public NavigationController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public ActionResult MainMenu()
{
return View(_unitOfWork.NavigationItems.Where(x => x.Section == null).ToList());
}
public ActionResult SectionMenu()
{
return View(_unitOfWork.NavigationItems.Where(x => x.Section == "// name of section").ToLis());
}
}
And finally my layout page is :
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
</head>
<body>
<div class="main-menu">
#{Html.RenderAction("MainMenu", "Navigation");}
</div>
<div class="section-menu">
#{Html.RenderAction("SectionMenu", "Navigation");}
</div>
#RenderBody()
</body>
</html>
With this implementation i have problem how i handle in SectionMenu with MainMenu is active, because i want generate SectionMenu depends on MainMenu and highlight them.
One workaround of witch i think is handle url(controller) in SectionMenu.
For example :
if (RouteData.Values["controller"].ToString() == "Administration")
{
// Generate section menu for Administration main menu
}
I dont like this solution because i work with "magic string" values and i havent only one controller per one MainMenu.
How implement this solution?
Thanks for advice
I guess you have to get deeper into MVC's routing system. It doesn't look very straightforward in the beginning, but this is probably the most important area of the entire MVC. Sometimes though you can easily get confused especially if you have many routes defined in Global.asax. There is a project called Route debugger, it helps. I don't remember the link. Try to google it... If you can't find it I'll send the link later...
Ok, I use ViewBag feature. Not clean, but works

Resources