'FromBody' failed after upgrade to net core 3.0 with the new JsonSerializer - asp.net-core-3.0

After upgrade to net core 3.0 with the new json serializer Objects with the [FromBody] tag are null;
controller api
[HttpPost("")]
[AllowAnonymous]
public async Task<JwtToken> Login([FromBody] UserCredentials userCredentials)
{
...
}
Startup config
public class Startup
{
services.AddControllers();
...
}
public void Configure()
{
app.UseEndpoints(endpoints => { endpoints.MapControllers().RequireAuthorization(); });
...
}
public class UserCredentials
{
public string Password;
public string Username;
}

With Json.net serializer it was allowed to use fields. With the build in version of net core 3.0 the fields must be changed to properties.
public class UserCredentials
{
public string Password { get; set; }
public string Username { get; set; }
}

Related

Can't Figure Out How To Use AutoMapper With Post Action In RESTful Api

I have a simple RESTful API and this is the post route handler I'm trying to apply AutoMapper in:
[HttpPost]
[Route("[action]")]
public async Task<IActionResult> CreateHotel([FromBody]Hotel hotelCreateDto)
{
var hotel = _mapper.Map<Hotel>(hotelCreateDto);
var createdHotel = await _hotelService.CreateHotel(hotel);
var hotelReadDto = _mapper.Map<HotelReadDto>(createdHotel);
return CreatedAtAction("GetHotelById", new { id = hotelReadDto.Id }, hotelReadDto);
}
So in the request I get a hotelCreateDto which looks like that:
public class HotelCreateDto
{
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
}
and I map this to Hotel entity:
public class Hotel
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
}
and a new hotel object is created in the next line. However when hotelReadDto is going to be assigned to the new mapped object, a 500 error occurs: "AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping."
Could you catch a mistake here? I don't know where I'm doing wrong.
Edit: there'S also this things after the error above: "Mapping types:
Object -> HotelReadDto
System.Object -> HotelFinder.DTO.DTOs.HotelReadDto"
Edit2: Here is the code in the Configure Services:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
And in the Profile class:
public class HotelProfile : Profile
{
public HotelProfile()
{
CreateMap<Hotel, HotelReadDto>();
CreateMap<HotelCreateDto, Hotel>();
}
}
Add this in your services in startup :
it's reusable and cleaner
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
}
add these interface and class in your project
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
using AutoMapper;
using System;
using System.Linq;
using System.Reflection;
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.GetInterfaces()
.Any(i =>i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetMethod("Mapping")
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
methodInfo?.Invoke(instance, new object[] { this });
}
}
}
and your dto be like this (map hotel to HotelDto):
public class HotelCreateDto : IMapFrom<HotelCreateDto>
{
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<Hotel,HotelCreateDto>();
}
}

Create sub-type instances in ASP.NET Core 2.2

