Creating a new comment only works once - c#

I created a method for posting comments on a post. Everything works fine when I first post a comment. It appears under the post, it's saved in the database etc. However, if I want to create a second comment right away (without refreshing the page- because this way it does work) I get this error:
Microsoft.EntityFrameworkCore.Update: Error: An exception occurred in the database while saving changes for context type 'JoyAndFaithLicenta.Data.DataContext'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert explicit value for identity column in table 'AspNetUsers' when IDENTITY_INSERT is set to OFF.
at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task`1 result)
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync
(IRelationalConnection connection, CancellationToken cancellationToken) at
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at
Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at
Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at JoyAndFaithLicenta.Data.UserRepository.SaveAllAsync() in C:\Users\Georgia\source\repos\JoyAndFaithLicenta\JoyAndFaithLicenta\Data\UserRepository.cs:line 84 at JoyAndFaithLicenta.Helpers.LogUserActivity.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) in C:\Users\Georgia\source\repos\JoyAndFaithLicenta\JoyAndFaithLicenta\Helpers\LogUserActivity.cs:line 30 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at JoyAndFaithLicenta.Middleware.ExceptionMiddleware.InvokeAsync(HttpContext context) in C:\Users\Georgia\source\repos\JoyAndFaithLicenta\JoyAndFaithLicenta\Middleware\ExceptionMiddleware.cs:line 33
Why is that? It's the only functionality where I have this problem. However, every other functionality has a SaveAsync() method created in the repository, and I have not created a repository for the comments, so I'm using DataContext: _context.SaveChangesAsync().
This is the method for posting a comment:
[HttpPost("create/{postId}")]
public async Task<ActionResult<CommentDto>> PostComment(CommentDto commentDto, int postId)
{
if (!ModelState.IsValid) return BadRequest("Not a valid model");
var user = await _userRepository.GetUserByUsernameAsync(User.GetUsername());
var comment = new Comment
{
PostId = postId,
UserId = user.Id,
Text = commentDto.Text,
Created = commentDto.Created,
User = commentDto.User
};
_context.Comments.Add(comment);
await _context.SaveChangesAsync();
return new CommentDto
{
PostId = comment.PostId,
UserId = comment.UserId,
Text = comment.Text,
Created = comment.Created,
User = comment.User
};
}
And this is the comment entity:
public class Comment
{
public int Id { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public string Text { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
public int UserId { get; set; }
[ForeignKey("PostId")]
public Post Post { get; set; }
public int? PostId { get; set; }
}

I can't see your onModelCreating function, but i think it is caused because you have not [Key] in your comment id.
public class Comment
{
[Key]
public int Id { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public string Text { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
public int UserId { get; set; }
[ForeignKey("PostId")]
public Post Post { get; set; }
public int? PostId { get; set; }
}
Then database will use Id as Key, and you are sure that keys aren't repeated.
Also, i think that you are injected DataContext service wrong.
Try to make the same job like this:
using(var cnx = new DataContext()
{
var comment = new Comment
{
PostId = postId,
UserId = user.Id,
Text = commentDto.Text,
Created = commentDto.Created,
User = commentDto.User
};
cnx.Comments.Add(comment);
await cnx.SaveChangesAsync();
}
If it works, its probplem with your DI in Startup.cs.
Remember, the best way to inject DataContext of EntityFramework is:
services.AddDbContext<DataContext>();

According to below code
var comment = new Comment
{
PostId = postId,
UserId = user.Id,
Text = commentDto.Text,
Created = commentDto.Created,
User = commentDto.User
};
You are trying to insert a new User as well. Since you are trying to create a new user with assigned ID you are getting this error. Because Aspnetuser table does not allow to pass ID explicitly since IDENTITY_INSERT is set to false by default.
I am not sure why you want to insert a user as well along with comment. Probably it can be a requirement. If you really want to create a user, then your User object should not have a assigned ID value.
If this is by mistake change User to UserId.

Related

Entity Framework Core 6 - Cannot insert explicit value for identity column in table 'Roles' when IDENTITY_INSERT is set to OFF

I currently have a problem with EF6 and Fluent API. When inserting into database where model have one to many relationship, I'm getting this error. I've checked some related topics, but I want to keep autoincrement. Model without one to many relationship works perfectly.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert explicit value for identity column in table 'Roles' when IDENTITY_INSERT is set to OFF.
at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__188_0(Task`1 result)
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.Tasks.Task.<>c.<.cctor>b__272_0(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
ClientConnectionId:501a6880-e298-43d5-be69-fcbebacdb15e
Error Number:544,State:1,Class:16
--- End of inner exception stack trace ---
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at WorkIT_Backend.Services.UserService.Create(String username, String password, String role) in C:\Users\Ondřej\Desktop\škola\2022 PRF\WS\OPR3\WorkIT_Backend\WorkIT_Backend\WorkIT_Backend\Services\UserService.cs:line 54
at WorkIT_Backend.Controllers.UsersController.CreateUser(UserDto user) in C:\Users\Ondřej\Desktop\škola\2022 PRF\WS\OPR3\WorkIT_Backend\WorkIT_Backend\WorkIT_Backend\Controllers\UsersController.cs:line 57
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Model classes
public sealed class Role
{
public long RoleId { get; set; }
public string? Name { get; set; }
public ICollection<User> Users { get; set; }
public Role()
{
Users = new HashSet<User>();
}
}
public class User
{
public long UserId { get; set; }
public string? UserName { get; set; }
public string? PasswordHash { get; set; }
public long RoleId { get; set; }
public virtual Role Role { get; set; }
public User()
{
}
}
OnModelCreating in dbcontext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Role>(entity =>
{
entity.HasKey(q => q.RoleId);
entity.Property(q => q.RoleId)
.ValueGeneratedOnAdd();
entity.Property(q => q.Name)
.IsRequired();
entity.HasIndex(q => q.Name)
.IsUnique();
});
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(q => q.UserId);
entity.Property(q => q.UserId)
.ValueGeneratedOnAdd();
entity.Property(q => q.UserName)
.IsRequired();
entity.Property(q => q.PasswordHash)
.IsRequired();
entity.HasOne(u => u.Role)
.WithMany(r => r.Users)
.HasForeignKey(u => u.RoleId)
.OnDelete(DeleteBehavior.ClientSetNull);
});
}
Method in service which is adding the user
public async Task<User> Create(string username, string password, string role)
{
EnsureNotNull(username, nameof(username));
EnsureNotNull(password, nameof(password));
EnsureNotNull(role, nameof(role));
username = username.ToLower();
if (_context.Users.Any(q => q.UserName == username))
throw CreateException($"User {username} already exists.", null);
var hash = _securityService.HashPassword(password);
var userRole = await _roleService.GetRole(role);
var ret = new User {UserName = username, PasswordHash = hash, Role = userRole};
_context.Users.Add(ret);
await _context.SaveChangesAsync();
return ret;
}
This method for adding roles works perfectly
public class RoleService
{
private readonly WorkItDbContext _context;
private readonly SecurityService _securityService;
public RoleService(WorkItDbContext context, SecurityService securityService)
{
_context = context;
_securityService = securityService;
}
public async Task<Role> Create(string name)
{
EnsureNotNull(name, nameof(name));
name = name.ToLower();
if (_context.Roles.Any(q => q.Name == name))
throw CreateException($"Role {name} already exists.", null);
var ret = new Role {Name = name};
_context.Add(ret);
await _context.SaveChangesAsync();
return ret;
}
public async Task<List<Role>> GetRoles()
{
var roles = await _context.Roles.ToListAsync();
return roles;
}
public async Task<Role> GetRole(string name)
{
var role = await _context.Roles.FirstAsync(q => q.Name == name) ??
throw CreateException($"Role {name} does not exist.");
return role;
}
}
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);
SecurityService securityService = new(builder.Configuration);
builder.Services.AddTransient<WorkItDbContext>();
builder.Services.AddSingleton<SecurityService>();
builder.Services.AddTransient<UserService>();
builder.Services.AddTransient<RoleService>();
I've different combination of dbcontext methods. I tried auto increment through anotation - identity. I've checked SQL server and autoincrement is alright.
Change
builder.Services.AddTransient<WorkItDbContext>();
to
builder.Services.AddDbContext<WorkItDbContext>();
and then your UserService and RoleService will share a WorkItDbContext, because it will be added as a Scoped service. See DbContext in dependency injection for ASP.NET Core

