Implementing an asynchronous call to a long running process MVC4 - asp.net-mvc-4

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.

Related

how i know blazor OnInitializedAsync exec in once or twice

I want get data from db once on OnInitializedAsync. I try to use tableLoading to judue,but it's not work.
protected override async Task OnInitializedAsync()
{
if (tableLoading)
{
return;
}
tableLoading = true;
users = await userService.GetSome(1, userType);
_total = await userService.GetCount(userType);
tableLoading = false;
Console.WriteLine("OnInitializedAsync");
}
This is the official way to solve your problem. You have to persist component state during first load so that your services won't be called second time during second load.
First add <persist-component-state /> tag helper inside your apps body:
<body>
...
<persist-component-state />
</body>
Then inject PersistentComponentState in your component and use like this:
#implements IDisposable
#inject PersistentComponentState ApplicationState
#code {
private IEnumerable<User> _users;
private int _total;
private PersistingComponentStateSubscription _persistingSubscription;
protected override async Task OnInitializedAsync()
{
_persistingSubscription =
ApplicationState.RegisterOnPersisting(PersistState);
if (!ApplicationState.TryTakeFromJson<IEnumerable<User>>("users", out var restoredUsers))
{
_users = await userService.GetSome(1, userType);
}
else
{
_users = restoredUsers;
}
if (!ApplicationState.TryTakeFromJson<int>("total", out var restoredTotal))
{
_total = await userService.GetCount(userType);
}
else
{
_total = restoredTotal;
}
}
private Task PersistState()
{
ApplicationState.PersistAsJson("users", _users);
ApplicationState.PersistAsJson("total", _total);
return Task.CompletedTask;
}
void IDisposable.Dispose()
{
_persistingSubscription.Dispose();
}
}
How i know blazor OnInitializedAsync exec in once or twice?
It usually loads twice.
Once when the component is initially rendered statically as part of the page.
A second time when the browser renders the component.
However, If you want to load it once, in that case, you could go to _Host.cshtml and change render-mode="ServerPrerendered" to render-mode="Server", and it would be called only once as a result it would then load your data from the database once only.
Note: For more information you could refer to the official documents here.
I know it's usually loads twice, i want to know when the function is run, how to konw it's run on once or twice. This is my solution.
static bool first = true;
protected override async Task OnInitializedAsync()
{
if (first)
{
first = false;
Console.WriteLine("first time");
return;
}
Console.WriteLine("second time");
}

signalr avoid login repeat on load client side

