Issue on signalR dotnet core - asp.net-core

Issue
Scenario:
SignalR version 1.0.0 RC1 Final
DotNet Core 2.0
Tipical: User - Posts - Comments models.
Issue:
When I send a model through websocket fails silently (no message is send to clients). It works fine if I set to null navigation property:
_context.Posts.Add(model);
_context.SaveChanges();
model.User.Posts = null; // <- removing this line fails silently
_hub.Clients.All.SendAsync("AddAsync", model);
How can I diagnose what is happens? Someone knows what is the reason?
Some unnecesary code details
User.cs
public Guid Id { get; set; }
// ...
public virtual List<Post> Posts { get; set; } = new List<Post>();
Post.cs
public Guid Id { get; set; }
// ...
public virtual User User { get; set; }
PostsController.cs
private IHubContext<PostsHub> _hub;
private DatabaseContext _context;
public PostsController(IHubContext<PostsHub> hub)
{
_hub = hub;
}
// ...
[HttpPost]
public async Task<ActionResult> PostAsync([FromBody] Post model)
{
// ...
_context.Posts.Add(model);
_context.SaveChanges();
model.User.Posts = null;
_hub.Clients.All.SendAsync("AddAsync", model);
// ...
}

With json convert Serialize the model to json and deserialize it again as Post object just before sending it.
If there is an error it should popup there, because if there is a dependency loop json convert should throw exception

Related

fluent validator in class library not work in asp.net core

when i put fluent validators in asp.net core client side validation project exactly work
but when i put validator in class library not work
My model and validator in class library :
using FluentValidation;
namespace ClassLibrary1
{
public class Person
{
public string Name { get; set; }
public string Family { get; set; }
public int Age { get; set; }
}
public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(c => c.Name).NotEmpty().WithMessage("Name Is Empty");
}
}
}
In program.cs file :
services.AddFluentValidationAutoValidation(M =>
{
M.DisableDataAnnotationsValidation = true;
}).AddFluentValidationClientsideAdapters()
.AddValidatorsFromAssemblyContaining<PersonValidator>();
I can't reproduce the issue, and it works in my side, I will show my test steps.
Steps
my project structure.
The person.cs code same as yours
The program.cs code same as yours
My test method in Controller.
[HttpPost]
[Route("Test")]
public IActionResult Test([FromBody]Person model)
{
if (!ModelState.IsValid) //<----Validate here
{
return new BadRequestObjectResult(ModelState);
}
return Ok();
//Other Code..
}
Test result and it works.
I found the solution.
When the class library is nullable, the client-side validation in ASP.NET Core does not work.
Solution:
Remove <Nullable>enable</Nullable> from the *.csproj
Define nullable property:
public string? name{get;set}

ASP.Net Core 2.1 OData Parameter issue

I am trying to get OData to work in ASP.Net 2.1. The main Get is ok as I get the results from the DB.
When I try to call the second Get with a Parameter it returns with a 404. I put a breakpoint on the second get and it never hits. The http "get" statement http://localhost:5000/odata/userrole('Admin')
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/odata/userrole('Admin')
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 423.7117ms 404
What am I not doing right here? Thank you.
Microsoft.AspNetCore.Odata = v7.1.0
namespace MyApp.Controllers
{
public class UserRoleController : ODataController
{
private IMyDB _db;
public UserRoleController(IMyDB db)
{
_db = db;
}
[EnableQuery(PageSize = 20)]
public IActionResult Get()
{
return Ok(_db.UserRole().AsQueryable());
}
[EnableQuery]
public IActionResult Get([FromODataUri] string roletype)
{
return Ok(_db.UserRole().Find(roletype));
}
}
}
I have found out what is the problem. In the future if someone is working with OData this might help in countless hours of debugging.
The UserRole Class I have looks like below. Notice the data annotation [Key], that is what is causing the OData to ignore the path. I need to change the data Type to match the UserRole class "key" public IActionResult Get([FromODataUri] Guid roleid)
That fixes the problem. Hope this helps people in the future.
public class UserRole
{
[Key]
public Guid RoleId { get; set; }
public Role RoleType { get; set; }
public string RoleName { get; set; }
}