I am developing an ASP.NET Core application. There are several entities, some of which have one-to-many relations with each other i.e.
Project 1->* Priority, Project 1->* Milestone; Milestone 1->* Issue...
All navigation properties of all entities are virtual and I have already configured lazy loading in OnConfiguring method in the DbContext class:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
}
The situation is following: I have already created an instance or Project type and now I want to create instances of other sub-types which should be related to the already created Project instance.
My logic for creating of Priority instance is to pass a projectId to the priority entity which will be added to the corresponding DbSet.
The entity models:
public class Project
{
[Required]
public virtual ICollection<Priority> Priorities { get; set; }
}
public class Priority
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string ProjectId { get; set; }
[Required]
public virtual Project Project { get; set; }
}
The priorityInputModel from the form:
public class PriorityInputModel : IMapTo<PriorityServiceModel>
{
[Required]
public string Name { get; set; }
[Required]
public string ProjectId { get; set; }
}
The PriorityServiceModel:
public class PriorityServiceModel : IMapFrom<Priority>, IMapTo<Priority>
{
public int Id { get; set; }
public string Name { get; set; }
public string ProjectId { get; set; }
public ProjectServiceModel Project { get; set; }
}
The controller:
[HttpPost]
public async Task<ActionResult> Create(PriorityInputModel inputModel)
{
var priorityServiceModel = inputModel.To<PriorityServiceModel>();
var priorityServiceModelResult = await this.priorityService.CreateAsync(priorityServiceModel);
return this.RedirectToRoute(
routeName: ValuesConstants.DefaultRouteName,
routeValues: new
{
controller = ValuesConstants.ProjectControllerName,
action = ValuesConstants.DetailsActionName,
id = priorityServiceModelResult.ProjectId,
});
}
The service method:
public async Task<PriorityServiceModel> CreateAsync(PriorityServiceModel priorityServiceModel)
{
var priority = priorityServiceModel.To<Priority>();
var priorityResult = await this.repository.AddAsync(priority);
var priorityServiceModelResult = priorityResult.To<PriorityServiceModel>();
return priorityServiceModelResult;
}
The repository method:
public virtual async Task<TEntity> AddAsync(TEntity entity)
{
await this.DbSet.AddAsync(entity);
await this.SaveChangesAsync();
return entity;
}
I expected when the changes had been saved, the returned Priority object to have initialized Project navigation property with the corresponding project instance but the returned object has value as follows:
Priority {
"Id" = 33,
"Name" = "Lowest",
"ProjectId" = "bd15bd0c-fed7-4186-ade0-ad9338dff1d7"
"Project" = null
}
What is the reason for this and how could I receive the project instance initialized in the navigation property Project of newly created and returned Priority object?
Thanks in advance!

Logged user based connection string in .NET Core

I have an application that uses identity database to store users and customers.
Each customer has also a separate database with its data and its connection string is stored in Customer table in the identity database.
AspNetUsers has a field to tell which customer the user belongs to (also identity db).
I want to assign connection string to the user when he logs in and make it available in the application for the duration of the session.
I currently have customer model:
public partial class `Customer`
{
public int Id { get; set; }
public string Name { get; set; }
public int NoLicenses { get; set; }
public bool? Enabled { get; set; }
public string CustomerConnectionString { get; set; }
}
and user model:
public class ApplicationUser : IdentityUser
{
public string CustomerId { get; set; }
public bool? IsEnabled { get; set; }
// there ideally I'd have a connstring property
}
The models map db table fields.
I'm using .NET Core 1.1 and EF Core.
With the defalut ASP.NET Identity template , you can :
extend the ApplicationUser class in Models folder :
public class ApplicationUser : IdentityUser
{
public string CustomerId { get; set; }
public bool? IsEnabled { get; set; }
//add your custom claims
public string CustomerConnectionString { get; set; }
}
Add your custom model to ApplicationDbContext in Data folder :
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
public DbSet<Customer> Customers { get; set; }
}
Sync your database : Add-Migration xxxx , then run the Update-Database command in Package Manager Console . Now you have the Customer table and have CustomerConnectionString column in AspNetUsers table.
Create you own implementation of IUserClaimsPrincipalFactory by inheriting the default one to generate a ClaimsPrincipal from your user :
public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
public AppClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager
, RoleManager<IdentityRole> roleManager
, IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{ }
public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
var principal = await base.CreateAsync(user);
if (!string.IsNullOrWhiteSpace(user.CustomerId))
{
((ClaimsIdentity)principal.Identity).AddClaims(new[] {
new Claim("customid", user.CustomerId)
});
}
if (!string.IsNullOrWhiteSpace(user.CustomerConnectionString))
{
((ClaimsIdentity)principal.Identity).AddClaims(new[] {
new Claim("CustomerConnectionString", user.CustomerConnectionString)
});
}
return principal;
}
}
Register the custom factory you just created in your application startup class, after adding Identity service:
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddScoped<Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();
Then you could access the claims like :
var connectString = User.Claims.FirstOrDefault(c => c.Type == "CustomerConnectionString").Value;
Modify the creating/editing user view/controller , add the customer dropdownlist on view , get the custom id in Register function in AccountController , query the connectingString of custom from db , and save in ApplicationUser object :
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);

SignalR missing connection in HubCallerContext

