question

daniel-niccoli avatar image
0 Votes"
daniel-niccoli asked ZhiLv-MSFT edited

Require IdentityUser (currently signed in user) as a parameter of a ASP.NET Core model

Hi,

I am developing a small blog application in ASP.NET Core 6. I am using Identity on ASP.NET Core.

I'm planning to use the CRUD Scaffolding to create the razor pages. A BlogPost must only be created by authenticated users. And each BlogPost should always be immediately linked to the authenticated user.

I created a model BlogPost and added the fields Author, Title, Content and CreationDate. No field is nullable and the constructor requires to pass a user object, which should be the currently signed in user later in the pages. When I run Add-Migration, I get this error:

No suitable constructor was found for entity type 'BlogPost'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'author' in 'BlogPost(IdentityUser author, string title, string content)'.

What do I need to correct to get this working like I intend to?

BlogPost.cs

csharp
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;

namespace BlogPost.Models
{
    public class BlogPost
    {
        public string Id { get; set; }

        [Required]
        public IdentityUser Author { get; set; }

        [Required]
        public string Title { get; set; }

        [Required]
        public string Content { get; set; }

        [Required]
        public DateTime CreationDate { get; set; }

        public BlogPost(IdentityUser author, string title, string content)
        {
            Author = author;
            Title = title;
            Content = content;

            CreationDate = DateTime.UtcNow;
        }
    }
}



dotnet-entity-framework-coredotnet-aspnet-core-razor
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

cooldadtx avatar image
0 Votes"
cooldadtx answered cooldadtx commented

I assume you properly registered the services by calling AddDefaultIdentity at app startup correct?

Personally I wouldn't recommend that you pass IdentityUser as data to the model. There is way more information in that object than you should need in your view. The data is already available in the Controller class so you should ideally either wrap the user information into its own model (if you need several pieces of data) or just expose the user's display name, email or something. This would completely solve the issue while not exposing more sensitive data.

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I assume you properly registered the services by calling AddDefaultIdentity at app startup correct?

Yes. I just used the sample app included in ASP.NET Core 6 and added the model.

Personally I wouldn't recommend that you pass IdentityUser as data to the model. There is way more information in that object than you should need in your view. The data is already available in the Controller class so you should ideally either wrap the user information into its own model (if you need several pieces of data) or just expose the user's display name, email or something. This would completely solve the issue while not exposing more sensitive data.

I don't understand what you mean by that. I do this so that any new BlogPost instance cannot be created without immediately have it related to a user (the signed in user). I also don't have a Controller class in my application and don't understand the rest of your comment.




And there is no Controller class in my application.

0 Votes 0 ·

Yes. I just used the sample app included in ASP.NET Core 6 and added the model.

Please post the relevant code around the registration of your IdentityUser information.

And there is no Controller class in my application.

Are you building a Page Model-based application then?

I don't understand what you mean by that. I do this so that any new BlogPost instance cannot be created without immediately have it related to a user (the signed in user).

You don't need the IdentityUser in your model in order to immediately have it related to a user. It wouldn't do it anyway as a model is for binding to a view, not updating a database. It is in your "save" logic where you would grab the current user (from the controller/page model) and then associate it with the post being created.








0 Votes 0 ·
AgaveJoe avatar image
0 Votes"
AgaveJoe answered AgaveJoe edited

The user's identity is cached in an authentication cookie and the Identity middleware exposes the user principal within an HTTP request. For example the following gets the username in the Get or Post Razor Page methods.

 User.Identity.Name

Typically you don't want to expose the user information to the browser. When a user creates a blog entry you'll simply grab the user's Id and use the Id to relate the record to the user.

 var user = await _userManager.GetUserAsync(User);
 var userId = user.Id;

The same idea applies to fetching records. Fetch the current user's identity (id) and use the Id to filter the records so only the logged in user can see their information.

Lastly, using entities to scaffold Razor pages in not always a good idea. For example, you do not want to expose the user's Id to the browser. An entity will have the user's Id so rather than scaffolding an entity scaffold a view model. Use the view model to populate an entity to do any CRUD operations.

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

ZhiLv-MSFT avatar image
0 Votes"
ZhiLv-MSFT answered ZhiLv-MSFT edited

Hi @daniel-niccoli,

No suitable constructor was found for entity type 'BlogPost'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'author' in 'BlogPost(IdentityUser author, string title, string content)'.

To solve this issue, you could add a parameterless constructor on the BlogPost class, like this:

 public class BlogPost
 {
     public string Id { get; set; }
     [Required]
     public IdentityUser Author { get; set; }
     [Required]
     public string Title { get; set; }
     [Required]
     public string Content { get; set; }
     [Required]
     public DateTime CreationDate { get; set; }
    
     public BlogPost()  {   }

     public BlogPost(IdentityUser author, string title, string content)
     {
         Author = author;
         Title = title;
         Content = content;
         CreationDate = DateTime.UtcNow;
     }
 }

Then, enable migrations, it should works well.

Note: when adding a new item to the BlogPost table, you need to set the Author, otherwise it will show the foreign key (AuthorId) is null exception, like this:

200769-image.png


If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

Best regards,
Dillion


image.png (104.2 KiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.