I am currently working on MVC4 in VS2010-SP1. I made one of the function in
the controller class Asynchronous. As part of that I made the controller class
derived from AsyncController and added the below two methods ( see code section 1 and
2 below). one method ending with Async(See Code Section 1 ) and another method ending
with Completed ( See Code Section 2 ). The problem is in the model class I am trying
to access my webservice with credentials from HttpContext ( See Code below 3 ). The
context is going null when making an asynchronous call. ie, In the new thread
httpcontext is not available. How to pass the context from main thread to new threads
created.
Code Section 1
public void SendPlotDataNewAsync(string fromDate, string toDate, string item)
{
AsyncManager.OutstandingOperations.Increment();
var highChartModel = new HighChartViewModel();
Task.Factory.StartNew(() =>
{
AsyncManager.Parameters["dataPlot"] =
highChartModel.GetGraphPlotPointsNew(fromDate, toDate, item);
AsyncManager.OutstandingOperations.Decrement();
});
}
Code Section 2
public JsonResult SendPlotDataNewCompleted(Dictionary<string, List<ChartData>>
dataPlot)
{
return Json(new { Data = dataPlot });
}
Code Section 3
public List<MeterReportData> GetMeterDataPointReading(MeterReadingRequestDto
meterPlotData)
{
var client = WcfClient.OpenWebServiceConnection<ReportReadingClient,
IReportReading>(null, (string)HttpContext.Current.Session["WebserviceCredentials"] ??
string.Empty);
try
{
return
ReadReportMapper.MeterReportReadMap(client.GetMeterDataPointReading(meterPlotData));
}
catch (Exception ex)
{
Log.Error("MetaData Exception:{0},{1},{2},{3}",
ex.GetType().ToString(), ex.Message, (ex.InnerException != null) ?
ex.InnerException.Message : String.Empty, " ");
throw;
}
finally
{
WcfClient.CloseWebServiceConnection<ReportReadingClient,
IReportReading> (client);
}
}
HttpContext.Current is null because your task is executed on a pool thread without AspNetSynchronizationContext synchronization context.
Use TaskScheduler.FromCurrentSynchronizationContext():
Task.Factory.StartNew(() =>
{
AsyncManager.Parameters["dataPlot"] =
highChartModel.GetGraphPlotPointsNew(fromDate, toDate, item);
AsyncManager.OutstandingOperations.Decrement();
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
Related
In ASP.NET Core 5 I had a custom Action Result as follows:
public class ErrorResult : ActionResult {
private readonly IList<Error> _errors;
public ErrorResult(IList<Error> errors) {
_errors = errors;
}
public override async Task ExecuteResultAsync(ActionContext context) {
// Code that creates Response
await result.ExecuteResultAsync(context);
}
}
Then on a Controller action I would have:
return new ErrorResult(errors);
How to do something similar in NET 6 Minimal APIs?
I have been looking at it and I think I should implement IResult.
But I am not sure if that is the solution or how to do it.
I have recently been playing around with minimal APIs and and working on global exception handling. Here is what I have come up with so far.
Create a class implementation of IResult
Create a constructor which will take an argument of the details you want going into your IResult response. APIErrorDetails is a custom implementation of mine similar to what you'd see in ProblemDetails in MVC. Method implementation is open to whatever your requirements are.
public class ExceptionAllResult : IResult
{
private readonly ApiErrorDetails _details;
public ExceptionAllResult(ApiErrorDetails details)
{
_details = details;
}
public async Task ExecuteAsync(HttpContext httpContext)
{
var jsonDetails = JsonSerializer.Serialize(_details);
httpContext.Response.ContentType = MediaTypeNames.Application.Json;
httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(jsonDetails);
httpContext.Response.StatusCode = _details.StatusCode;
await httpContext.Response.WriteAsync(jsonDetails);
}
}
Return result in your exception handling middleware in your Program.cs file.
app.UseExceptionHandler(
x =>
{
x.Run(
async context =>
{
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-6.0
var exceptionFeature = context.Features.Get<IExceptionHandlerPathFeature>();
// Whatever you want for null handling
if (exceptionFeature is null) throw new Exception();
// My result service for creating my API details from the HTTP context and exception. This returns the Result class seen in the code snippet above
var result = resultService.GetErrorResponse(exceptionFeature.Error, context);
await result.ExecuteAsync(context); // returns the custom result
});
}
);
If you still want to use MVC (Model-View-Controller), you still can use Custom ActionResult.
If you just want to use Minimal APIs to do the response, then you have to implement IResult, Task<IResult> or ValueTask<IResult>.
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
The following example uses the built-in result types to customize the response:
app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound())
.Produces<Todo>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
You can find more IResult implementation samples here: https://github.com/dotnet/aspnetcore/tree/main/src/Http/Http.Results/src
Link: Minimal APIs overview | Microsoft Docs
Please I need your help to solve FluentValidation issue. I have an old desktop application which I wrote a few years ago. I used FluentValidation Ver 4 and Now I'm trying to upgrade this application to use .Net framework 4.8 and FluentValidation Ver 10, but unfortunately, I couldn't continue because of an exception that I still cannot fix.
I have this customer class:
class Customer : MyClassBase
{
string _CustomerName = string.Empty;
public string CustomerName
{
get { return _CustomerName; }
set
{
if (_CustomerName == value)
return;
_CustomerName = value;
}
}
class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(obj => obj.CustomerName).NotEmpty().WithMessage("{PropertyName} is Empty");
}
}
protected override IValidator GetValidator()
{
return new CustomerValidator();
}
}
This is my base class:
class MyClassBase
{
public MyClassBase()
{
_Validator = GetValidator();
Validate();
}
protected IValidator _Validator = null;
protected IEnumerable<ValidationFailure> _ValidationErrors = null;
protected virtual IValidator GetValidator()
{
return null;
}
public IEnumerable<ValidationFailure> ValidationErrors
{
get { return _ValidationErrors; }
set { }
}
public void Validate()
{
if (_Validator != null)
{
var context = new ValidationContext<Object>(_Validator);
var results = _Validator.Validate(context); **// <======= Exception is here in this line**
_ValidationErrors = results.Errors;
}
}
public virtual bool IsValid
{
get
{
if (_ValidationErrors != null && _ValidationErrors.Count() > 0)
return false;
else
return true;
}
}
}
When I run the application test I get the below exception:
System.InvalidOperationException HResult=0x80131509 Message=Cannot
validate instances of type 'CustomerValidator'. This validator can
only validate instances of type 'Customer'. Source=FluentValidation
StackTrace: at
FluentValidation.ValidationContext1.GetFromNonGenericContext(IValidationContext context) in C:\Projects\FluentValidation\src\FluentValidation\IValidationContext.cs:line 211 at FluentValidation.AbstractValidator1.FluentValidation.IValidator.Validate(IValidationContext
context)
Please, what is the issue here and How can I fix it?
Thank you
Your overall implementation isn't what I'd consider normal usage however the problem is that you're asking FV to validate the validator instance, rather than the customer instance:
var context = new ValidationContext<Object>(_Validator);
var results = _Validator.Validate(context);
It should start working if you change it to:
var context = new ValidationContext<object>(this);
var results = _Validator.Validate(context);
You're stuck with using the object argument for the validation context unless you introduce a generic argument to the base class, or create it using reflection.
I have two webapi controllers in my MVC 5 project, one in the main domain:
namespace VincConsultancy.Controllers
{
public class QuestionGroupController : ApiController
{
//...
public IEnumerable<SAQQuestionGroup> Get(int id = 0)
{
var groups = (from g in repository.SAQQuestionGroups
where g.RequirementId == id
select g).ToList();
if (groups.Count == 0)
{
string message = string.Format("Groups with Req id = {0} not found", id);
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.NotFound, message)
);
}
return groups;
}
}
}
The other one in the area domain:
namespace VincConsultancy.Areas.admin.Controllers
{
public class SAQGroupsController : ApiController
{
//...
public IEnumerable<SAQGroup> Get(int id = 0)
{
var groups = (from g in this.dbCnxt.SAQQustnGroups
where g.RequirementId == id
select g).ToList();
if (groups.Count == 0)
{
string message = string.Format("Groups with Req id = {0} not found", id);
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.NotFound, message)
);
}
return groups;
}
}
}
In both controllers, I return error messages by throwing HttpResponseException. Visual Studio pauses the execution at the line of 'throw new HttpResponseException' only in the controller in the admin area, saying an exception occurs but was not handled by user code, that doesnt affect the functionality though, i just need to press the continue button every time. However it doesnt happen to the other controller. So, I am wondering if there is some mechanism handling the exceptions in main domain automatically. The throw-exception line is not supposed to be caught by my code, how should I get rid of visual studio breaking there?
I have a large data set that holds up my UI so I thought I would create a background call to fill my local repository and display my other controls in the UI right away and load the results of the async call when I get a response.
I found a helpful tutorial but I am still having to wait until all my results are loaded before I see any controls.
http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4
CODE UPDATED
I have created a folder called Services and created FacilitiesService.cs in that folder, see below:
public class FacilitiesService
{
internal async Task<List<Facility>> GetFacilitiesBySourceDbAsync(string sourceDb)
{
var fac = new Facility();
var con = Connect(); // Omitted
try
{
con.Open();
}
catch (Exception ex)
{
Console.WriteLine("Error: GetFacilityBySourceDb " + ex.Message);
}
try
{
OracleDataReader reader = null;
// Requestor
var cmd = new OracleCommand("SELECT FACILITY, FACILITY_ID FROM MyTable where (source_db = '" + sourceDb + "')", con);
reader = cmd.ExecuteReader();
while (reader.Read())
{
fac.Add(new Facility()
{
FacilityName = reader["FACILITY"].ToString(),
FacilityId = reader["FACILITY_ID"].ToString()
});
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
con.Dispose();
}
return fac;
}
}
Then in my HomeController.cs I have the following:
public class HomeController
{
public async Task<List<Facility>> FacilitiesAsync()
{
ViewBag.SyncOrAsync = "Asynchronous";
var service = new FacilitiesService();
this._facilities = new List<Facility>();
var facilities = await service.GetFacilitiesBySourceDbAsync("TEST");
foreach (var item in facilities)
{
Facility fac = new Facility()
{
FacilityName = item.FacilityName,
FacilityId = item.FacilityId
};
_facilities.Add(fac);
}
return _facilities;
}
}
This is my Facility (model) class:
public class Facility : List<Facility>
{
[Required]
[Display(Name = "Facility")]
public string FacilityName { get; set; }
public string FacilityId { get; set; }
public Facility()
{
// Default Constructor
}
public Facility(string facilityName, string facilityId)
{
this.FacilityName = facilityName;
this.FacilityId = facilityId;
}
}
I am using an Ajax call to kick off the FacilitiesAsync method in the code behind from a function call in the About.cshtml page when the user tabs off the tetbox/input control with an id of "tags", I could switch this to something else later but I get the data back when I step through the code-behind and I see both the beforeSend and complete functions fire an alert:
<script type="text/javascript">
$(function () {
var availableTags = [
// Neeed data from function call to populate this list
];
$("#tags").autocomplete({
source: availableTags
});
$("#tags").focusout(function () {
var result = null;
$.ajax({
beforeSend: function() {
alert("Testing");
},
url: "FacilitiesAsync",
success: function(data) {
result = data;
},
complete: function () {
alert(result);
}
});
});
});
</script>
#using (Html.BeginForm()) {
<div class="ui-widget">
<label for="tags">Tags: </label>
<input id="tags" />
</div>
}
This works GREAT! However, I want to take the data from the call made to the code-behind to populate the array availableTags and I'm not sure how to do that. Suggestions?
There's a few things wrong with the implementations, and one problem with the approach.
First, the GetFacilitiesBySourceDbAsync does not contain an await. The compiler will issue a warning in this situation, informing you that it's not really an asynchronous method when you do that; it will run synchronously. That's an important warning. If you want asynchronous code, you'll need to make it asynchronous all the way. This means using the asynchronous database methods (ExecuteReaderAsync, etc).
Secondly, the Task.WhenAll call in Index is meaningless (since you only pass it a single task). Also, since Index is synchronous but calls an asynchronous method, the code not shown is probably calling Result, which is a no-no on ASP.NET. As I explain on my blog, this will actually deadlock once your async code is actually asynchronous.
But even if you fix these, it won't do what you want. There's a problem with the approach, and that is that async doesn't change the HTTP protocol (this is also a link to my blog). ASP.NET MVC understands asynchronous methods and will not complete the request until the async action method completes. You'll need to use something like AJAX to get the web page to do what you want.
I have a WebApi method which returns an IQueryable of RavenDB documents. The caller needs to know the number of possible results (because the actual results are limited/paged).
So, I have something like this at the end of my WebApi method:
HttpContext.Current.Response.AddHeader("Total-Result-Count",
resultsStats.TotalResults.ToString())
Unfortunately, this won't work, because the IQueryable hasnt actually executed yet - so the stats will be empty.
How do I go about deferring the population of the stats response-header until AFTER the query has executed?
[UPDATE]
I attempted to apply an ActionFilter to capture the result after the controller action had executed... but it seems the ActionFilter is invoked BEFORE the IQueryable is actually enumerated...
public class CountQueryableResultsActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext filterContext)
{
var controllerStats = filterContext.ActionContext.ControllerContext.Controller as IControllerStatistics;
System.Web.HttpContext.Current.Response.AddHeader("Total-Result-Count", controllerStats.TotalResults.ToString());
}
}
IF, I called "IQueryable.ToArray()" at the end of the WebApi method, then the Linq query gets executed immediately, it generates statistics, and everything works - but that will prevent the user from being able to apply their own OData filters etc...
Ok - I figured it out.
The following will result in only a single Raven query being issued, which returns both the result, and the result-count.
Thanks to David Ruttka for his experiments in this area. I have adapted his code to work with with RavenDb. This code will return the results, and the result-count through one database query, as RavenDB intended.
I have appended my code below - to use this, you must return IRavenQueryable<T> from your WebApi method (not IQueryable<T>). Then, appending $inlinecount=allpages to your Uri will invoke the handler. This code will not break the other OData query extensions ($take, $skip etc)
Note: This code uses the 'inline' technique, in that the statistics are returned in the message body - you could change the code to inject the stats in the header if you liked - I just chose to go with the standard way that OData works.
You could adapt this code to include any and all of the statistics that Raven generates.
Use the following code to register the handler with ASP.NET (in your Global.asax.cs)
RegistrationCode:
GlobalConfiguration.Configuration.MessageHandlers.Add(new WebApi.Extensions.InlineRavenCountHandler());
Handler code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Reflection;
using System.Net.Http.Headers;
using System.Net;
namespace WebApi.Extensions
{
public class InlineRavenCountHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (!ShouldInlineCount(request))
return base.SendAsync(request, cancellationToken);
// Otherwise, we have a continuation to work our magic...
return base.SendAsync(request, cancellationToken).ContinueWith(
t =>
{
var response = t.Result;
// Is this a response we can work with?
if (!ResponseIsValid(response)) return response;
var pagedResultsValue = this.GetValueFromObjectContent(response.Content);
Type queriedType;
// Can we find the underlying type of the results?
if (pagedResultsValue is IQueryable)
{
queriedType = ((IQueryable)pagedResultsValue).ElementType;
// we need to work with an instance of IRavenQueryable to support statistics
var genericQueryableType = typeof(Raven.Client.Linq.IRavenQueryable<>).MakeGenericType(queriedType);
if (genericQueryableType.IsInstanceOfType(pagedResultsValue))
{
Raven.Client.Linq.RavenQueryStatistics stats = null;
// register our statistics object with the Raven query provider.
// After the query executes, this object will contain the appropriate stats data
dynamic dynamicResults = pagedResultsValue;
dynamicResults.Statistics(out stats);
// Create the return object.
var resultsValueMethod =
this.GetType().GetMethod(
"CreateResultValue", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(
new[] { queriedType });
// Create the result value with dynamic type
var resultValue = resultsValueMethod.Invoke(
this, new[] { stats, pagedResultsValue });
// Push the new content and return the response
response.Content = CreateObjectContent(
resultValue, response.Content.Headers.ContentType);
return response;
}
else
return response;
}
else
return response;
});
}
private bool ResponseIsValid(HttpResponseMessage response)
{
// Only do work if the response is OK
if (response == null || response.StatusCode != HttpStatusCode.OK) return false;
// Only do work if we are an ObjectContent
return response.Content is ObjectContent;
}
private bool ShouldInlineCount(HttpRequestMessage request)
{
var queryParams = request.RequestUri.ParseQueryString();
var inlinecount = queryParams["$inlinecount"];
return string.Compare(inlinecount, "allpages", true) == 0;
}
// Dynamically invoked for the T returned by the resulting ApiController
private ResultValue<T> CreateResultValue<T>(Raven.Client.Linq.RavenQueryStatistics stats, IQueryable<T> pagedResults)
{
var genericType = typeof(ResultValue<>);
var constructedType = genericType.MakeGenericType(new[] { typeof(T) });
var ctor = constructedType
.GetConstructors().First();
var instance = ctor.Invoke(null);
var resultsProperty = constructedType.GetProperty("Results");
resultsProperty.SetValue(instance, pagedResults.ToArray(), null);
var countProperty = constructedType.GetProperty("Count");
countProperty.SetValue(instance, stats.TotalResults, null);
return instance as ResultValue<T>;
}
// We need this because ObjectContent's Value property is internal
private object GetValueFromObjectContent(HttpContent content)
{
if (!(content is ObjectContent)) return null;
var valueProperty = typeof(ObjectContent).GetProperty("Value", BindingFlags.Instance | BindingFlags.NonPublic);
if (valueProperty == null) return null;
return valueProperty.GetValue(content, null);
}
// We need this because ObjectContent's constructors are internal
private ObjectContent CreateObjectContent(object value, MediaTypeHeaderValue mthv)
{
if (value == null) return null;
var ctor = typeof(ObjectContent).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(
ci =>
{
var parameters = ci.GetParameters();
if (parameters.Length != 3) return false;
if (parameters[0].ParameterType != typeof(Type)) return false;
if (parameters[1].ParameterType != typeof(object)) return false;
if (parameters[2].ParameterType != typeof(MediaTypeHeaderValue)) return false;
return true;
});
if (ctor == null) return null;
return ctor.Invoke(new[] { value.GetType(), value, mthv }) as ObjectContent;
}
}
public class ResultValue<T>
{
public int Count { get; set; }
public T[] Results { get; set; }
}
}
You can wrap the IQueryable and intercept the GetEnumerator. A sample of this is for example here:
http://blogs.msdn.com/b/alexj/archive/2010/03/01/tip-55-how-to-extend-an-iqueryable-by-wrapping-it.aspx. It does something a bit different but it should give you the idea.
Also - the caller can use $inlinecount=allpages in the URL to do this using the OData protocol. Although I'm not sure if WebAPI supports this query option yet.