JsonConvert.DeserializeObject error mvc asp.net newtonjson - c#

I use HttpRequestMessage my model is some different VirtualStock. My API JSON model as below
[{
"_id": "5f2d66ae6abb1d6870e926ac",
"MessageId": "EAD8B97887FFC1F180F3005056BF2302",
"MessageType": "CreateDiscount",
"RequestedBy": "AAA",
"RequestedDate": "2020-08-07T17:22:58.000Z",
"Data": {
"_id": "5f2d66ae6abb1d6870e926ad",
"Sku": "A",
"SkuId": "EAD8B97887FFC0F180F3005056BF2302",
"Price": 1759.17,
"PurchasePrice": 1768.88,
"StartDateTime": "2020-08-07T17:24:34.000Z",
"EndDateTime": "2020-09-01T00:00:00.000Z",
"VirtualStock": "1000",
"MaximumQuantityForCart": 10,
"CurrencyCode": "949"
},
"__v": 0
},
{
"_id": "5f2d65daf7168b686d9474b9",
"MessageId": "EAD8B8F92D90CDF180F3005056BF2302",
"MessageType": "UpdateDiscount",
"RequestedBy": "AAA",
"RequestedDate": "2020-08-07T17:19:26.000Z",
"Data": {
"_id": "5f2d65daf7168b686d9474c5",
"Sku": "A",
"SkuId": "EAD8B8F92D90C5F180F3005056BF2302",
"DiscountId": "8c3975a0-ea85-48e9-9c31-ea488bdad5c4",
"Price": 296.2,
"PurchasePrice": 239.91,
"StartDateTime": "2020-08-01T14:35:00.000Z",
"EndDateTime": "2020-08-07T17:23:08.000Z",
"VirtualStock": {
"Value": "100",
"IsAssigned": true
},
"MaximumQuantityForCart": 25,
"CurrencyCode": "949"
},
"__v": 0
}
]
I defined my model VirtualStock like object and nullable.
public object? VirtualStock { get; set; }
but I get error parsing how can fix this?
I saw with debug Deserialize Success but showing JSON GetDiscount method I have exceptions
controller code as below
public IActionResult Discount()
{
return View();
}
public async Task<IActionResult> GetDiscount()
{
return Json(new { data = await _log.GetAllAsync("http://0.0.0.0/DiscountLog?pagesize=9&page=16") });
}
my Repository
public async Task<IEnumerable<T>> GetAllAsync(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
var client = _clientFactory.CreateClient();
HttpResponseMessage response = await client.SendAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var jsonString = await response.Content.ReadAsStringAsync();
var jsonSerializerSettings = new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore
};
return JsonConvert.DeserializeObject<IEnumerable<T>>(jsonString,jsonSerializerSettings);
}
return null;
}
My error:
An unhandled exception occurred while processing the request.
NotSupportedException: The collection type 'System.Object' on
'RetailLogWeb.Models.LogItem.VirtualStock' is not supported.
System.Text.Json.JsonPropertyInfoNotNullable<TClass,
TDeclaredProperty, TRuntimeProperty,
TConverter>.GetDictionaryKeyAndValueFromGenericDictionary(ref
WriteStackFrame writeStackFrame, out string key, out object value)
NotSupportedException: The collection type 'System.Object' on
'RetailLogWeb.Models.LogItem.VirtualStock' is not supported.
System.Text.Json.JsonPropertyInfoNotNullable<TClass,
TDeclaredProperty, TRuntimeProperty,
TConverter>.GetDictionaryKeyAndValueFromGenericDictionary(ref
WriteStackFrame writeStackFrame, out string key, out object value)
System.Text.Json.JsonPropertyInfo.GetDictionaryKeyAndValue(ref
WriteStackFrame writeStackFrame, out string key, out object value)
System.Text.Json.JsonSerializer.HandleDictionary(JsonClassInfo
elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter
writer, ref WriteStack state)
System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, int
originalWriterDepth, int flushThreshold, JsonSerializerOptions
options, ref WriteStack state)
System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, object
value, Type inputType, JsonSerializerOptions options,
CancellationToken cancellationToken)
Microsoft.AspNetCore.Mvc.Infrastructure.SystemTextJsonResultExecutor.ExecuteAsync(ActionContext
context, JsonResult result)
Microsoft.AspNetCore.Mvc.Infrastructure.SystemTextJsonResultExecutor.ExecuteAsync(ActionContext
context, JsonResult result)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0<TFilter,
TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next,
Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed
context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter,
TFilterAsync>(ref State next, ref Scope scope, ref object state, ref
bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker
invoker, Task lastTask, State next, Scope scope, object state, bool
isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed
context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State
next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker
invoker, Task lastTask, State next, Scope scope, object state, bool
isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker
invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint
endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext
context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)

