So I've been following along with Steven Sanderson's TechDays Talk on SignalR and ASP.NET Async. I've run into a problem with the following actions
public class SomeController : AsyncController
{
static TaskCompletionSource<String> _nextMessage
= new TaskCompletionSource<string>();
[HttpPost]
public ActionResult PostMessage(string message)
{
_nextMessage = new TaskCompletionSource<string>();
oldNextMessage.SetResult(message);
return Content("success");
}
public async void GetMessages()
{
Response.ContentType = "text/event-stream";
while (true)
{
var message = await _nextMessage.Task;
Response.Write("data: " + message + "\n\n");
Response.Flush();
}
}
}
GetMessages seems to be blocking when it is first called, but once it times out, it starts working as expected.
So I load up my javascript which posts and runs GetMessages. On the first call to GetMessages, it hangs (as expected) at
await _nextMessage.Task;
but it won't let me make a call to PostMessage from any where (including other instances of the browser) This is on my dev machine, so it may be a thread issue, but I thought the whole purpose of this was to allow threads to work on multiple things. And that's besides the point that after it times out the first time, it starts to work as expected, where I can post, and get responses immediately. But if I refresh the page, I get the same problem. Any ideas on how to fix this? I'm using Eventsource by the way, but the same problem occurs when I'm doing long polling e.g.
public async Task<string> GetNextMessage()
{
return await _nextMessage.Task;
}
with a javascript loop function calling GetNextMessage()
Here's what I wound up using
[SessionState(SessionStateBehavior.Disabled)]
public class SomeController : AsyncController
{
static TaskCompletionSource<String> _nextMessage =
new TaskCompletionSource<string>();
public void PostMessage(string message)
{
var CreationJObj = FormUtils.AsciiToJObject(Request.Form[0]);
var CreationObj = FormUtils.FromJObject<MessageDisplayVO>(CreationJObj);
Parallel.Invoke(() => { _nextMessage.SetResult(CreationObj.Message); });
_nextMessage = new TaskCompletionSource<string>();
}
public async void GetMessages()
{
Response.ContentType = "text/event-stream";
var oldMessage = _nextMessage;
var message = await oldMessage.Task;
Response.Write("data: " + message + "\n\n");
Response.Flush();
}
}
Related
Im trying to perform a synchronization task without blocking UI thread. I have implemented a Android Service to do so, but I found out, if the synchronization task needs too much computational time, the UI thread was blocked. So I tried the migration to IntentService. This is how my IntentService looks like:
[Service]
public class SynchronizeIntentService : IntentService
{
static readonly string TAG = typeof(SynchronizeIntentService).FullName;
private NotificationCompat.Builder Builder;
private NotificationManagerCompat NotificationManager;
public SynchronizeIntentService() : base("SynchronizeIntentService")
{
}
public override void OnDestroy()
{
var tmp = 5;
base.OnDestroy();
}
private NotificationChannel createNotificationChannel()
{
var channelId = Constants.NOTIFICATION_CHANNELID;
var channelName = "My Notification Service";
var Channel = new NotificationChannel(channelId, channelName, Android.App.NotificationImportance.Default);
Channel.LightColor = Android.Resource.Color.HoloBlueBright;
Channel.LockscreenVisibility = NotificationVisibility.Public;
return Channel;
}
private void createForegroundService()
{
var mNotificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
mNotificationManager.CreateNotificationChannel(createNotificationChannel());
}
var notificationBuilder = new NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNELID);
GenerateNotification();
StartForeground(Constants.SERVICE_RUNNING_NOTIFICATION_ID, Builder.Notification);
}
private void GenerateNotification()
{
NotificationManager = NotificationManagerCompat.From(this);
Builder = new NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNELID);
Builder.SetContentTitle(ContaScan.Classes.Localize.GetString("Global_SynchProcess", ""))
.SetSmallIcon(Resource.Drawable.icon)
.SetPriority(NotificationCompat.PriorityLow);
}
protected async override void OnHandleIntent(Intent intent)
{
Log.Debug(TAG, "Service Started!");
await Synch();
Log.Debug(TAG, "Service Stopping!");
StopForeground(true);
this.StopSelf();
}
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
base.OnStartCommand(intent, flags, startId);
createForegroundService();
return StartCommandResult.Sticky;
}
private async Task Synch()
{
//Large synch process
}
}
And this is how the service is getting started:
startServiceIntent = new Intent(Android.App.Application.Context, typeof(SynchronizeIntentService));
startServiceIntent.SetAction(Constants.ACTION_START_SERVICE);
ContextWrapper contextWrapper = new ContextWrapper(Android.App.Application.Context);
contextWrapper.StartService(startServiceIntent);
The problem is OnDestroy() method is called while the Synch() task is being performed and looks like the IntentService is being killed before ending the process.
What am I doing wrong?
First, check your API level. This class was deprecated in API level 30.
And then, when you use the OnHandleIntent, do not call Service.stopSelf().
This method is invoked on the worker thread with a request to process. Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call Service.stopSelf().
For more details, please check the link below. https://developer.android.com/reference/android/app/IntentService#onHandleIntent(android.content.Intent)
I built a simple Console Application to test the connection to an API. Calling the connection method from Console App Main works fine. I get a response with an access-token.
I though that I just could implement the same method/code to an MVC-project and add the method within the HomeController, then call the method from any ActionResult, getting the access-token and then put it in a ViewBag to display it in a view (just for testing). But it doesn't work in the MVC-project.
If I run the debugger, it seems like the app hangs when SendAsync is executed in the method. The console gives this output:
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-04-08T09:26:32.4945663Z","tags":{"ai.internal.sdkVersion":"rddf:2.2.0-738","ai.internal.nodeName":"XXXXXX","ai.cloud.roleInstance":"XXXXXXXX"},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"/token","id":"XXXXXXXXX=","data":"https://api.vasttrafik.se/token","duration":"00:00:00.2810000","resultCode":"200","success":true,"type":"Http","target":"api.vasttrafik.se","properties":{"DeveloperMode":"true"}}}}
The thread 0x1f68 has exited with code 0 (0x0).
What can I do to make the API-call / response work in the MVC-application?
My knowledge in the area is ridiculously low. But I really want to understand whats going on here.
Thanks!
Best
J
MVC project
public class HomeController : Controller
{
public ActionResult Index()
{
string token = PostRequest().Result;
ViewBag.Token = token;
return View();
}
async static Task<string> PostRequest()
{
var client = new HttpClient();
client.BaseAddress = new Uri("https://api.vasttrafik.se");
var request = new HttpRequestMessage(HttpMethod.Post, "/token");
// Key // Secret
string credentials = "xxxxxxxxxoVS5xDrcO6qZsAp0a" + ":" + "xxxxxxxxhn0STj1w4asDwixdMa";
var plainTextBytes = Encoding.UTF8.GetBytes(credentials);
//Key and secret encoded
string encodedCrentedials = Convert.ToBase64String(plainTextBytes);
//Console.WriteLine(encodedCrentedials);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", encodedCrentedials);
var formData = new List<KeyValuePair<string, string>>();
formData.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
formData.Add(new KeyValuePair<string, string>("scope", "xxxxxxxxw0oVS5xDrcO6qZsAp0a"));
request.Content = new FormUrlEncodedContent(formData);
// This is where the app hangs....
var response = await client.SendAsync(request);
var mycontentres = await response.Content.ReadAsByteArrayAsync();
var responseBody = Encoding.Default.GetString(mycontentres);
//Console.WriteLine(responseBody);
JavaScriptSerializer seri = new JavaScriptSerializer();
dynamic data = JObject.Parse(responseBody);
string tok = data.access_token;
return tok;
}
}
Don't block on async code:
public async Task<ActionResult> Index()
{
string token = await PostRequest();
ViewBag.Token = token;
return View();
}
I have an external dll written in C# and I studied from the assemblies documentation that it writes its debug messages to the Console using Console.WriteLine.
this DLL writes to console during my interaction with the UI of the Application, so i don't make DLL calls directly, but i would capture all console output , so i think i got to intialize in form load , then get that captured text later.
I would like to redirect all the output to a string variable.
I tried Console.SetOut, but its use to redirect to string is not easy.
As it seems like you want to catch the Console output in realtime, I figured out that you might create your own TextWriter implementation that fires an event whenever a Write or WriteLine happens on the Console.
The writer looks like this:
public class ConsoleWriterEventArgs : EventArgs
{
public string Value { get; private set; }
public ConsoleWriterEventArgs(string value)
{
Value = value;
}
}
public class ConsoleWriter : TextWriter
{
public override Encoding Encoding { get { return Encoding.UTF8; } }
public override void Write(string value)
{
if (WriteEvent != null) WriteEvent(this, new ConsoleWriterEventArgs(value));
base.Write(value);
}
public override void WriteLine(string value)
{
if (WriteLineEvent != null) WriteLineEvent(this, new ConsoleWriterEventArgs(value));
base.WriteLine(value);
}
public event EventHandler<ConsoleWriterEventArgs> WriteEvent;
public event EventHandler<ConsoleWriterEventArgs> WriteLineEvent;
}
If it's a WinForm app, you can setup the writer and consume its events in the Program.cs like this:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (var consoleWriter = new ConsoleWriter())
{
consoleWriter.WriteEvent += consoleWriter_WriteEvent;
consoleWriter.WriteLineEvent += consoleWriter_WriteLineEvent;
Console.SetOut(consoleWriter);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
static void consoleWriter_WriteLineEvent(object sender, Program.ConsoleWriterEventArgs e)
{
MessageBox.Show(e.Value, "WriteLine");
}
static void consoleWriter_WriteEvent(object sender, Program.ConsoleWriterEventArgs e)
{
MessageBox.Show(e.Value, "Write");
}
It basically amounts to the following:
var originalConsoleOut = Console.Out; // preserve the original stream
using(var writer = new StringWriter())
{
Console.SetOut(writer);
Console.WriteLine("some stuff"); // or make your DLL calls :)
writer.Flush(); // when you're done, make sure everything is written out
var myString = writer.GetStringBuilder().ToString();
}
Console.SetOut(originalConsoleOut); // restore Console.Out
So in your case you'd set this up before making calls to your third-party DLL.
You can also call SetOut with Console.OpenStandardOutput, this will restore the original output stream:
Console.SetOut(new StreamWriter(Console.OpenStandardOutput()));
Or you can wrap it up in a helper method that takes some code as an argument run it and returns the string that was printed. Notice how we gracefully handle exceptions.
public string RunCodeReturnConsoleOut(Action code)
{
string result;
var originalConsoleOut = Console.Out;
try
{
using (var writer = new StringWriter())
{
Console.SetOut(writer);
code();
writer.Flush();
result = writer.GetStringBuilder().ToString();
}
return result;
}
finally
{
Console.SetOut(originalConsoleOut);
}
}
Using solutions proposed by #Adam Lear and #Carlo V. Dango I created a helper class:
public sealed class RedirectConsole : IDisposable
{
private readonly Action<string> logFunction;
private readonly TextWriter oldOut = Console.Out;
private readonly StringWriter sw = new StringWriter();
public RedirectConsole(Action<string> logFunction)
{
this.logFunction = logFunction;
Console.SetOut(sw);
}
public void Dispose()
{
Console.SetOut(oldOut);
sw.Flush();
logFunction(sw.ToString());
sw.Dispose();
}
}
which can be used in the following way:
public static void MyWrite(string str)
{
// print console output to Log/Socket/File
}
public static void Main()
{
using(var r = new RedirectConsole(MyWrite)) {
Console.WriteLine("Message 1");
Console.WriteLine("Message 2");
}
// After the using section is finished,
// MyWrite will be called once with a string containing all messages,
// which has been written during the using section,
// separated by new line characters
}
using asp.net mvc 4,
I created a class with a static method like this
public class StaticClass
{
public static int val { get; set; }
public static string ReturnValueBasedOnInput(int n)
{
string res;
switch (n)
{
case 101:
Thread.Sleep(30000);
res = "Long lasting response: 101" + val;
break;
default:
res = n.ToString() + " was provided..." + val;
break;
}
return res;
}
}
it is called from my controller :
public ActionResult Index(int id = 1)
{
ViewBag.returnValue = StaticClass.ReturnValueBasedOnInput(id);
return View(id);
}
I expected that when i call the method with a parameter value of 101 the application should be blocked for 30 secs, but it remains responsive. I thought since this is a static method it should be blocked for 30 second for all incoming method calls. can someone explain what happens here?
The thread handling the request to the Index action with id=101 of your controller should be blocked. Threads handling other requests of other sessions will not be blocked. Even other requests for the same session may not be blocked depending on ReadOnly session attribute of corresponding controllers [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)].
I have almost exactly the same scenario described by Nathon Taylor in ASP.NET MVC - Sharing Session State Between Controllers. The problem is that if I save the path to the images inside a Session variable List<string> it is not being defined back in the ItemController so all the paths are being lost... Here's my setup:
Inside ImageController I have the Upload() action method:
public ActionResult Upload()
{
var newFile = System.Web.HttpContext.Current.Request.Files["Filedata"];
string guid = Guid.NewGuid() + newFile.FileName;
string itemImagesFolder = Server.MapPath(Url.Content("~/Content/ItemImages/"));
string fileName = itemImagesFolder + "originals/" + guid;
newFile.SaveAs(fileName);
var resizePath = itemImagesFolder + "temp/";
string finalPath;
foreach (var dim in _dimensions)
{
var resizedPath = _imageService.ResizeImage(fileName, resizePath, dim.Width + (dim.Width * 10/100), guid);
var bytes = _imageService.CropImage(resizedPath, dim.Width, dim.Height, 0, 0);
finalPath = itemImagesFolder + dim.Title + "/" + guid;
_imageService.SaveImage(bytes, finalPath);
}
AddToSession(guid);
var returnPath = Url.Content("~/Content/ItemImages/150x150/" + guid);
return Content(returnPath);
}
private void AddToSession(string fileName)
{
if(Session[SessionKeys.Images] == null)
{
var imageList = new List<string>();
Session[SessionKeys.Images] = imageList;
}
((List<string>)Session[SessionKeys.Images]).Add(fileName);
}
Then inside my ItemController I have the New() action method which has the following code:
List<string> imageNames;
var images = new List<Image>();
if (Session[SessionKeys.Images] != null) //always returns false
{
imageNames = Session[SessionKeys.Images] as List<string>;
int rank = 1;
foreach (var name in imageNames)
{
var img = new Image {Name = name, Rank = rank};
images.Add(img);
rank++;
}
}
Ok so why is this happening and how do I solve it?
Also, I was thinking of whether I could move the ActionMethod that takes care of the upload of the images into the ItemController and store the image paths inside a List property on the ItemController itself, would that actually work? Note though, that images are being uploaded and taken care of via an AJAX request. Then when the user submits the item entry form, all the data about the Item along with the images should be saved to the database...
Update:
I've updated the code. Also I think I should add that I'm using StructureMap as my controller factorory. Could it be a scoping issue? What is the default scope that is usually used by StructureMap?
public class StructureMapDependencyResolver : IDependencyResolver
{
public StructureMapDependencyResolver(IContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
if (serviceType.IsAbstract || serviceType.IsInterface)
{
return _container.TryGetInstance(serviceType);
}
else
{
return _container.GetInstance(serviceType);
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances<object>()
.Where(s => s.GetType() == serviceType);
}
private readonly IContainer _container;
}
And inside my Global.asax file:
private static IContainer ConfigureStructureMap()
{
ObjectFactory.Configure(x =>
{
x.For<IDatabaseFactory>().Use<EfDatabaseFactory>();
x.For<IUnitOfWork>().Use<UnitOfWork>();
x.For<IGenericMethodsRepository>().Use<GenericMethodsRepository>();
x.For<IUserService>().Use<UsersManager>();
x.For<IBiddingService>().Use<BiddingService>();
x.For<ISearchService>().Use<SearchService>();
x.For<IFaqService>().Use<FaqService>();
x.For<IItemsService>().Use<ItemsService>();
x.For<IMessagingService>().Use<MessagingService>();
x.For<IStaticQueriesService>().Use<StaticQueriesService>();
x.For < IImagesService<Image>>().Use<ImagesService>();
x.For<ICommentingService>().Use<CommentingService>();
x.For<ICategoryService>().Use<CategoryService>();
x.For<IHelper>().Use<Helper>();
x.For<HttpContext>().HttpContextScoped().Use(HttpContext.Current);
x.For(typeof(Validator<>)).Use(typeof(NullValidator<>));
x.For<Validator<Rating>>().Use<RatingValidator>();
x.For<Validator<TopLevelCategory>>().Use<TopLevelCategoryValidator>();
});
Func<Type, IValidator> validatorFactory = type =>
{
var valType = typeof(Validator<>).MakeGenericType(type);
return (IValidator)ObjectFactory.GetInstance(valType);
};
ObjectFactory.Configure(x => x.For<IValidationProvider>().Use(() => new ValidationProvider(validatorFactory)));
return ObjectFactory.Container;
}
Any thoughts?
I just added this to Global.asax.cs
protected void Session_Start()
{
}
It seems that this fixed the issue. I set a breakpoint that gets hit only once per session (as expected).
One possible reason for this is that the application domain restarts between the first and the second actions and because session is stored in memory it will be lost. This could happen if you recompile the application between the two. Try putting a breakpoints in the Application_Start and Session_Start callbacks in Global.asax and see if they are called twice.
Are you ever using it other than accessing HttpContext.Current directly in your code? In other words, are there any places where you're injecting the HttpContext for the sake of mocking in unit tests?
If you're only accessing it directly in your methods, then there's no reason to have the entry x.For<HttpContext>().HttpContextScoped().Use(HttpContext.Current); in you application startup. I wonder if it would start working if you removed it.