I am using signalR pushnotification service.
I have created a partial view. Inside partial view. Here is my client side code:
<script type="text/javascript">
var objHub;
$(function () {
objHub = $.connection.AnilHub;
loadClientMethods(objHub);
$.connection.hub.start()
.done(function () { objHub.server.connect();
console.log('Now connected, connection ID=' + $.connection.hub.id); }
// at the same time i want to insert into database to set user is online.
objHub.server.login('user1');
)
.fail(function () { console.log('Could not Connect!'); });
function loadClientMethods(objHub) {
objHub.client.getMessages = function (message) {
$('#divMessage').append('<div><p>' + message + '</p></div>');
var height = $('#divMessage')[0].scrollHeight;
$('#divMessage').scrollTop(height);
}
}
</script>
Hub Code
[HubName("MyHub")]
public class MainHub : Hub
{
public void Connect()
{
try
{
string userGroup = "test";
var id = Context.ConnectionId;
Groups.Add(id, userGroup);
Clients.Caller.onConnected(id, userGroup);
}
catch
{
Clients.Caller.NoExistAdmin();
}
}
public void NotifyAllClients(string Message)
{
Clients.Group("test").getMessages(Message);
}
public override Task OnConnected()
{
// Set status online on database
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled = false)
{
// set status disconnct in database
return base.OnDisconnected(stopCalled);
}
}
}
Now I just want to avoid re-loading check of login. because everytime I refresh the page it will call the connect method and call the hub method. How to avoid the re-connect issue. How Do I persist the things, even hub is not handle sessions.
Please suggest...
inside your html, in first load, create a random id and store it in cookies.
In your hub code, create an arraylist and store these random ids with corresponding connection ID.
In your html, try to read the random id from the cookies during each page refresh, if it is not found, it's a new connection, if it is found, use the old random id with a new connection ID to connect to your hub. Then in your hub arraylist, for this particular random id, replace the old connection ID with the new connection ID.

HttpContext.Current null when making a function Asynchronous in MVC4

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

Structuring tests (or property) for this reactive ui scenario

I'm not sure the correct way to structure this test. I've got a view model here:
public class ViewModel
{
public ReactiveCommand PerformSearchCommand { get; private set; }
private readonly ObservableAsPropertyHelper<bool> _IsBusy;
public bool IsBusy
{
get { return _IsBusy.Value; }
}
public ViewModel(IAdventureWorksRepository _awRepository)
{
PerformSearchCommand = new ReactiveCommand();
PerformSearchCommand.RegisterAsyncFunction((x) =>
{
return _awRepository.vIndividualCustomers.Take(1000).ToList();
}).Subscribe(rval =>
{
CustomerList = rval;
SelectedCustomer = CustomerList.FirstOrDefault();
});
PerformSearchCommand.IsExecuting.ToProperty(this, x => x.IsBusy, out _IsBusy);
PerformSearchCommand.Execute(null); // begin executing immediately
}
}
The dependency is a data access object to AdventureWorks
public interface IAdventureWorksRepository
{
IQueryable<vIndividualCustomer> vIndividualCustomers { get; }
}
Finally, my test looks something like this:
[TestMethod]
public void TestTiming()
{
new TestScheduler().With(sched =>
{
var repoMock = new Mock<IAdventureWorksRepository>();
repoMock.Setup(x => x.vIndividualCustomers).Returns(() =>
{
return new vIndividualCustomer[] {
new vIndividualCustomer { FirstName = "John", LastName = "Doe" }
};
});
var vm = new ViewModel(repoMock.Object);
Assert.AreEqual(true, vm.IsBusy); //fails?
Assert.AreEqual(1, vm.CustomerList.Count); //also fails, so it's not like the whole thing ran already
sched.AdvanceTo(2);
Assert.AreEqual(1, vm.CustomerList.Count); // success
// now the customer list is set at tick 2 (not at 1?)
// IsBusy was NEVER true.
});
}
So the viewmodel should immediately begin searching upon load
My immediate problem is that the IsBusy property doesn't seem to get set in the testing scheduler, even though it seems to work fine when I run the code normally. Am I using the ToProperty method correctly in the view model?
More generally, what is the proper way to do the full 'time travel' testing when my object under test has a dependency like this? The issue is that unlike most testing examples I'm seeing, the called interface is not an IObservable. It's just a synchronous query, used asynchronously in my view model. Of course in the view model test, I can mock the query to do whatever rx things I want. How would I set this up if I wanted the query to last 200 ticks, for example?
So, you've got a few things in your code that is stopping you from getting things to work correctly:
Don't invoke commands in ViewModel Constructors
First, calling Execute in the constructor means you'll never see the state change. The best pattern is to write that command but not execute it in the VM immediately, then in the View:
this.WhenAnyValue(x => x.ViewModel)
.InvokeCommand(this, x => x.ViewModel.PerformSearchCommand);
Move the clock after async actions
Ok, now that we can properly test the before and after state, we have to realize that after every time we do something that normally would be async, we have to advance the scheduler if we use TestScheduler. This means, that when we invoke the command, we should immediately advance the clock:
Assert.IsTrue(vm.PerformSearchCommand.CanExecute(null));
vm.PerformSearchCommand.Execute(null);
sched.AdvanceByMs(10);
Can't test Time Travel without IObservable
However, the trick is, your mock executes code immediately, there's no delay, so you'll never see it be busy. It just returns a canned value. Unfortunately, injecting the Repository makes this difficult to test if you want to see IsBusy toggle.
So, let's rig the constructor a little bit:
public ViewModel(IAdventureWorksRepository _awRepository, Func<IObservable<List<Customer>>> searchCommand = null)
{
PerformSearchCommand = new ReactiveCommand();
searchCommand = searchCommand ?? () => Observable.Start(() => {
return _awRepository.vIndividualCustomers.Take(1000).ToList();
}, RxApp.TaskPoolScheduler);
PerformSearchCommand.RegisterAsync(searchCommand)
.Subscribe(rval => {
CustomerList = rval;
SelectedCustomer = CustomerList.FirstOrDefault();
});
PerformSearchCommand.IsExecuting
.ToProperty(this, x => x.IsBusy, out _IsBusy);
}
Set up the test now
Now, we can set up the test, to replace PerformSearchCommand's action with something that has a delay on it:
new TestScheduler().With(sched =>
{
var repoMock = new Mock<IAdventureWorksRepository>();
var vm = new ViewModel(repoMock.Object, () =>
Observable.Return(new[] { new vIndividualCustomer(), })
.Delay(TimeSpan.FromSeconds(1.0), sched));
Assert.AreEqual(false, vm.IsBusy);
Assert.AreEqual(0, vm.CustomerList.Count);
vm.PerformSearchCommand.Execute(null);
sched.AdvanceByMs(10);
// We should be busy, we haven't finished yet - no customers
Assert.AreEqual(true, vm.IsBusy);
Assert.AreEqual(0, vm.CustomerList.Count);
// Skip ahead to after we've returned the customer
sched.AdvanceByMs(1000);
Assert.AreEqual(false, vm.IsBusy);
Assert.AreEqual(1, vm.CustomerList.Count);
});

MVC4 custom unobtrusive validator isn't working

not sure what is wrong. Syntax seems correct.... but it still doesn't fire on client side. If I submit the form, I get server side validation, client side nothing...
Here is the code that is on the page:
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>
<script type="text/javascript">
// we add a custom jquery validation method
(function ($) {
$.validator.addMethod('additive', function (value, element, params) {
//just return false to test it.
return false;
});
// and an unobtrusive adapter
$.validator.unobtrusive.adapters.add("additive", ["field2", "field3", "field4"], function (options) {
var params = {
field2: options.params.field2,
field3: options.params.field3,
field4: options.params.field4
};
options.rules['additive'] = params;
if (options.message) {
options.messages['additive'] = options.message;
}
});
}) (jQuery);
</script>
Here is the part that is on the validator that is related to client side (IClientValidatable):
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule
{
ValidationType = "additive",
ErrorMessage = "ERROR MESSAGE"
};
rule.ValidationParameters.Add("field2", propName2);
rule.ValidationParameters.Add("field3", propName3);
rule.ValidationParameters.Add("field4", propName4);
yield return rule;
}
The model is decorated as following:
[SumValidation("OtherField2...")]
public int MyField { get; set; }
When field renders, it is all there, all the stuff from the server side in terms of data-xxx attributes. Just this specific client validation does not fire. Anyone see what I'm missing?
figured it out. If anyone runs into this. Added custom validation too late on the page. After I moved my custom validation javascript to the head section of the _Layout.cshtml it started to work.
So if your script looks right, good place to check.
Another work around is to run $.validator.unobtrusive.parse('form'); which reloads all the validators.