ASP.NET Web Api calling a non Task async method - c#

In my Web Api project, I have a [HttpPost] method - public HttpResponseMessage saveFiles() {}
which saves some audio files to the server.
after I save the files, I need to call a method in the Microsoft.Speech server api, this method is async but it returns void:
public void RecognizeAsync(RecognizeMode mode);
I would want to wait until this method is finished and just then return an answer to the client with all the information I gathered.
I can not use await here because this function returns void.
I implemented an event: public event RecognitionFinishedHandler RecognitionFinished;
This event is called when this function is finished.
-- EDIT
I am wrapping this event with a Task, but I guess I am doing something wrong because I can not get the RecognizeAsync function to actually do its job. it seems that the function is not working now, here is my code:
the functions containing the Speech recognition:
public delegate void RecognitionFinishedHandler(object sender);
public class SpeechActions
{
public event RecognitionFinishedHandler RecognitionFinished;
private SpeechRecognitionEngine sre;
public Dictionary<string, List<TimeSpan>> timeTags; // contains the times of each tag: "tag": [00:00, 00:23 .. ]
public SpeechActions()
{
sre = new SpeechRecognitionEngine(new System.Globalization.CultureInfo("en-US"));
sre.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(sre_SpeechRecognized);
sre.AudioStateChanged += new EventHandler<AudioStateChangedEventArgs>(sre_AudioStateChanged);
}
/// <summary>
/// Calculates the tags appearances in a voice over wav file.
/// </summary>
/// <param name="path">The path to the voice over wav file.</param>
public void CalcTagsAppearancesInVO(string path, string[] tags, TimeSpan voLength)
{
timeTags = new Dictionary<string, List<TimeSpan>>();
sre.SetInputToWaveFile(path);
foreach (string tag in tags)
{
GrammarBuilder gb = new GrammarBuilder(tag);
gb.Culture = new System.Globalization.CultureInfo("en-US");
Grammar g = new Grammar(gb);
sre.LoadGrammar(g);
}
sre.RecognizeAsync(RecognizeMode.Multiple);
}
void sre_AudioStateChanged(object sender, AudioStateChangedEventArgs e)
{
if (e.AudioState == AudioState.Stopped)
{
sre.RecognizeAsyncStop();
if (RecognitionFinished != null)
{
RecognitionFinished(this);
}
}
}
void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
string word = e.Result.Text;
TimeSpan time = e.Result.Audio.AudioPosition;
if(!timeTags.ContainsKey(word))
{
timeTags.Add(word, new List<TimeSpan>());
}
// add the found time
timeTags[word].Add(time);
}
}
and my function that calls it + the event hander:
[HttpPost]
public HttpResponseMessage saveFiles()
{
if (HttpContext.Current.Request.Files.AllKeys.Any())
{
string originalFolder = HttpContext.Current.Server.MapPath("~/files/original/");
string lowFolder = HttpContext.Current.Server.MapPath("~/files/low/");
string audioFolder = HttpContext.Current.Server.MapPath("~/files/audio/");
string voiceoverPath = Path.Combine(originalFolder, Path.GetFileName(HttpContext.Current.Request.Files["voiceover"].FileName));
string outputFile = HttpContext.Current.Server.MapPath("~/files/output/") + "result.mp4";
string voiceoverWavPath = Path.Combine(audioFolder, "voiceover.wav");
var voiceoverInfo = Resource.From(voiceoverWavPath).LoadMetadata().Streams.OfType<AudioStream>().ElementAt(0).Info;
DirectoryInfo di = new DirectoryInfo(originalFolder);
// speech recognition
// get tags from video filenames
string sTags = "";
di = new DirectoryInfo(HttpContext.Current.Server.MapPath("~/files/low/"));
foreach (var item in di.EnumerateFiles())
{
string filename = item.Name.Substring(0, item.Name.LastIndexOf("."));
if (item.Name.ToLower().Contains("thumbs") || filename == "voiceover")
{
continue;
}
sTags += filename + ",";
}
if (sTags.Length > 0) // remove last ','
{
sTags = sTags.Substring(0, sTags.Length - 1);
}
string[] tags = sTags.Split(new char[] { ',' });
// HERE STARTS THE PROBLEMATIC PART! ----------------------------------------------------
var task = GetSpeechActionsCalculated(voiceoverWavPath, tags, voiceoverInfo.Duration);
// now return the times to the client
var finalTimes = GetFinalTimes(HttpContext.Current.Server.MapPath("~/files/low/"), task.Result.timeTags);
var goodResponse = Request.CreateResponse(HttpStatusCode.OK, finalTimes);
return goodResponse;
}
return Request.CreateResponse(HttpStatusCode.OK, "no files");
}
private Task<SpeechActions> GetSpeechActionsCalculated(string voPath, string[] tags, TimeSpan voLength)
{
var tcs = new TaskCompletionSource<SpeechActions>();
SpeechActions sa = new SpeechActions();
sa.RecognitionFinished += (s) =>
{
tcs.TrySetResult((SpeechActions)s);
};
sa.CalcTagsAppearancesInVO(voPath, tags, voLength);
return tcs.Task;
}