Trying to do simple chat and sending user to the userTracker when he/she is connected
public override async Task OnConnectedAsync()
{
var user = Helper.GetUserInformationFromContext(Context);
await this.userTracker.AddUserAsync(Context.Connection, user);
await Clients.All.SendAsync("UsersJoined", new UserInformation[] { user });
await Clients.All.SendAsync("SetUsersOnline", await GetOnlineUsersAsync());
await base.OnConnectedAsync();
}
but in the old versions HubCallerContext is like this :
public HubCallerContext(HubConnectionContext connection);
public HubConnectionContext Connection { get; }
public ClaimsPrincipal User { get; }
public string ConnectionId { get; }
the version I am using ( 2.3.0 ) is like
protected HubCallerContext();
public abstract string ConnectionId { get; }
public abstract string UserIdentifier { get; }
public abstract ClaimsPrincipal User { get; }
public abstract IFeatureCollection Features { get; }
public abstract CancellationToken ConnectionAborted { get; }
public abstract void Abort();
So how can I get the missing Connection ?
You simple have to inject it where you use it
Sample:
public class YourClassWhereYouNeedHubContext
{
// Inject into constructor
public YourClassWhereYouNeedHubContext (IHubContext<VarDesignHub> hubcontext)
{
HubContext = hubcontext;
...
}
private IHubContext<VarDesignHub> HubContext
{
get;
set;
}
}
Then you can also call
await this.HubContext.Clients.All.InvokeAsync("Completed", id);
Please read also:
Call SignalR Core Hub method from Controller

Asp.Net Core: Add data to IdentityDbContext or use DbContext

I work with Asp.Net Core WebApi project.
Can I add my tables to IdentityDbContext, like this:
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{ }
public DbSet<ProgrammerRole> ProgrammerRoles { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<SubProject> SubProjects { get; set; }
public DbSet<Report> Reports { get; set; }
}
Or do I need to create a second DbContext. And if i create a second DbContext
how can I communicate wiht User in IdentityDbContext.
And my second question:
If i add data in IdentityDbContext, like above, How do I get the data from my tables in ApplicationDbContext?
Because i need to pass DbContextOptions object every time I create a new instance оf ApplicationDbContext. I do this in Startup.cs:
// ===== Add DbContext ========
var connectionString = Configuration.GetConnectionString("DbConnection");
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
I saw in older version of Asp.Net Core, that i can pass Connection String in IdentityDbContext constructor, but now only DbContextOptions.
And i can't do, for example this:
[AllowAnonymous]
[HttpGet]
public IEnumerable<Project> GetRoles()
{
using (var db = new ApplicationDbContext())
{
return db.Projects;
}
}
Can I add my tables to IdentityDbContext, like this:
Yes, it is how you create custom tables. You do not need to create another DbContext. E.g.
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Project> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Project>(entity =>
{
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(50);
});
base.OnModelCreating(builder);
}
}
Note: you might need to run dotnet ef migrations add Initial and dotnet ef database update for database migration.
using (var db = new ApplicationDbContext()) {...}
You should not create or manage ApplicationDbContext inside controller. If you do so, they become tightly coupled, and you cannot implement unit tests.
Instead, you let dependency inversion (DI) container manage it for you. E.g.
public class UserController : Controller
{
private readonly ApplicationDbContext _context;
public UserController(ApplicationDbContext context)
{
_context = context;
}
[AllowAnonymous]
[HttpGet]
public IEnumerable<Project> GetRoles()
{
return _context.Projects;
}
}
I solve my problem, i just replaced code in my ApplicationDbContext, and get connection string from method:
public class ApplicationDbContext : IdentityDbContext<User>
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(GetConnectionString());
}
private static string GetConnectionString()
{
const string databaseName = "EmployeeReportsDb";
const string databasePass = "SuperPuper_Random_DB-key!";
return $"Server=localhost;" +
$"database={databaseName};" +
$"Trusted_Connection = True;" +
$"MultipleActiveResultSets = True;" +
$"pwd={databasePass};" +
$"pooling=true;";
}
public DbSet<ProgrammerRole> ProgrammerRoles { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<SubProject> SubProjects { get; set; }
public DbSet<Report> Reports { get; set; }
}
here is the resource: https://medium.com/#ozgurgul/asp-net-core-2-0-webapi-jwt-authentication-with-identity-mysql-3698eeba6ff8

Resources