How can to map to a wrapper class using Mapster?

Based on my project I need to create a wrapper for “IFormFile”, Actually I created a “AppFileProxy” class and “IAppFile” interface for this purpose :
IAppFile.cs :
public interface IAppFile
{
string Name { get; }
string FileName { get; }
string ContentType { get; }
long Length { get; }
void CopyTo(Stream target);
Task CopyToAsync(Stream target, CancellationToken cancellationToken = default);
Stream OpenReadStream();
}
AppFileProxy.cs :
public class AppFileProxy : IAppFile
{
private readonly IFormFile _formFile;
public AppFileProxy(IFormFile formFile)
{
_formFile = formFile ?? throw new ArgumentNullException(nameof(formFile));
}
public string Name => _formFile.Name;
public string FileName => _formFile.FileName;
public string ContentType => _formFile.ContentType;
public long Length => _formFile.Length;
public void CopyTo(Stream target)
{
_formFile.CopyTo(target);
}
public Task CopyToAsync(Stream target, CancellationToken cancellationToken = default)
{
return _formFile.CopyToAsync(target, cancellationToken);
}
public Stream OpenReadStream()
{
return _formFile.OpenReadStream();
}
}
Now, I want to Map “IFormFile” to “IAppFile” by using Mapster in the action controller as shown below :
CompanyDto.cs :
public class CompanyDto
{
public string Name { get; set; }
public IFormFile Logo { get; set; }
}
CompanyMapDto.cs :
public class CompanyMapDto : IRegister
{
public int Id { get; set; }
public string Name { get; set; }
public IAppFile Logo { get; set; }
public void Register(TypeAdapterConfig config)
{
config.ForType<CompanyDto, CompanyMapDto>()
.Map(dest => dest.Logo, src => new AppFileProxy(src.Logo));
}
}
action controller :
[HttpPost]
[Route("[controller]/[action]")]
public async Task<IActionResult> AddCompanyWithLogo([FromForm]CompanyDto dto)
{
CompanyMapDto company = dto.Adapt<CompanyMapDto>();
var stream = company.Logo.OpenReadStream();
return Ok();
}
But When I call action I get exception error for OpenReadStream() method :
System.NotImplementedException: The method or operation is not implemented.
at GeneratedType_1.OpenReadStream()
at MapsterInDotNet.Controllers.CompaniesController.AddCompanyWithLogo(CompanyDto dto) in C:\Users\Mohsen\source\repos\UseMapsterInDotNet\MapsterInDotNet\MapsterInDotNet\Controllers\CompaniesController.cs:line 52
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
How do I resolve this?
Well. I managed to make it work. You have to explicitly configurate mapping from IAppFile to IFormFile.
So instead of your configuration:
config.ForType<CompanyDto, CompanyMapDto>()
.Map(dest => dest.Logo, src => new AppFileProxy(src.Logo));
Use this:
TypeAdapterConfig<IFormFile, IAppFile>.ForType() // Or NewConfig()
.MapWith(src => new AppFileProxy(src));