You're almost there with your edit, you just have to await the task:
[HttpPost]
public async Task<HttpResponseMessage> saveFiles()
{
if (HttpContext.Current.Request.Files.AllKeys.Any())
{
...
string[] tags = sTags.Split(new char[] { ',' });
await GetSpeechActionsCalculated(voiceoverWavPath, tags, voiceoverInfo.Duration);
// now return the times to the client
var finalTimes = GetFinalTimes(HttpContext.Current.Server.MapPath("~/files/low/"), task.Result.timeTags);
var goodResponse = Request.CreateResponse(HttpStatusCode.OK, finalTimes);
return goodResponse;
}
return Request.CreateResponse(HttpStatusCode.OK, "no files");
}

Related

UWP Event on content changed

I want to observe on a folder. Like i want a Event When the content is changed.
I found this
var options = new Windows.Storage.Search.QueryOptions
{
FolderDepth = Windows.Storage.Search.FolderDepth.Deep
};
var query = Folder.CreateFileQueryWithOptions(options);
query.ContentsChanged += QueryContentsChanged;
var files = await query.GetFilesAsync();
private void QueryContentsChanged(IStorageQueryResultBase sender, object args)
{
//Code here
}
But the problem with this is I can't find which file caused the event and i even can't know what caused the Event (Like Modify , Create , Delete or Rename of file) How to get these details?
I used this code
public async void MonitorFolder()
{
var options = new Windows.Storage.Search.QueryOptions
{
FolderDepth = Windows.Storage.Search.FolderDepth.Deep
};
var query = Folder1.CreateFileQueryWithOptions(options);
query.ContentsChanged += QueryContentsChanged;
var files = await query.GetFilesAsync();
await addtoOld(Folder1, Old);
}
private async void addtoOld(StorageFolder folder1, List<FDate> old)
{
var files = await folder1.GetFilesAsync();
foreach (var file in files)
{
BasicProperties basicProperties = await file.GetBasicPropertiesAsync();
FDate f = new FDate
{
Path = file.Path,
Id = file.FolderRelativeId,
Modified = basicProperties.DateModified,
Change = ChangeType.NoChange,
FileType = Type.File
};
old.Add(f);
}
var folders = await folder1.GetFoldersAsync();
foreach (var folder in folders)
{
BasicProperties basicProperties = await folder.GetBasicPropertiesAsync();
FDate f = new FDate
{
Path = folder.Path,
Id = folder.FolderRelativeId,
Modified = basicProperties.DateModified,
Change = ChangeType.NoChange,
FileType = Type.Folder
};
old.Add(f);
addtoOld(folder, old);
}
return;
}
private async void QueryContentsChanged(IStorageQueryResultBase sender, object args)
{
New.Clear();
List<FDate> changed = new List<FDate>();
await addtoOld(Folder1, New);
foreach(var f in New)
{
var f1 = getFile(f);
if (f1 != null)
{
if (f1.Modified < f.Modified)
{
f1.Change = ChangeType.Modified;
changed.Add(f1);
}
Old.Remove(f1);
}
else
{
f.Change = ChangeType.Created;
changed.Add(f);
}
}
foreach (var f in Old)
{
f.Change = ChangeType.Deleted;
changed.Add(f);
}
Old = New;
foreach (var f in changed)
{
if(f.FileType== Type.File)
{
if (f.Change == ChangeType.Modified)
{
//code here
}
if(f.Change == ChangeType.Created)
{
//Created code here
}
if(f.Change == ChangeType.Deleted)
{
//Deleted code here
}
}
else
{
if (f.Change == ChangeType.Created)
{
//Created code here
}
if(f.Change == ChangeType.Deleted)
{
//Deleted code here
}
}
}
private FDate getFile(FDate f)
{
foreach(var fi in Old)
{
if (f.Name == fi.Name)
return fi;
}
return null;
}
This code is not working properly I looks like it is because the addtoOld is async The code can't be substituted because it is recursive. and the function can't be made sync it has many await how do i solve this?
Note:OLD and New are Lists ChangeType and Type are enums
According to the following blog post, there is unfortunately no way to identify the reason of the event and there is also no information about affected items.
File system change notifications in WinRT: http://lunarfrog.com/blog/filesystem-change-notifications
So I guess you will have to go through all files and check their Properties property to determine when each file was last modified, created etc.: https://learn.microsoft.com/en-us/uwp/api/windows.storage.storagefile

Database async query and processing

Let me rephrase.
I have a method that generates strings(paths) after given a start string(path)
IF those paths are for a directory I want to enqueue that in the input of the method.
After processing the path synchronously, I want to get the Data and clone it async into multiple paths of a pipeline, were each path needs to get the datablock. So the Broadcastblock is out of the question (it cant send a blocking signal to the blocks before itself),
The joinblock, joining the results is relatively straight forward.
So to sum up
Is there a Block in Dataflow block, where i can access the inputqueue from the delegate, if when, how?
Is there a construct that acts like the broadcastblock but can block the blocks that came before it?
I tried doing it via almighty google:
class subversion
{
private static string repo;
private static string user;
private static string pw;
private static DateTime start;
private static DateTime end;
private static List<parserObject> output;
public static List<parserObject> svnOutputList
{
get {return output; }
}
private static List<string> extension_whitelist;
public async void run(string link, string i_user, string i_pw, DateTime i_start, DateTime i_end)
{
repo = link;
user = i_user;
pw = i_pw;
start = i_start;
end = i_end;
output = new List<parserObject>();
BufferBlock<string> crawler_que = new BufferBlock<string>();
BufferBlock<svnFile> parser_que = new BufferBlock<svnFile>();
var svn = crawl(crawler_que, parser_que);
var broadcaster = new ActionBlock<svnFile>(async file =>
{//tried to addapt the code from this ensure always send broadcastblock -> see link below
List<Task> todo = new List<Task>();
todo.Add(mLoc);//error cannot convert methodgroup to task
foreach (var task in todo)//error: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement?
{
task.SendAsync(file);//error cannot convert task to targetblock
}
await Task.WhenAll(todo.ToArray());
});
parser_que.LinkTo(broadcaster);
await Task.WhenAll(broadcaster, svn);//error cannot convert actionblock to task
}
private static async Task crawl(BufferBlock<string> in_queue, BufferBlock<svnFile> out_queue)
{
SvnClient client = new SvnClient();
client.Authentication.ForceCredentials(user, pw);
SvnListArgs arg = new SvnListArgs
{
Depth = SvnDepth.Children,
RetrieveEntries = SvnDirEntryItems.AllFieldsV15
};
while (await in_queue.OutputAvailableAsync())
{
string buffer_author = null;
string prev_author = null;
System.Collections.ObjectModel.Collection<SvnListEventArgs> contents;
string link = await in_queue.ReceiveAsync();
if (client.GetList(new Uri(link), arg, out contents))
{
foreach (SvnListEventArgs item in contents)
{
if (item.Entry.NodeKind == SvnNodeKind.Directory)
{
in_queue.Post(item.Path);
}
else if (item.Entry.NodeKind == SvnNodeKind.File)
{
try
{
int length = item.Name.LastIndexOf(".");
if (length <= 0)
{
continue;
}
string ext = item.Name.Substring(length);
if (extension_whitelist.Contains(ext))
{
Uri target = new Uri((repo + link));
SvnRevisionRange range;
SvnBlameArgs args = new SvnBlameArgs
{
Start = start.AddDays(-1),
End = end
};
try
{
svnFile file_instance = new svnFile();
client.Blame(target, args, delegate(object sender3, SvnBlameEventArgs e)
{
if (e.Author != null)
{
buffer_author = e.Author;
prev_author = e.Author;
}
else
{
buffer_author = prev_author;
}
file_instance.lines.Add(new svnLine(buffer_author, e.Line));
});
out_queue.Post(file_instance);
}
catch (Exception a) { Console.WriteLine("exception:" + a.Message);}
}
}
catch (Exception a)
{
}
}
}
}
}
}
private static async Task mLoc(svnFile file)
{
List<parserPart> parts = new List<parserPart>();
int find;
foreach (svnLine line in file.lines)
{
if ((find = parts.FindIndex(x => x.uploader_id == line.author)) > 0)
{
parts[find].count += 1;
}
else
{
parts.Add(new parserPart(line.author));
}
find = 0;
}
parserObject ret = new parserObject(parts, "mLoc");
await output.Add(ret);
return;
}
}
broadcastblock answer: Alternate to Dataflow BroadcastBlock with guaranteed delivery

I have code in C# that looks like this that I need to extract the same fields, but I am uncertain how to do it

In python, I have code that looks like this using libxml
parser = etree.HTMLParser()
id = 0
nodes = node.findall(r'.//div[#id="flexBox_flex_calendar_mainCal"]//table/tr[#class]')
for x in nodes:
if x.attrib['class'].startswith('calendar'):
item = GetARow(x, id)
newsitems.addRow(item)
id = id + 1
for id in range(0, newsitems.getLength()):
rowDict = newsitems.getRow(id)
if rowDict is not None:
rowItems = QStringList([rowDict['Time'], rowDict['Currency'], rowDict['Impact'], rowDict['Event'], rowDict['Actual'], rowDict['Forecast'], rowDict['Previous']] )
#newsItems[rowDict['Time']].append(rowItems)
newsTable.addrow(rowItems)
I have code in C# that looks like this that I need to extract the same fields, but I am uncertain how to do it. The whatNodesToFind string is problematical.
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Windows.Forms;
using HtmlAgilityPack;
namespace ConsoleApplication276
{
// a container for a url and a parser Action
public class Link
{
public string link { get; set; }
public Action<string> parser { get; set; }
}
public class Program
{
static string[] monthstrings = new string[] { "", "jan", "feb", "mar", "apr", "may", "jun", "july", "aug", "sep", "oct", "nov", "dec" };
public static string GetDateInFOREXFactoryFormat()
{
var today = System.DateTime.Now;
var dayStr = today.Day.ToString();
var monthStr = monthstrings[today.Month];
var yearStr = today.Year.ToString();
return dayStr + monthStr + '.' + yearStr;
}
// Entry Point of the console app
public static void Main(string[] args)
{
try
{
// download each page and dump the content
// you can add more links here, associate each link with a parser action, as for what data should the parser generate create a property for that in the Link container
var task = MessageLoopWorker.Run(DoWorkAsync, new Link()
{
link = "http://www.forexfactory.com/calendar.php?day=" + GetDateInFOREXFactoryFormat(),
parser = (string html) =>
{
//do what ever you need with hap here
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);
string whatNodesToFind = ".//div";
//string whatNodesToFind = "table";
//var someNodes = doc.DocumentNode.SelectSingleNode(whatNodesToFind);
var someNodes = doc.DocumentNode.SelectNodes(whatNodesToFind);
foreach (var node in someNodes)
{
Console.WriteLine(node);
}
}
});
task.Wait();
Console.WriteLine("DoWorkAsync completed.");
}
catch (Exception ex)
{
Console.WriteLine("DoWorkAsync failed: " + ex.Message);
}
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
// navigate WebBrowser to the list of urls in a loop
public static async Task<Link> DoWorkAsync(Link[] args)
{
Console.WriteLine("Start working.");
using (var wb = new WebBrowser())
{
wb.ScriptErrorsSuppressed = true;
TaskCompletionSource<bool> tcs = null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
tcs.TrySetResult(true);
// navigate to each URL in the list
foreach (var arg in args)
{
tcs = new TaskCompletionSource<bool>();
wb.DocumentCompleted += documentCompletedHandler;
try
{
wb.Navigate(arg.link.ToString());
// await for DocumentCompleted
await tcs.Task;
// after the page loads pass the html to the parser
arg.parser(wb.DocumentText);
}
finally
{
wb.DocumentCompleted -= documentCompletedHandler;
}
// the DOM is ready
Console.WriteLine(arg.link.ToString());
Console.WriteLine(wb.Document.Body.OuterHtml);
}
}
Console.WriteLine("End working.");
return null;
}
}
// a helper class to start the message loop and execute an asynchronous task
public static class MessageLoopWorker
{
public static async Task<Object> Run(Func<Link[], Task<Link>> worker, params Link[] args)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
EventHandler idleHandler = null;
idleHandler = async (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return to the message loop
await Task.Yield();
// and continue asynchronously
// propogate the result or exception
try
{
var result = await worker(args);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
// signal to exit the message loop
// Application.Run will exit at this point
Application.ExitThread();
};
// handle Application.Idle just once
// to make sure we're inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
// set STA model for the new thread
thread.SetApartmentState(ApartmentState.STA);
// start the thread and await for the task
thread.Start();
try
{
return await tcs.Task;
}
finally
{
thread.Join();
}
}
}
}
I tried this but it doesn't work, meaning it returns no nodes. Yet, I can see those nodes using Google Chrome inspect element:
var findclasses = doc.DocumentNode.Descendants("div").Where(d =>
d.Attributes.Contains("class") && d.Attributes["id"].Value.Contains("flex"));
foreach (var d in findclasses)
{
Console.WriteLine(d);
}
Regarding the Edit 1 section, I'd recommend to use d.GetAttributeValue("id", "") to replace d.Attributes["id"].Value, because the latter will throw exception in case current d element doesn't have the attribute id (and it did happen when parsing HTML page retrieved from the URL in this sample) :
var link = "http://www.forexfactory.com/calendar.php?day=aug7.2015";
var doc = new HtmlWeb().Load(link);
var findclasses = doc.DocumentNode
.Descendants("div")
.Where(d => d.Attributes.Contains("class")
&&
d.GetAttributeValue("id", "").Contains("flex")
);
foreach (var d in findclasses)
{
Console.WriteLine("{0}, {1}", d.Name, d.GetAttributeValue("id", ""));
}
Dotnetfiddle Demo
output :
div, flexBox_flex_minicalendar_
div, flexBox_flex_calendar_mainCal
div, flexDatePicker_Calendar_mainCal_begindate
div, flexDatePicker_Calendar_mainCal_enddate
The answer is really subtle. It turns out there is a section missing from the html! Instantiating a "correct" Webclient with the right headers works:
using (WebClient wb = new WebClient())
{
wb.Headers["User-Agent"] =
"User-Agent" + "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.3 Gecko/2008092417 Firefox/3.0.3";

Why do some files result in 0KB when using WebClient.DownloadFileTaskAsync

I'm trying to download multiple files from an FTP server using WebClient.DownloadFileTaskAsync and repeatedly have the issue that several files end up being 0KB.
I've tried different suggested solutions but I just don't manage to get all files. What am I doing wrong?
class Program
{
static void Main()
{
Setup(); // This only sets up working folders etc
Task t = ProcessAsync();
t.ContinueWith(bl =>
{
if (bl.Status == TaskStatus.RanToCompletion)
Logger.Info("All done.");
else
Logger.Warn("Something went wrong.");
});
t.Wait();
}
private static void Setup() {...}
static async Task<bool> ProcessAsync()
{
var c = new Catalog();
var maxItems = Settings.GetInt("maxItems");
Logger.Info((maxItems == 0 ? "Processing all items" : "Processing first {0} items"), maxItems);
await c.ProcessCatalogAsync(maxItems);
return true; // This is not really used atm
}
}
public class Catalog
{
public async Task ProcessCatalogAsync(int maxItems)
{
var client = new Client();
var d = await client.GetFoldersAsync(_remoteFolder, maxItems);
var list = d as IList<string> ?? d.ToList();
Logger.Info("Found {0} folders", list.Count());
await ProcessFoldersAsync(list);
}
private async Task ProcessFoldersAsync(IEnumerable<string> list)
{
var client = new Client();
foreach (var mFolder in list.Select(folder => _folder + "/" + folder))
{
var items = await client.GetItemsAsync(mFolder);
var file = items.FirstOrDefault(n => n.ToLower().EndsWith(".xml"));
if (string.IsNullOrEmpty(file))
{
Logger.Warn("No metadata file found in {0}", mFolder);
continue;
}
await client.DownloadFileAsync(mFolder, file);
// Continue processing the received file...
}
}
}
public class Client
{
public async Task<IEnumerable<string>> GetItemsAsync(string subfolder)
{
return await GetFolderItemsAsync(subfolder, false);
}
public async Task<IEnumerable<string>> GetFoldersAsync(string subfolder, int maxItems)
{
var folders = await GetFolderItemsAsync(subfolder, true);
return maxItems == 0 ? folders : folders.Take(maxItems);
}
private async Task<IEnumerable<string>> GetFolderItemsAsync(string subfolder, bool onlyFolders)
{
// Downloads folder contents using WebRequest
}
public async Task DownloadFileAsync(string path, string file)
{
var remote = new Uri("ftp://" + _hostname + path + "/" + file);
var local = _workingFolder + #"\" + file;
using (var ftpClient = new WebClient { Credentials = new NetworkCredential(_username, _password) })
{
ftpClient.DownloadFileCompleted += (sender, e) => DownloadFileCompleted(sender, e, file);
await ftpClient.DownloadFileTaskAsync(remote, local);
}
}
public void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e, Uri remote, string local)
{
if (e.Error != null)
{
Logger.Warn("Failed downloading\n\t{0}\n\t{1}", file, e.Error.Message);
return;
}
Logger.Info("Downloaded \n\t{1}", file);
}
}
Seems like some of your tasks aren't completed. Try do like this (I do the same when putting bunch of files to ftp)
Create array of tasks for every file being downloaded. Run them in cycle like this:
Task[] tArray = new Task[DictToUpload.Count];
foreach (var pair in DictToUpload)
{
tArray[i] = Task.Factory.StartNew(()=>{/* some stuff */});
}
await Task.WhenAll(tArray);
Use await Task.WhenAll(taskArray) instead of await each task. This guarantees all your tasks are completed.
Learn some peace of TPL ;)