AspNetCore - Array on QueryString BadRequest

I have a simple httpGet endpoint that receive a string
[Authorize]
[HttpGet("filter/{filters}"), Route("filter")]
public virtual IActionResult Filter(string filters)
{
try
{
return Ok(Service.GetByFilter(JsonConvert.DeserializeObject<FilterHelper>(filters)));
}
catch (Exception ex)
{
Logger.LogWarning(LoggingEvents.GET_ENTITY_BY_FILTER, ex, Localizer["EntityGetFilterFailed"]);
return Ok(new RequestResult(StatusResult.Danger, new Message(string.Format(Localizer["UnexpectedError"], LoggingEvents.GET_ENTITY_BY_FILTER))));
}
}
Yesterday, it was working fine, today my PC fails on boot and is been restored, after that my API is giving me bad request results.
If I call this url
http://localhost:23272/api/users/filter?filters={"Page":1,"PageSize":5}
The request works correct, but if I try to call this url ( which works earlier )
http://localhost:23272/api/users/filter?filters={"Page":1,"PageSize":5, "FilterItens": []}
The API send me back a BadRequest status (400). Already tried to pass the array with some itens, badrequest too.
Any ideias on how to handle that ?
Detail: If I call from Postman, it works correct.
http://localhost:23272/api/users/filter?filters={"Page":1,"PageSize":5, "FilterItens": []}
My model
public class FilterHelper
{
#region Properties
public int Page { get; set; }
public int PageSize { get; set; }
public ICollection<FilterItem> FilterItens { get; set; }
public string[] Includes { get; set; }
#endregion
}
The root problem was:
My Authorization token was too large for the request header.
The token length was with about 16k length and was broking the header.
I made a change to the token and It finished with 800 length, everything Works again.

Return Entity Framework objects over WCF

We have a problem concerning Entity Framework objects and sending them through WCF.
We have a database, and Entity Framework created classes from that database, a 'Wallet' class in this particular situation.
We try to transfer a Wallet using this code:
public Wallet getWallet()
{
Wallet w = new Wallet();
w.name = "myname";
w.walletID = 123;
return w;
}
We need to transfer that Wallet class, but it won't work, we always encounter the same exception:
"An error occurred while receiving the HTTP response to localhost:8860/ComplementaryCoins.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
We searched on the internet, and there is a possibility that the error is due to the need of serialization of Entity Framework-objects.
We have absolutely no idea if this could be the case, and if this is the case, how to solve it.
Our DataContract looks like this (very simple):
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } }
[DataMember]
public string getname { get { return name; } }
}
Does anyone ever encountered this problem?
EDIT: Our Entity Framework created class looks like this:
namespace ComplementaryCoins
{
using System;
using System.Collections.Generic;
public partial class Wallet
{
public Wallet()
{
this.Transaction = new HashSet<Transaction>();
this.Transaction1 = new HashSet<Transaction>();
this.User_Wallet = new HashSet<User_Wallet>();
this.Wallet_Item = new HashSet<Wallet_Item>();
}
public int walletID { get; set; }
public string name { get; set; }
public virtual ICollection<Transaction> Transaction { get; set; }
public virtual ICollection<Transaction> Transaction1 { get; set; }
public virtual ICollection<User_Wallet> User_Wallet { get; set; }
public virtual ICollection<Wallet_Item> Wallet_Item { get; set; }
}
}
Thanks for helping us.
I had the same problem some time ago and the solution for this was:
The entity framework was returning a serialized class instead of normal class.
eg. Wallet_asfawfklnaewfklawlfkawlfjlwfejlkef instead of Wallet
To solve that you can add this code:
base.Configuration.ProxyCreationEnabled = false;
in your Context file.
Since the context file is auto generated you can add it in the Context.tt
In the Context.tt file it can be added around lines 55-65:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
base.Configuration.ProxyCreationEnabled = false;
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
Try specifying a setter for the properties, something like this :
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } set { } }
[DataMember]
public string getname { get { return name; } set { } }
}
If it still doesn't work, you may consider creating an intermediate POCO class for this purpose, and use mapper library like AutoMapper or ValueInjecter to transfer the data from the EF objects.
The POCO class should have same properties as your EF class :
[DataContract]
public class WalletDTO
{
[DataMember]
public int walletID { get; set; }
[DataMember]
public string name { get; set; }
}
And modify your method to return this class instead :
public WalletDTO getWallet()
{
Wallet w = new Wallet(); // or get it from db using EF
var dto = new WalletDTO();
//assuming we are using ValueInjecter, this code below will transfer all matched properties from w to dto
dto.InjectFrom(w);
return dto;
}
Are you trying to recieve a IEnumerable<Wallets>? If - yes, please modify your server class that returns the IEnumerable by adding .ToArray() method