SQL error, (0x80131904): Cannot insert the value NULL,

After an update of Visual Studio 2022 to v17.2.2, I suddenly get an error in my ASP.NET app, that a virtual class cannot insert into SQL database because the class does not allow nulls. Before the update I was not getting this error.
This is the model a use
public class Member
{
public int MemberId { get; set; }
[Required(ErrorMessage = "Dit is een verplicht veld")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Dit is een verplicht veld")]
public string LastName { get; set; }
public string MemberImage { get; set; }
public Gender Gender { get; set; }
public int TeamId { get; set; }
public virtual Team Team { get; set; } = new();
}
I need the last virtual property to get all members from the database of a the Team model from the MemberRepository
public async Task<IEnumerable<Member>> GetAllMembersByTeamId(int TeamId)
{
var result = await dbContext.Members
.Include(t => t.Team)
.Where(t => t.TeamId == TeamId)
.ToListAsync();
if (result != null)
{
return result;
}
else
return null;
}
And with the a MemberRepository I store it into the database:
public class MemberRepository : IMemberRepository
{
private readonly AppDbContext dbContext;
public MemberRepository(AppDbContext dbContext)
{
this.dbContext = dbContext;
}
public async Task<Member> AddMember(Member obj)
{
await dbContext.Members.AddAsync(obj);
await dbContext.SaveChangesAsync();
return obj;
}
}
I ask for the team I want to add the member with:
protected async override Task OnInitializedAsync()
{
Team = await TeamsRepository.GetTeamById(int.Parse(Id));
}
and fired it up with the EditForm, OnValidSubmit.
public async Task HandleValidSubmit()
{
ConfirmationTitle = StaticString.ConfirmationTitle;
ConfirmationMessage = StaticString.ConfirmationMessage;
Member.TeamId = int.Parse(Id);
await MemberRepository.AddMember(Member);
NavigationManager.NavigateTo("/adminpage");
}
I'm confused, is it because the update or is something else, because I get the error also now in the previous made ASP.NET application.
Why this approach? I learned it of Kudvenkat from this site: https://www.pragimtech.com/blog/blazor/blazor-model-classes/
This is the error:
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
This is the error i get:
blazor.server.js:1 [2022-06-02T13:13:22.000Z] Error: Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Class', table 'Tjoba-Server.dbo.Teams'; column does not allow nulls. INSERT fails.
The statement has been terminated.
The statement has been terminated.
at Microsoft.Data.SqlClient.SqlCommand.<>c.b__188_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
ClientConnectionId:2df86c5d-9cee-4c70-ad12-ebb86a036fe9
Error Number:515,State:2,Class:16
--- End of inner exception stack trace ---
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at DataAcces.Data.MemberRepository.AddMember(Member obj) in C:\VsProjects\Tjoba_Server\DataAccess\Data\MemberRepository.cs:line 21
at Tjoba.Pages.Admin.AddMember.HandleValidSubmit() in C:\VsProjects\Tjoba_Server\Tjoba\Pages\Admin\AddMember.razor:line 207
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.Forms.EditForm.HandleSubmitAsync()
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
log # blazor.server.js:1
blazor.server.js:1 [2022-06-02T13:13:22.001Z] Information: Connection disconnected.
Uncaught (in promise) Error: Cannot send data if the connection is not in the 'Connected' State.
at bt.send (blazor.server.js:1:52004)
at kt._sendMessage (blazor.server.js:1:68639)
at kt._sendWithProtocol (blazor.server.js:1:68680)
at kt.send (blazor.server.js:1:68788)
at Object.beginInvokeDotNetFromJS (blazor.server.js:1:131279)
at w (blazor.server.js:1:2164)
at C.invokeMethodAsync (blazor.server.js:1:4014)
at blazor.server.js:1:11516
at T (blazor.server.js:1:11694)
at I (blazor.server.js:1:11485)
I get with a try catch block the same error message
Cannot insert the value NULL into column 'Class', table 'TjobaServer.dbo.Teams'; column does not allow nulls. INSERT fails. The statement has been terminated.
Strange is that I only want to insert a member to the database with a TeamId, but nothing to the table Teams.
I figured out and found the solution.
Have added an if block with the Entity unchanged code into the AddMember Task in the MemberRepository:
public async Task<Member> AddMember(Member member)
{
if (member.Team != null)
{
dbContext.Entry(member.Team).State = EntityState.Unchanged;
}
var result = await dbContext.Memberss.AddAsync(member);
await dbContext.SaveChangesAsync();
return result.Entity;
}

How to map JsonPatchDocument using Mapster?

I have my model:
public class Membership
{
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MinLength(3)]
[MaxLength(30)]
public string Name { get; set; }
[Required] public int PeriodType { get; set; }
[Required] public int Duration { get; set; }
[Required] public int TerminationPeriod { get; set; }
[Required] public float InstallmentPrice { get; set; }
}
And my Dto:
public class MembershipDto
{
public int Id { get; private set; }
public string Name { get; set; }
public int PeriodType { get; set; }
public int Duration { get; set; }
public int TerminationPeriod { get; set; }
public float InstallmentPrice { get; set; }
}
I'm using Entity Framework and JsonPatchDocument to make PATCH operation in my API, the code is following:
public async Task<MembershipDto> Handle(EditMembershipCommand request, CancellationToken cancellationToken)
{
var membershipEntity = await _dbContext
.Memberships
.SingleOrDefaultAsync(m => m.Id == request.Id);
if (membershipEntity is null)
throw new NullReferenceException($"Membership [Id: {request.Id}] not found");
var editedMembership = request.NewMembershipDto.Adapt<JsonPatchDocument<Membership>>(); //request.NewMembershipDto is type of JsonPatchDocument<MembershipDto>
editedMembership.ApplyTo(membershipEntity, ModelState);
_ = await _dbContext.SaveChangesAsync();
var membershipDto = editedMembership.Adapt<MembershipDto>();
return membershipDto;
}
As an output I get the following information:
ProblemDetails.ProblemDetailsMiddleware[1]
An unhandled exception has occurred while executing the request.
Mapster.CompileException: Error while compiling
source=Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1[AgreementApi.Dtos.MembershipDto]
destination=Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1[AgreementApi.Models.Membership]
type=Map
---> Mapster.CompileException: Error while compiling
source=Newtonsoft.Json.Serialization.IContractResolver
destination=Newtonsoft.Json.Serialization.IContractResolver
type=Map
---> System.InvalidOperationException: No default constructor for type 'IContractResolver', please use 'ConstructUsing' or 'MapWith'
at Mapster.Utils.DynamicTypeGenerator.CreateTypeForInterface(Type interfaceType)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Mapster.Utils.DynamicTypeGenerator.GetTypeForInterface(Type interfaceType)
at Mapster.Adapters.RecordTypeAdapter.CreateInstantiationExpression(Expression source, Expression destination, CompileArgument arg)
at Mapster.Adapters.BaseAdapter.CreateInstantiationExpression(Expression source, CompileArgument arg)
at Mapster.Adapters.RecordTypeAdapter.CreateInlineExpression(Expression source, CompileArgument arg)
at Mapster.Adapters.BaseAdapter.CreateInlineExpressionBody(Expression source, CompileArgument arg)
at Mapster.Adapters.BaseAdapter.CreateExpressionBody(Expression source, Expression destination, CompileArgument arg)
at Mapster.Adapters.BaseAdapter.CreateAdaptFunc(CompileArgument arg)
at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
--- End of inner exception stack trace ---
at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
at Mapster.TypeAdapterConfig.CreateInlineMapExpression(Type sourceType, Type destinationType, MapType mapType, CompileContext context, MemberMapping mapping)
at Mapster.Adapters.BaseAdapter.CreateAdaptExpressionCore(Expression source, Type destinationType, CompileArgument arg, MemberMapping mapping, Expression destination)
at Mapster.Adapters.BaseAdapter.CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg, MemberMapping mapping, Expression destination)
at Mapster.Adapters.ClassAdapter.CreateInlineExpression(Expression source, CompileArgument arg)
at Mapster.Adapters.BaseAdapter.CreateInlineExpressionBody(Expression source, CompileArgument arg)
at Mapster.Adapters.BaseAdapter.CreateExpressionBody(Expression source, Expression destination, CompileArgument arg)
at Mapster.Adapters.BaseAdapter.CreateAdaptFunc(CompileArgument arg)
at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
--- End of inner exception stack trace ---
at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
at Mapster.TypeAdapterConfig.CreateMapExpression(TypeTuple tuple, MapType mapType)
at Mapster.TypeAdapterConfig.CreateDynamicMapExpression(TypeTuple tuple)
at Mapster.TypeAdapterConfig.<GetDynamicMapFunction>b__66_0[TDestination](TypeTuple tuple)
at Mapster.TypeAdapterConfig.<>c__DisplayClass55_0`1.<AddToHash>b__0(TypeTuple types)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Mapster.TypeAdapterConfig.AddToHash[T](ConcurrentDictionary`2 hash, TypeTuple key, Func`2 func)
at Mapster.TypeAdapterConfig.GetDynamicMapFunction[TDestination](Type sourceType)
at Mapster.TypeAdapter.Adapt[TDestination](Object source, TypeAdapterConfig config)
at Mapster.TypeAdapter.Adapt[TDestination](Object source)
at Fitverse.AgreementApi.Handlers.EditMembershipHandler.Handle(EditMembershipCommand request, CancellationToken cancellationToken) in /Users/wonsu/Repositories/Fitverse/Fitverse.AgreementApi/Handlers/EditMembershipHandler.cs:line 34
at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
at MediatR.Pipeline.RequestPostProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
at MediatR.Pipeline.RequestPreProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
at Fitverse.AgreementApi.Controllers.SettingsController.EditMembership(Int32 id, JsonPatchDocument`1 membershipDto) in /Users/wonsu/Repositories/Fitverse/Fitverse.AgreementApi/Controllers/SettingsController.cs:line 52
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Hellang.Middleware.ProblemDetails.ProblemDetailsMiddleware.Invoke(HttpContext context)
I know that the issue is in this line of code:
var editedMembership = request.NewMembershipDto.Adapt<JsonPatchDocument<Membership>>();
But I don't know how to change it to properly map it to desired type. Could you help me? :)
I know that at the end I should probably get the membership from DB again, map it and return it instead of just mapping editedMembership, but I'll fit it after dealing with this mapping issue.
Add the mapping of IContractResolver helps resolve the issue.
private static TypeAdapterConfig GetConfiguredMappingConfig()
{
var config = TypeAdapterConfig.GlobalSettings;
IList<IRegister> registers = config.Scan(Assembly.GetExecutingAssembly());
config.Apply(registers);
config.NewConfig<IContractResolver, IContractResolver>()
.ConstructUsing(src => new DefaultContractResolver());
return config;
}