OP figured it out as shared in the comments, but I'll provide an answer here just to close out the question. As explained in this answer, the solution was to add a variant of this line:
services.AddControllers().AddNewtonsoftJson();
This was needed because Newtonsoft.Json was removed during a recent upgrade to .NET Core, and this adds it back in. Note that this assumes you have the Microsoft.AspNetCore.Mvc.NewtonsoftJson package installed.
More info here.

Related

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));

What is the reason for this error? (.Net 5, C#)

I am coding a dynamic website, everything works but I noticed an error in the terminal.
Error:
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot perform runtime binding on a null reference
at CallSite.Target(Closure , CallSite , Object )
at AspNetCore.Views_Content_CategoryContent.ExecuteAsync() in C:\Users\lenovo\Desktop\Projeler\Wutemp\Views\Content\CategoryContent.cshtml:line 2
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable`1 statusCode)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
at Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
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 ---
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.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Controller (Edit: I accidentally deleted the ViewBag part):
[Route("Content/{url}/{id}")]
public IActionResult Index(int id)
{
var content = repoContents.TGet(id);
//If content has a category, go to CategoryContent
if(content.ContentCategory != null && content.ContentCategory != 0){
return RedirectToAction("CategoryContent", new
{
category = repoContentCategories.TGet((int)content.ContentCategory).Title,
url = content.URL,
id = content.ID
});
}
else{
ViewBag.Content = content;
return View();
}
}
What is the problem, and how can I solve it?
I solved the problem.
I used model instead of ViewBag mentioned in comments it didn't work I kept getting the same error
I think the problem is due to the similarity of the route parts of the 2 Views.
Index:
[Route("Content/{url}/{id}")]
public IActionResult Index(int id)
{
var content = repoContents.TGet(id);
//If content has a category, go to CategoryContent
if(content.ContentCategory != null && content.ContentCategory != 0){
return RedirectToAction("CategoryContent", new
{
category = repoContentCategories.TGet((int)content.ContentCategory).Title,
url = content.URL,
id = content.ID
});
}
else{
ViewBag.Content = content;
return View();
}
}
CategoryContent:
[Route("Content/{category}/{url}/{id}")]
public IActionResult CategoryContent(int id)
{
ViewBag.Content = repoContents.TGet(id);
return View();
}
I solved the problem by changing the Route part of the ContentCategory page
[Route("Content/{category}/{url}/{id}")]
|
V
[Route("Category-Content/{category}/{url}/{id}")]

Value cannot be null. (Parameter 'value')

I have created a method where a user can log into my application and then a Token is sent to the user so it can be used to view any other views in the application.When i test the login method on postman i get the 500 server error as below instead of the token. I have seen similar questions/posts but haven't seen one that is related to JWT
Below is the output
System.ArgumentNullException: Value cannot be null. (Parameter 'value')
at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue)
at System.Security.Claims.Claim..ctor(String type, String value)
at Coop_Marketing.Controllers.AccountController.Login(LoginViewModel formdata) in /Users/Ken/Desktop/Coop_Marketing/Coop_Marketing/Coop_Marketing/Controllers/AccountController.cs:line 101
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.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.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.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.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
The Account controller with the login method is below
public async Task<IActionResult> Login([FromBody] LoginViewModel formdata)
{
// Get the User from database
var user = await _userManager.FindByNameAsync(formdata.Username);
var roles = await _userManager.GetRolesAsync(user);
var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_appSettings.Secret));
double tokenExpiryTime = Convert.ToDouble(_appSettings.ExpireTime);
if(user !=null && await _userManager.CheckPasswordAsync(user,formdata.Password))
{
var tokenhandler = new JwtSecurityTokenHandler();
// Describe the token used by the user to log in
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, formdata.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Role, roles.FirstOrDefault()),
new Claim("LoggedOn", DateTime.Now.ToString()),
}),
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature),
Issuer = _appSettings.Site,
Audience = _appSettings.Audience,
Expires = DateTime.UtcNow.AddMinutes(tokenExpiryTime)
};
// Generate the token
var token = tokenhandler.CreateToken(tokenDescriptor);
return Ok(new {token= tokenhandler.WriteToken(token), expiration = token.ValidTo,username = user.UserName,userRole = roles.FirstOrDefault()});
}
// Return error
ModelState.AddModelError("","Username/Password was not Found");
return Unauthorized(new { LoginError = "Please check the Login Credentials - Invalid Username/Password was entered" });
}
}
}
And the LoginViewModel
public class LoginViewModel
{
[Required]
[Display(Name = "User Name")]
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
The problem is with > var tokenDescriptor = new SecurityTokenDescriptor as per line 101 in the code which is mentioned in the output. However,I have failed to figure out why I am getting the error.
So it is like Claim has a null parameter. for that, you found this issue.
Update your code like below:-
.....
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(JwtRegisteredClaimNames.NameId,formdata.UserName), //modified
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), //modified
new Claim(ClaimTypes.Role, roles.FirstOrDefault()),
new Claim("LoggedOn", DateTime.Now.ToString()),
}),
.....
Hope it will resolve your issue.
As you can see in the official documentation, the constructor will throw an argument null exception when the claim type/value is null.
So it seems you are passing a null value(s) to the Claim constructor when creating the Claim object array.
As #Jasen mentioned to make the debugging easier change the one-line Claims array creation just for debugging and put a breakpoint to identify which claim is the culprit.

Polymorphic model binding for generic type parameter

I'm sending a partial object along with a file.
var formData = new FormData();
formData.append('model', JSON.stringify({ ... }));
formData.append('file', file, file.name)
// send formData to backend
My controller method is using Delta<T> from OData.
[HttpPatch("{id}")]
public async Task<IActionResult> Patch(
[FromRoute] Guid id,
[ModelBinder(BinderType = typeof(FormDataJsonBinder))] Delta<AbstractModel> model,
IFormFile file = null
)
However I'm not sending Delta<AbstractModel> as AbstractModel is an abstract class. So I'm really sending Delta<DerivedModel> or Delta<AnotherDerivedModel> etc...
My problem is that ASP.NET keeps throwing saying that it cannot cast to Delta<DerivedModel>
System.InvalidCastException: Unable to cast object of type 'Microsoft.AspNet.OData.Delta`1[DerivedModel]' to type 'Microsoft.AspNet.OData.Delta`1[AbstractModel]'.
at lambda_method308(Closure , Object , Object[] )
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.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
Here's my FormDataJsonBinder
public class FormDataJsonBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
// Fetch the value of the argument by name and set it to the model state
string fieldName = bindingContext.FieldName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(fieldName);
if (valueProviderResult == ValueProviderResult.None) return Task.CompletedTask;
else bindingContext.ModelState.SetModelValue(fieldName, valueProviderResult);
// Do nothing if the value is null or empty
string value = valueProviderResult.FirstValue;
if (string.IsNullOrEmpty(value)) return Task.CompletedTask;
try
{
// Delta is a special case
if (bindingContext.ModelType.IsAssignableTo(typeof(Delta)))
{
var jsonObject = JObject.Parse(value);
// This extracts type T from Delta<T>
var innerModelType = bindingContext.ModelType.GenericTypeArguments.First();
if (jsonObject["#odata.type"] is not null)
{
var odataType = jsonObject["#odata.type"].Value<string>();
innerModelType = Type.GetType(odataType);
}
var innerModel = JsonConvert.DeserializeObject(value, innerModelType);
var deltaType = typeof(Delta<>).MakeGenericType(innerModelType);
var delta = Activator.CreateInstance(deltaType) as IDelta;
foreach (var property in jsonObject.Properties())
{
delta.TrySetPropertyValue(property.Name, innerModel.GetType().GetProperty(property.Name)?.GetValue(innerModel));
}
bindingContext.Result = ModelBindingResult.Success(delta);
}
else
{
// Deserialize the provided value and set the binding result
var result = JsonConvert.DeserializeObject(value, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
}
catch (JsonException)
{
bindingContext.Result = ModelBindingResult.Failed();
}
return Task.CompletedTask;
}
}
I also tried using a IModelBinderProvider as recommended here, but that also just throws "cannot cast to Delta<DerivedModel>"
The solution was to create the Delta object the right way.
var delta = Activator.CreateInstance(bindingContext.ModelType, innerModelType) as IDelta;

Threading issue when using IViewComponentHelper to convert ViewComponent to HTML string

I'm trying to use IViewComponentHelper in a multi threaded manner. But it's throwing :( (see full exception at bottom of post)
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.AspNetCore.Http.DefaultHttpContext.get_Items()
I have no idea why.
Here's my setup: (simplified for brevity)
Controller:
public async Task<IActionResult> Get()
{
var pdfStream = await _pdfService.GenerateAsync();
return File(pdfStream, "application/pdf");
}
PdfService:
public class PdfService
{
private readonly PdfSDK _pdfSDK;
public constructor(PdfSDK pdfSDK)
{
_pdfSDK = pdfSDK;
}
public async Task<Stream> GenerateAsync()
{
List<Task<PdfDocument>> tasks = await _context.Foos.Select(x => DoWorkAsync(x));
var pdfs = await Task.WhenAll(tasks);
var mergedPdf = _pdfSDK.MergePdfs(pdfs);
return mergedPdf.Stream;
}
private async Task<PdfDocument> DoWorkAsync(Foo foo)
{
var html = await _renderViewComponentService.RenderViewComponentAsStringAsync<MyViewComponent>(foo);
var document = await _pdfSDK.HtmlToDocumentAsync(html);
return document;
}
}
PdfSDK:
public class PdfSDK
{
public async Task<PdfDocument> HtmlToDocumentAsync(string html)
{
using var pdfEngine = new PdfEngine();
var pdf = await pdfEngine.HtmlAsPdfAsync(html);
return pdf;
}
public PdfDocument MergePdfs(params PdfDocument[] pdfs)
{
var pdf = PdfDocument.Merge(pdfs);
return pdf;
}
}
MyViewComponent:
public class MyViewComponent : ViewComponent
{
public IViewComponentResult Invoke(Foo args)
{
return View(args);
}
}
Default.cshtml
#model Foo
<h1>Hello from #Foo.Id<h1>
RenderViewComponentService:
public class RenderViewComponentService
{
private readonly IServiceProvider _serviceProvider;
private readonly ITempDataProvider _tempDataProvider;
private readonly IViewComponentHelper _viewComponentHelper;
public RenderViewComponentService(
IServiceProvider serviceProvider,
ITempDataProvider tempDataProvider,
IViewComponentHelper viewComponentHelper
)
{
_serviceProvider = serviceProvider;
_tempDataProvider = tempDataProvider;
_viewComponentHelper = viewComponentHelper;
}
public async Task<string> RenderViewComponentAsStringAsync<TViewComponent>(object args)
where TViewComponent : ViewComponent
{
var viewContext = GetFakeViewContext();
(_viewComponentHelper as IViewContextAware).Contextualize(viewContext);
// this appears to call InvokeAsync in TViewComponent, but it'll also call Invoke (synchronously) if it's implemented
// see https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.1#perform-synchronous-work
var htmlContent = await _viewComponentHelper.InvokeAsync<TViewComponent>(args); // exception is thrown here!
using var stringWriter = new StringWriter();
htmlContent.WriteTo(stringWriter, HtmlEncoder.Default);
var html = stringWriter.ToString();
return html;
}
private ViewContext GetFakeViewContext(ActionContext actionContext = null, TextWriter writer = null)
{
actionContext ??= GetFakeActionContext();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
var tempData = new TempDataDictionary(actionContext.HttpContext, _tempDataProvider);
var viewContext = new ViewContext(
actionContext,
NullView.Instance,
viewData,
tempData,
writer ?? TextWriter.Null,
new HtmlHelperOptions());
return viewContext;
}
private ActionContext GetFakeActionContext()
{
var httpContext = new DefaultHttpContext
{
RequestServices = _serviceProvider,
};
var routeData = new RouteData();
var actionDescriptor = new ActionDescriptor();
return new ActionContext(httpContext, routeData, actionDescriptor);
}
private class NullView : IView
{
public static readonly NullView Instance = new NullView();
public string Path => string.Empty;
public Task RenderAsync(ViewContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
return Task.CompletedTask;
}
}
}
Exception:
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.AspNetCore.Http.DefaultHttpContext.get_Items()
at Microsoft.AspNetCore.Mvc.Routing.UrlHelperFactory.GetUrlHelper(ActionContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorPageActivator.<>c__DisplayClass4_0.<.ctor>b__0(ViewContext context)
at Microsoft.Extensions.Internal.PropertyActivator`1.Activate(Object instance, TContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorPagePropertyActivator.Activate(Object page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorPageActivator.Activate(IRazorPage page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
at Microsoft.AspNetCore.Mvc.ViewComponents.ViewViewComponentResult.ExecuteAsync(ViewComponentContext context)
at Microsoft.AspNetCore.Mvc.ViewComponents.DefaultViewComponentInvoker.InvokeAsync(ViewComponentContext context)
at Microsoft.AspNetCore.Mvc.ViewComponents.DefaultViewComponentHelper.InvokeCoreAsync(ViewComponentDescriptor descriptor, Object arguments)
at MyProject.Services.RenderViewComponentService.RenderViewComponentToStringAsync[TViewComponent](Object args) in MyProject\Services\RenderViewComponentService.cs:line ??
at MyProject.Services.PdfService.DoWorkAsync(Foo foo) in MyProject\Services\PdfService.cs:line ??
at MyProject.Services.PdfService.GenerateAsync() in MyProject\Services\PdfService.cs:line ??
at MyProject.Controllers.MyController.Get() in MyProject\Controllers\MyController.cs:line ??
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.<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__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.HeaderPropagation.HeaderPropagationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
I think the problem is how I Contextualize the IViewComponentHelper. But I'm at a loss on how else to do it.
Can you spot anything I'm doing wrong?

Categories

Resources