AutoMapper Update Actions in ASP.NET MVC

This is probably quite straight forward for some, however I'm a bit confused and can't find a decent example. Say I'm using view models and my POST action takes in that view model. Typically I would do something along the following lines:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
User user = Mapper.Map<UserViewModel, User>(uvm);
_repository.Update(user);
return RedirectToAction("Index");
}
Although this isn't the full picture. The mapping would work fine, however if I were to just update what I've mapped then it'd get rid of valuable data in the database because of course in this case I'm not updating the password or other details.
My repository looks something like this:
public void Update(User user)
{
User u = Session.QueryOver<User>().Where(x => x.UserName == user.UserName).SingleOrDefault();
if (u == null)
throw new Exception("User not found");
u.Forename = user.Forename;
u.Surname = user.Surname;
u.EmailAddress = user.EmailAddress;
}
[I'm using NHibernate so it'll save the object back to the DB once the session is closed (after the request has finished) automatically for me.]
So my question is, in my repository should I load the "User" entity, then update the values I want, and then save it back, or is there another method to do this? The reason I ask is because it seems a bit... "manual" if you see what I mean? Perhaps it is correct, but I just wanted to see opinions of those with more experience in this area.
Cheers
I use the following approach:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
User user = _userRepository.FindById(uvm.Id);
user.Forename = uvm.Forename;
user.Surname = uvm.Surname;
user.EmailAddress = uvm.EmailAddress;
_userRepository.Update(user);
return RedirectToAction("Index");
}
UPDATE:
To address the comments about AutoMapper here's how to proceed:
Let's take for example the following classes:
public class UserViewModel
{
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
}
public class User
{
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
We don't want to modify the user password in the UI. So we express our intention to AutoMapper:
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
and then:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
// Fetch the original model we would like to update
User user = _userRepository.FindById(uvm.Id);
Mapper.Map(uvm, user);
// At this stage the user model will have its
// Forename, Surname and EmailAddress properties
// updated from the view model and its Password property
// will remain the one we got from the repository
_userRepository.Update(user);
return RedirectToAction("Index");
}
UPDATE 2:
To address the question in the comments about configuring AutoMapper I usually use Profiles:
public class UsersProfile : Profile
{
protected override void Configure()
{
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
Mapper
.CreateMap<User, UserViewModel>();
}
}
and then have a registry class which registers all the mappers:
public class MappingsRegistry
{
public static void Configure()
{
Mapper.AddProfile(new UsersProfile());
Mapper.AddProfile(new SomeOtherProfile());
...
}
}
which is called in Application_Start:
MappingsRegistry.Configure();
Finally my controllers have a reference to the mapping engine:
public class UsersController : Controller
{
private readonly IUsersRepository _repository;
private readonly IMappingEngine _mappingEngine;
public ContratsFCController(IUsersRepository repository, IMappingEngine mapperEngine)
{
_repository = repository;
_mapperEngine = mapperEngine;
}
[AutoMap(typeof(User), typeof(UserViewModel))]
public ActionResult Update(int id)
{
var user = _repository.FindById(id);
return View(user);
}
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
if (!ModelState.IsValid)
{
return View(uvm);
}
var user = _repository.FindById(uvm.Id);
_mapperEngine.Map(uvm, user);
_repository.Update(user);
return RedirectToAction("Index");
}
}
Now all that's left is to instruct your DI framework to pass the Mapper.Engine property to the constructor and in your unit tests obviously substitute them with an appropriate mock.