Hash added into a POST http request

I'm trying to integrate a hash into my database, which is generated by a POST request when creating a new user.
The APIs being new to me, I don't really understand the error and why my code failed. Is someone can explain to me what I am doing wrong and how I could make this work ?
Here's the code I wrote :
Controller
//POST: api/user/
[HttpPost]
public ActionResult<User> PostUserItem(User user)
{
string Hash = RandomNumberGenerator.GetInt32(1000, 9999).ToString();
SHA512 sha512 = SHA512.Create(Hash);
string Hashed = sha512.ToString();
user.Token_Validation = Hashed;
_context.UserItems.Add(user);
_context.SaveChanges();
return CreatedAtAction("GetUserItem", new User{idUser=user.idUser}, user);
}
Model
[Table("USERS")]
public class User
{
[Key]
public int idUser { get; set; }
public string name { get; set; }
public string lastName { get; set; }
public string email { get; set; }
public string pwd { get; set; }
public string Token_Validation { get; set; }
}
DBContext
public class RouteContext : DbContext
{
public RouteContext(DbContextOptions<RouteContext> options) : base(options)
{
}
public DbSet<User> UserItems { get; set; }
}
And here's the ERROR 500 I got when trying to use my API
System.NullReferenceException: Object reference not set to an instance of an object.
at LocalBeers.Controllers.UserController.PostUserItem(User user) in /var/www/html/backend/Controllers/UserController.cs:line 46
at lambda_method(Closure , Object , Object[] )
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
This is incorrect:
string Hash = RandomNumberGenerator.GetInt32(1000, 9999).ToString();
SHA512 sha512 = SHA512.Create(Hash);
...At this point, sha512 is null and the next line throws NullReferenceException which causes the server to return an HTTP 500 (Internal Server Error) status.
The SHA512.Create(String) overload expects the name of an implementation, not the data you want to hash.
More information here.
Instead, try something like this:
var random = RandomNumberGenerator.GetInt32(1000, 9999).ToString();
string hashed = string.Empty;
using (SHA512 sha512 = SHA512.Create())
{
var hashValue = sha512.ComputeHash(System.Text.Encoding.Default.GetBytes(random));
hashed = System.Convert.ToBase64String(hashValue);
}
user.Token_Validation = hashed;

Categories

Resources