How to return JSON to browser from model class using SignalR and URL call to Web API?

Here's what's going on. I have an ASP.NET MVC 4 Web API web application. I can call API resources via URL. One of these functions get performance monitoring data for a specified amount of time and returns it in JSON once it has completed. However, what I want to do is return
It is IMPORTANT to note that I am working with a the browser and API resources in the model, not with a View. Please don't casually tell me to use Javascript in a View, because there is no view, or tell me to look at the SignalR wiki because the information for ".NET" sections is meant for desktop applications, not web apps. For example, you can't "Console.WriteLine()" to a browser.
To reiterate, I am using ASP.NET MVC 4 Web API to develop an API, and am calling the API via URL in the browser and it is returning JSON. I am attempting to use SignalR to have the app send JSON to the browser, but it is not doing anything at all. Rather, the application simply returns the completed JSON from the controller action with all of the performance data values once the process has completed. In other words, SignalR is not working.
So what I'm trying to do is while the API resource is gathering all the information, SignalR sends JSON to the browser every second so that the client can see what's going on in real time.
What I need to find out is why SignalR isn't sending it, and how I can send information to be displayed in the browser without Javascript, since I'm working from a model class, not from a view.
As you can see, I subscribe to the event using On, and then use Invoke to call the server-side hub method SendToClient.
Please let me know if I'm trying to do is impossible. I have never heard of a "real-time", dynamic API call via URL.
Here is my hub class. It is located in ~/signalr/hubs and is in a file called LiveHub.cs. The method Send is what I am trying to invoke in the method seen in the next code block.
namespace PerfMon2.signalr.hubs
{
public class LiveHub : Hub
{
public void SendToClient(List<DataValueInfo> json)
{
Clients.showValue(json);
}
}
}
Here is the method from LogDBRepository.cs that includes the SignalR calls.
public List<LogInfo> LogTimedPerfData(string macName, string categoryName, string counterName,
string instanceName, string logName, string live, long? seconds)
{
iModsDBRepository modsDB = new iModsDBRepository();
List<MachineInfo> theMac = modsDB.GetMachineByName(macName);
if (theMac.Count == 0)
return new List<LogInfo>();
else if (instanceName == null)
{
if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
!PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) )
{
return new List<LogInfo>();
}
}
else if (instanceName != null)
{
if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
!PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) ||
!PerformanceCounterCategory.InstanceExists(instanceName, categoryName, macName))
{
return new List<LogInfo>();
}
}
else if (logName == null)
{
return new List<LogInfo>();
}
// Check if entered log name is a duplicate for the authenticated user
List<LogInfo> checkDuplicateLog = this.GetSingleLog(logName);
if (checkDuplicateLog.Count > 0)
{
return new List<LogInfo>();
}
PerformanceCounterCategory category = new PerformanceCounterCategory(categoryName, theMac[0].MachineName);
if (category.CategoryName == null || category.MachineName == null)
{
return new List<LogInfo>();
}
List<LogInfo> logIt = new List<LogInfo>();
if (category.CategoryType != PerformanceCounterCategoryType.SingleInstance)
{
List<InstanceInfo> instances = modsDB.GetInstancesFromCatMacName(theMac[0].MachineName, category.CategoryName);
foreach (InstanceInfo inst in instances)
{
if (!category.InstanceExists(inst.InstanceName))
{
continue;
}
else if (inst.InstanceName.Equals(instanceName, StringComparison.OrdinalIgnoreCase))
{
PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
inst.InstanceName, theMac[0].MachineName);
//CounterSample data = perfCounter.NextSample();
//double value = CounterSample.Calculate(data, perfCounter.NextSample());
string data = "";
List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);
string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
//string[] dataValues = new string[(int)seconds];
List<string> dataValues = new List<string>();
var hubConnection = new HubConnection("http://localhost/PerfMon2/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
var perfMon = hubConnection.CreateProxy("LiveHub");
// perfMon.On("sendValue", message => Console.WriteLine(message));
perfMon.On("showValue", json => Console.WriteLine(json));
hubConnection.Start().Wait();
List<DataValueInfo> lol = new List<DataValueInfo>();
for (int i = 0; i < seconds; i++)
{
data = "Value " + i + ": " + perfCounter.NextValue().ToString();
//dataValues[i] = data;
dataValues.Add(data);
lol.Add(new DataValueInfo
{
Value = perfCounter.NextValue().ToString()
});
// perfMon.Invoke<List<DataValueInfo>>("Send", lol);
perfMon.Invoke("SendToClient", lol);
Thread.Sleep(1000);
}
string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
Log log = new Log
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = string.Join(",", dataValues),
UserID = currUser[0].UserID
};
this.CreateLog(log);
logIt.Add(new LogInfo
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = dataValues.ToList<string>()
});
break;
}
}
}
else
{
PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
"", theMac[0].MachineName);
string data = "";
List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);
string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
//string[] dataValues = new string[(int)seconds];
List<string> dataValues = new List<string>();
var hubConnection = new HubConnection("http://localhost/PerfMon2/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
var perfMon = hubConnection.CreateProxy("LiveHub");
// perfMon.On("sendValue", message => Console.WriteLine(message));
perfMon.On("showValue", json => Console.WriteLine(json));
hubConnection.Start().Wait();
List<DataValueInfo> lol = new List<DataValueInfo>();
for (int i = 0; i < seconds; i++)
{
data = "Value " + i + ": " + perfCounter.NextValue().ToString();
//dataValues[i] = data;
dataValues.Add(data);
lol.Add(new DataValueInfo
{
Value = perfCounter.NextValue().ToString()
});
// perfMon.Invoke<List<DataValueInfo>>("Send", lol);
perfMon.Invoke("SendToClient", lol);
Thread.Sleep(1000);
}
string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
Log log = new Log
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = string.Join(",", dataValues),
UserID = currUser[0].UserID
};
this.CreateLog(log);
logIt.Add(new LogInfo
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = dataValues.ToList<string>()
});
}
return logIt;
}
Here is the controller for the method in LogController.cs :
[AcceptVerbs("GET", "POST")]
public List<LogInfo> Log_Perf_Data(string machine_name, string category_name, string counter_name, string instance_name,
string log_name, long? seconds, string live, string enforceQuery)
{
LogController.CheckUser();
// POST api/log/post_data?machine_name=&category_name=&counter_name=&instance_name=&log_name=&seconds=
if (machine_name != null && category_name != null && counter_name != null && log_name != null && seconds.HasValue && enforceQuery == null)
{
List<LogInfo> dataVal = logDB.LogTimedPerfData(machine_name, category_name, counter_name, instance_name,
log_name, live, seconds);
logDB.SaveChanges();
return dataVal;
}
return new List<LogInfo>();
}
Maybe you can implement it in push technique. Here is how I do it:
Class with message
public class Message
{
/// <summary>
/// The name who will receive this message.
/// </summary>
public string RecipientName { get; set; }
/// <summary>
/// The message content.
/// </summary>
public string MessageContent { get; set; }
}
Class that will represent client:
public class Client
{
private ManualResetEvent messageEvent = new ManualResetEvent(false);
private Queue<Message> messageQueue = new Queue<Message>();
/// <summary>
/// This method is called by a sender to send a message to this client.
/// </summary>
/// <param name="message">the new message</param>
public void EnqueueMessage(Message message)
{
lock (messageQueue)
{
messageQueue.Enqueue(message);
// Set a new message event.
messageEvent.Set();
}
}
/// <summary>
/// This method is called by the client to receive messages from the message queue.
/// If no message, it will wait until a new message is inserted.
/// </summary>
/// <returns>the unread message</returns>
public Message DequeueMessage()
{
// Wait until a new message.
messageEvent.WaitOne();
lock (messageQueue)
{
if (messageQueue.Count == 1)
{
messageEvent.Reset();
}
return messageQueue.Dequeue();
}
}
}
Class to send messages to clients:
public class ClientAdapter
{
/// <summary>
/// The recipient list.
/// </summary>
private Dictionary<string, Client> recipients = new Dictionary<string,Client>();
/// <summary>
/// Send a message to a particular recipient.
/// </summary>
public void SendMessage(Message message)
{
if (recipients.ContainsKey(message.RecipientName))
{
Client client = recipients[message.RecipientName];
client.EnqueueMessage(message);
}
}
/// <summary>
/// Called by a individual recipient to wait and receive a message.
/// </summary>
/// <returns>The message content</returns>
public string GetMessage(string userName)
{
string messageContent = string.Empty;
if (recipients.ContainsKey(userName))
{
Client client = recipients[userName];
messageContent = client.DequeueMessage().MessageContent;
}
return messageContent;
}
/// <summary>
/// Join a user to the recipient list.
/// </summary>
public void Join(string userName)
{
recipients[userName] = new Client();
}
/// <summary>
/// Singleton pattern.
/// This pattern will ensure there is only one instance of this class in the system.
/// </summary>
public static ClientAdapter Instance = new ClientAdapter();
private ClientAdapter() { }
}
Sending messages:
Message message = new Message
{
RecipientName = tbRecipientName.Text.Trim(),
MessageContent = tbMessageContent.Text.Trim()
};
if (!string.IsNullOrWhiteSpace(message.RecipientName) && !string.IsNullOrEmpty(message.MessageContent))
{
// Call the client adapter to send the message to the particular recipient instantly.
ClientAdapter.Instance.SendMessage(message);
}
Receive messages (this is JavaScript functions written in test page. They render content of the message on ASPX page. Here you should implement your logic):
// This method will persist a http request and wait for messages.
function waitEvent() {
CSASPNETReverseAJAX.Dispatcher.WaitMessage("<%= Session["userName"] %>",
function (result) {
displayMessage(result);
// Keep looping.
setTimeout(waitEvent, 0);
}, function () {
// Keep looping.
setTimeout(waitEvent, 0);
});
}
// Append a message content to the result panel.
function displayMessage(message) {
var panel = document.getElementById("<%= lbMessages.ClientID %>");
panel.innerHTML += currentTime() + ": " + message + "<br />";
}
// Return a current time string.
function currentTime() {
var currentDate = new Date();
return currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds();
}

Categories

Resources