I have a button that when clicked will start downloading multiple files (this button will also open a chrome://downloads tab and closes it immediately.
The page.download event handler for downloads will not fire.
The page.WaitForDownloadAsync() returns only one of these files.
I do not know the file names that will be downloaded, I also do not know if more than 1 file will be downloaded, there is always the possibility that only 1 file will be downloaded, but also the possibility that multiple files will be downloaded.
How can I handle this in playwright? I would like to return a list of all the downloaded files paths.
So I resolved this with the following logic.
I created two variables:
List<string> downloadedFiles = new List<string>();
List<string> fileDownloadSession = new();
I then created a method to add as a handler to the page.Download that looks like this:
private async void downloadHandler(object sender, IDownload download)
{
fileDownloadSession.Add("Downloading...");
var waiter = await download.PathAsync();
downloadedFiles.Add(waiter);
fileDownloadSession.Remove(fileDownloadSession.First());
}
Afterwards, I created a public method to get the downloaded files that looks like this:
public List<string> GetDownloadedFiles()
{
while (fileDownloadSession.Any())
{
}
var downloadedFilesList = downloadedFiles;
downloadedFiles = new List<string>();
return downloadedFilesList;
}
All these methods and planning are in a separate class of their own so that they can monitor the downloaded files properly, and also to freeze the main thread so it can grab all of the required files.
All in all it seems just as sketchy of a solution, similarly to how you would implement it in Selenium, nothing much has changed in terms of junkyard implementations in the new frameworks.
You can find my custom class here: https://paste.mod.gg/rztmzncvtagi/0, enjoy, there is no other topic that answers this specific question for playwright on C#.
Code here, in case it gets deleted from paste.mod.gg:
using System.Net;
using System.Runtime.InteropServices.JavaScript;
using Flanium;
using FlaUI.UIA3;
using Microsoft.Playwright;
using MoreLinq;
using Polly;
namespace Fight;
public class WebBrowser
{
private IBrowser _browser;
private IBrowserContext _context;
private IPage _page;
private bool _force;
private List<string> downloadedFiles = new List<string>();
private List<string> fileDownloadSession = new();
public void EagerMode()
{
_force = true;
}
public enum BrowserType
{
None,
Chrome,
Firefox,
}
public IPage GetPage()
{
return _page;
}
public WebBrowser(BrowserType browserType = BrowserType.Chrome, bool headlessMode = false)
{
var playwright = Playwright.CreateAsync().Result;
_browser = browserType switch
{
BrowserType.Chrome => playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions {Headless = headlessMode}).Result,
BrowserType.Firefox => playwright.Firefox.LaunchAsync(new BrowserTypeLaunchOptions {Headless = headlessMode}).Result,
_ => null
};
_context = _browser.NewContextAsync().Result;
_page = _context.NewPageAsync().Result;
_page.Download += downloadHandler;
Console.WriteLine("WebBrowser was successfully started.");
}
private async void downloadHandler(object sender, IDownload download)
{
fileDownloadSession.Add("Downloading...");
var waiter = await download.PathAsync();
downloadedFiles.Add(waiter);
fileDownloadSession.Remove(fileDownloadSession.First());
}
public List<string> GetDownloadedFiles()
{
while (fileDownloadSession.Any())
{
}
var downloadedFilesList = downloadedFiles;
downloadedFiles = new List<string>();
return downloadedFilesList;
}
public void Navigate(string url)
{
_page.GotoAsync(url).Wait();
}
public void Close(string containedURL)
{
var pages = _context.Pages.Where(x => x.Url.Contains(containedURL));
if (pages.Any())
pages.ForEach(x => x.CloseAsync().Wait());
}
public IElementHandle Click(string selector, int retries = 15, int retryInterval = 1)
{
var element = Policy.HandleResult<IElementHandle>(result => result == null)
.WaitAndRetry(retries, interval => TimeSpan.FromSeconds(retryInterval))
.Execute(() =>
{
var element = FindElement(selector);
if (element != null)
{
try
{
element.ClickAsync(new ElementHandleClickOptions() {Force = _force}).Wait();
element.DisposeAsync();
return element;
}
catch (Exception e)
{
return null;
}
}
return null;
});
return element;
}
public IElementHandle FindElement(string selector)
{
IElementHandle element = null;
var Pages = _context.Pages.ToArray();
foreach (var w in Pages)
{
//============================================================
element = w.QuerySelectorAsync(selector).Result;
if (element != null)
{
return element;
}
//============================================================
var iframes = w.Frames.ToList();
var index = 0;
for (; index < iframes.Count; index++)
{
var frame = iframes[index];
element = frame.QuerySelectorAsync(selector).Result;
if (element is not null)
{
return element;
}
var children = frame.ChildFrames;
if (children.Count > 0 && iframes.Any(x => children.Any(y => y.Equals(x))) == false)
{
iframes.InsertRange(index + 1, children);
index--;
}
}
}
return element;
}
}
Related
I implemented the download functionality in my android app using the download manager.
The download manager dows its job well, and once download is completed, the broadcast receiver I set is called successfully.
But, I want to monitor the download progress and display it inside my app, and not rely only on the download manager's notification.
So, I implemented a "ContentProvider and a ContentObserver" to query frequently the download manager for download progress.
Here is my content provider:
[ContentProvider(new string[] { DownloadsContentProvider.Authority })]
public class DownloadsContentProvider : ContentProvider
{
public const string Authority = "com.myapp.Myapp.DownloadProvider";
public DownloadsContentProvider()
{
}
public static Android.Net.Uri ProviderUri(long downloadId)
{
Android.Net.Uri uri = Android.Net.Uri.Parse($"http://content//downloads/my_downloads/{downloadId}");
var builder = new Android.Net.Uri.Builder()
.Authority(Authority)
.Scheme(ContentResolver.SchemeFile)
.Path(uri.Path)
.Query(uri.Query)
.Fragment(uri.Fragment);
return builder.Build();
}
public override int Delete(Android.Net.Uri uri, string selection, string[] selectionArgs)
{
return 0;
}
public override string GetType(Android.Net.Uri uri)
{
return null;
}
public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues values)
{
return null;
}
public override bool OnCreate()
{
return true;
}
public override ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
{
throw new NotImplementedException();
}
public override int Update(Android.Net.Uri uri, ContentValues values, string selection, string[] selectionArgs)
{
return 0;
}
}
Then, I created a content observer to observe what happens and trigger the query of downloads progress.
public DownloadProgressContentObserver(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference,
transfer)
{
}
public DownloadProgressContentObserver(Handler? handler) : base(handler)
{
}
public DownloadProgressContentObserver() : base(null)
{
}
public override void OnChange(bool selfChange, Uri? uri)
{
base.OnChange(selfChange, uri);
var downloadId = uri.ToString().Substring(uri.ToString().LastIndexOf(Path.PathSeparator) + 1);
if (!string.IsNullOrEmpty(downloadId))
{
ComputeDownloadStatus(Convert.ToInt64(downloadId));
//TODO: dispatch this download percentage to the whole app, and the database
}
}
public void ComputeDownloadStatus(long downloadId)
{
long downloadedBytes = 0;
long totalSize = 0;
int status = 0;
DownloadManager.Query query = new DownloadManager.Query().SetFilterById(downloadId);
var downloadManager = DownloadManager.FromContext(Android.App.Application.Context);
var cursor = downloadManager.InvokeQuery(query);
String downloadFilePath = (cursor.GetString(cursor.GetColumnIndex(DownloadManager.ColumnLocalUri))).Replace("file://", "");
try
{
if (cursor != null && cursor.MoveToFirst())
{
downloadedBytes =
cursor.GetLong(cursor.GetColumnIndexOrThrow(DownloadManager.ColumnBytesDownloadedSoFar));
totalSize =
cursor.GetInt(cursor.GetColumnIndexOrThrow(DownloadManager.ColumnTotalSizeBytes));
}
}
finally
{
if (cursor != null)
{
cursor.Close();
}
}
var percentage = (downloadedBytes / totalSize) * 100;
}
}
This is how I use both, and register them in the download manager to monitor the download progress.
var manager = DownloadManager.FromContext(Android.App.Application.Context);
var request = new DownloadManager.Request(Android.Net.Uri.Parse(downloadUrl));
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
request.SetDestinationInExternalPublicDir(downloadsPath, fileName);
request.SetTitle(productTitle);
request.SetDescription(downloadDescription);
long downloadId = manager.Enqueue(request);
//I provide a valid URI with my content provicer
var uri = DownloadsContentProvider.ProviderUri(downloadId);
var contentResolver = Android.App.Application.Context.ContentResolver;
var observer = new DownloadProgressContentObserver();
contentResolver.RegisterContentObserver(uri, true, observer);
ProductContentObservers.Add(downloadId, observer);
I have read a lot of doc, and my implementation seems to be ok. But the content observer's "OnCHange" method is never called.
Can someone please point out what I might be doing wrong ?
How can I get all classes that implements a specific interface then call a function of that class if a string member of the specific class matches a given one?
Basically what I have is a ICommandHandler interface:
interface ICommandHandler
{
string Command { get; }
Task ExecuteAsync();
}
and a class that implements it:
public class StartCommand : ICommandHandler
{
public string Command { get => "start"; }
public async Task ExecuteAsync()
{
// do something
}
}
What I want to do is to get all the classes that implements the ICommandHandler interface, then verify if the class.Command equals a specific string and if it does then call the ExecuteAsync method.
I've tried using this answer here: https://stackoverflow.com/a/45382386/15306888 but the class.Command is always null
Edit: The answer I got bellow does what I wanted to do:
What I was looking for was a way to use ICommandHandler to allow me to easily gather all the classes inheriting from it and call the ExecuteAsync function instead of having to manually add the methods in the part of the code handling TdLib message events.
So now my project directory looks something like this:
TelegramClient.cs - The place where the classes inheriting from the ICommandHandler are loaded, and where the ExecuteAsync method is called if the Command member matches the Command parsed from the message returned by telegram.
Handlers/ - Base directory for my handlers
CommandHandlers/ - Folder with the ICommandHandler interface and the classes inheriting from it
MessageHandlers/ - Folder with another interface IMessageHandler and the classes inheriting from it
Anyway, in the meantime I've found another answer on a stackoverflow question (Had to scroll a few times) that made it way easier and faster to get multiple handlers without having to repeat the same code over and over again. I've ended by combining the answer linked above with this one: https://stackoverflow.com/a/41650057/15306888
So I ended with a simple method:
public static IEnumerable<T> GetAll<T>()
{
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type))
.Where(type =>
!type.IsAbstract &&
!type.IsGenericType &&
type.GetConstructor(new Type[0]) != null)
.Select(type => (T)Activator.CreateInstance(type))
.ToList();
}
That can be easily used like:
private async Task ProcessMessage(TdApi.Update.UpdateNewMessage message)
{
var command = GetCommand(message.Message);
var textMessage = GetMessageText(message.Message);
if (!String.IsNullOrWhiteSpace(command))
{
var commandHandlers = GetAll<ICommandHandler>();
foreach (var handler in commandHandlers)
{
if (command == handler.Command)
await handler.ExecuteAsync(_client, message.Message);
}
}
else if (!String.IsNullOrWhiteSpace(textMessage))
{
var messageHandlers = GetAll<IMessageHandler>();
foreach (var handler in messageHandlers)
{
var outgoing = handler.Outgoing && message.Message.IsOutgoing;
var incoming = handler.Incoming && !message.Message.IsOutgoing;
if (outgoing || incoming)
{
if (!String.IsNullOrEmpty(handler.Pattern))
{
var match = Regex.Match(textMessage, handler.Pattern);
if (match.Success)
await handler.ExecuteAsync(_client, message.Message);
}
}
}
}
}
How the Interface is actually implemented:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using TdLib;
namespace YoutubeDl_Bot.Handlers.CommandHandlers
{
public class StartCommand : ICommandHandler
{
public string Command { get => "/start"; }
public async Task ExecuteAsync(TdClient client, TdApi.Message message)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Hi! I'm a bot that downloads and sends video and audio files from youtube links and many other supported services");
stringBuilder.AppendLine(String.Empty);
stringBuilder.AppendLine("**Usage:**");
stringBuilder.AppendLine("• Send or forward a text message containing links and I will:");
stringBuilder.AppendLine("• Download the best audio quality available for the video in the speecified link");
stringBuilder.AppendLine("• Download the best video quality available for the video in the speecified link");
stringBuilder.AppendLine("• Send the direct download URL for every link specified in the message");
stringBuilder.AppendLine("• Supported links are available here: https://ytdl-org.github.io/youtube-dl/supportedsites.html");
var formattedText = await client.ExecuteAsync(new TdLib.TdApi.ParseTextEntities { Text = stringBuilder.ToString(), ParseMode = new TdLib.TdApi.TextParseMode.TextParseModeMarkdown() });
await client.ExecuteAsync(new TdLib.TdApi.SendMessage { ChatId = message.ChatId, InputMessageContent = new TdLib.TdApi.InputMessageContent.InputMessageText { Text = formattedText } });
}
}
}
Full TelegramClient class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using TdLib;
using YoutubeDl_Bot.Handlers.CallbackHandlers;
using YoutubeDl_Bot.Handlers.CommandHandlers;
using YoutubeDl_Bot.Handlers.MessageHandlers;
using YoutubeDl_Bot.Settings;
using YoutubeDl_Bot.Utils;
namespace YoutubeDl_Bot
{
class TelegramBotClient
{
private static TdClient _client;
private static TdLib.TdApi.User _me;
private static int _apiId;
private static string _apiHash;
private static string _token;
#if DEBUG
private static readonly int _verbosityLevel = 4;
#else
private static readonly int _verbosityLevel = 0;
#endif
public static bool AuthCompleted = false;
public TelegramBotClient(int apiId, string apiHash)
{
_apiId = apiId;
_apiHash = apiHash;
}
public async Task<TdClient> CreateClient()
{
_client = new TdClient();
await _client.ExecuteAsync(new TdApi.SetLogVerbosityLevel { NewVerbosityLevel = _verbosityLevel });
return _client;
}
public void StartListening(string botToken)
{
_token = botToken;
_client.UpdateReceived += _client_UpdateReceived;
}
private async void _client_UpdateReceived(object sender, TdApi.Update update)
{
switch (update)
{
case TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdApi.AuthorizationState.AuthorizationStateWaitTdlibParameters):
await _client.ExecuteAsync(new TdApi.SetTdlibParameters
{
Parameters = new TdApi.TdlibParameters
{
ApiId = _apiId,
ApiHash = _apiHash,
ApplicationVersion = "0.0.1",
DeviceModel = "Bot",
SystemLanguageCode = "en",
SystemVersion = "Unknown"
}
});
break;
case TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdLib.TdApi.AuthorizationState.AuthorizationStateWaitEncryptionKey):
await _client.ExecuteAsync(new TdLib.TdApi.CheckDatabaseEncryptionKey());
break;
case TdLib.TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdLib.TdApi.AuthorizationState.AuthorizationStateWaitPhoneNumber):
await _client.ExecuteAsync(new TdLib.TdApi.CheckAuthenticationBotToken { Token = _token });
break;
case TdLib.TdApi.Update.UpdateConnectionState updateConnectionState when updateConnectionState.State.GetType() == typeof(TdLib.TdApi.ConnectionState.ConnectionStateReady):
// To Do Settings
var botSettings = new BotSettings(_apiId, _apiHash, _token);
_me = await _client.ExecuteAsync(new TdLib.TdApi.GetMe());
Helpers.Print($"Logged in as: {_me.FirstName}");
SettingsManager.Set<BotSettings>("BotSettings.data", botSettings);
break;
case TdLib.TdApi.Update.UpdateNewMessage message:
if (!message.Message.IsOutgoing)
await ProcessMessage(message);
break;
case TdApi.Update.UpdateNewCallbackQuery callbackQuery:
await ProcessCallbackQuery(callbackQuery);
break;
default:
break;
}
}
#region PROCESS_MESSAGE
private async Task ProcessMessage(TdApi.Update.UpdateNewMessage message)
{
var command = GetCommand(message.Message);
var textMessage = GetMessageText(message.Message);
#region COMMAND_HANDLERS
if (!String.IsNullOrWhiteSpace(command))
{
var commandHandlers = GetAll<ICommandHandler>();
foreach (var handler in commandHandlers)
{
if (command == handler.Command)
await handler.ExecuteAsync(_client, message.Message);
}
}
#endregion
#region MESSAGE_HANDLERS
else if (!String.IsNullOrWhiteSpace(textMessage))
{
var messageHandlers = GetAll<IMessageHandler>();
foreach (var handler in messageHandlers)
{
var outgoing = handler.Outgoing && message.Message.IsOutgoing;
var incoming = handler.Incoming && !message.Message.IsOutgoing;
if (outgoing || incoming)
{
if (!String.IsNullOrEmpty(handler.Pattern))
{
var match = Regex.Match(textMessage, handler.Pattern);
if (match.Success)
await handler.ExecuteAsync(_client, message.Message);
}
}
}
}
#endregion
}
#endregion
#region PROCESS_CALLACK
private async Task ProcessCallbackQuery(TdApi.Update.UpdateNewCallbackQuery callbackQuery)
{
if (callbackQuery.Payload.GetType() == typeof(TdApi.CallbackQueryPayload.CallbackQueryPayloadData))
{
var payload = callbackQuery.Payload as TdApi.CallbackQueryPayload.CallbackQueryPayloadData;
var callbackHandlers = GetAll<ICallbackHandler>();
foreach (var handler in callbackHandlers)
{
if (handler.DataIsRegex)
if (Regex.Match(System.Text.Encoding.UTF8.GetString(payload.Data), handler.Data).Success)
await handler.ExecuteAsync(_client, callbackQuery);
else if (handler.Data == System.Text.Encoding.UTF8.GetString(payload.Data))
await handler.ExecuteAsync(_client, callbackQuery);
}
}
}
#endregion
#region COMMAND_PARSER
public string GetCommand(TdApi.Message message)
{
string command = null;
TdLib.TdApi.FormattedText formattedText = new TdLib.TdApi.FormattedText();
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageText))
{
var messageText = message.Content as TdLib.TdApi.MessageContent.MessageText;
formattedText = messageText.Text;
}
else
{
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessagePhoto))
{
var messagePhoto = message.Content as TdLib.TdApi.MessageContent.MessagePhoto;
formattedText = messagePhoto.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageDocument))
{
var messageDocument = message.Content as TdLib.TdApi.MessageContent.MessageDocument;
formattedText = messageDocument.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageVideo))
{
var messageVideo = message.Content as TdLib.TdApi.MessageContent.MessageVideo;
formattedText = messageVideo.Caption;
}
}
foreach (var entity in formattedText.Entities)
{
if (entity.Type.GetType() == typeof(TdLib.TdApi.TextEntityType.TextEntityTypeBotCommand) && String.IsNullOrWhiteSpace(command))
{
if (entity.Offset == 0)
{
var splitCommand = formattedText.Text.Split();
if (splitCommand[0].EndsWith($"#{_me.Username}"))
{
command = splitCommand[0].Split('#')[0];
}
else
{
command = splitCommand[0];
}
}
}
}
return command;
}
#endregion
#region MESSAGE_PARSER
public string GetMessageText(TdApi.Message message)
{
TdLib.TdApi.FormattedText formattedText = new TdLib.TdApi.FormattedText();
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageText))
{
var messageText = message.Content as TdLib.TdApi.MessageContent.MessageText;
formattedText = messageText.Text;
}
else
{
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessagePhoto))
{
var messagePhoto = message.Content as TdLib.TdApi.MessageContent.MessagePhoto;
formattedText = messagePhoto.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageDocument))
{
var messageDocument = message.Content as TdLib.TdApi.MessageContent.MessageDocument;
formattedText = messageDocument.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageVideo))
{
var messageVideo = message.Content as TdLib.TdApi.MessageContent.MessageVideo;
formattedText = messageVideo.Caption;
}
}
return formattedText.Text;
}
#endregion
#region REFLECTION
// https://stackoverflow.com/a/41650057/15306888
public static IEnumerable<T> GetAll<T>()
{
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type))
.Where(type =>
!type.IsAbstract &&
!type.IsGenericType &&
type.GetConstructor(new Type[0]) != null)
.Select(type => (T)Activator.CreateInstance(type))
.ToList();
}
#endregion
}
}
Since it's always null I think that the problem is that you're not creating an instance of your handler. I prepared a demo for you where I did that and it works.
public interface ICommandHandler
{
string Command { get; }
Task ExecuteAsync();
}
public class FirstCommandHandler : ICommandHandler
{
public string Command => "First";
public async Task ExecuteAsync()
{
Console.WriteLine("Hello from first.");
await Task.Delay(10);
}
}
public class SecondCommandHandler : ICommandHandler
{
public string Command => "Second";
public async Task ExecuteAsync()
{
Console.WriteLine("Hello from second.");
await Task.Delay(10);
}
}
public class Program
{
static async Task Main(string[] args)
{
var handlers = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => typeof(ICommandHandler).IsAssignableFrom(p) && p.IsClass);
foreach (var handler in handlers)
{
var handlerInstance = (ICommandHandler)Activator.CreateInstance(handler);
if (handlerInstance.Command == "First")
{
await handlerInstance.ExecuteAsync();
}
}
}
}
If it's not the case, could you show some more code? Are you trying to check Command value by reflection?
I'm getting the following error on my C# Web API: "Exception thrown: 'System.Threading.ThreadAbortException' in System.Data.dll
Thread was being aborted". I have a long running process on one thread using my data access logic class to get and update records being process. Meanwhile a user submits another group to process which has need of the same data access logic class, thus resulting in the error. Here is a rough sketch of what I'm doing.
WebAPI Class:
public IHttpActionResult OkToProcess(string groupNameToProcess)
{
var logic = GetLogic();
//Gets All Unprocessed Records and Adds them to Blocking Queue
Task.Factory.StartNew(() => dataAccessLogic.LoadAndProcess(groupNameToProcess);
}
public IHttpActionResult AddToProcess(int recordIdToProcess)
{
StaticProcessingFactory.AddToQueue(recordIdToProcess);
}
StaticProcessingFactory
internal static ConcurrentDictionary<ApplicationEnvironment, Logic> correctors = new ConcurrentDictionary<ApplicationEnvironment, Logic>();
internal static BlockingCollection<CorrectionMessage> MessageQueue = new BlockingCollection<Message>(2000);
public void StartService(){
Task.Factory.StartNew(() => LoadService());
}
public void LoadService(){
var logic = GetLogic();
if(isFirstGroupOkToProcessAsPerTextFileLog())
logic.LoadAndProcess("FirstGroup");
if(isSeconddGroupOkToProcessAsPerTextFileLog())
logic.LoadAndProcess("SecondGroup");
}
public static GetLogic(){
var sqlConnectionFactory = Tools.GetSqlConnectionFactory();
string environment = ConfigurationManager.AppSettings["DefaultApplicationEnvironment"];
ApplicationEnvironment applicationEnvironment =
ApplicationEnvironmentExtensions.ToApplicationEnvironment(environment);
return correctors.GetOrAdd(applicationEnvironment, new Logic(sqlConnectionFactory ));
}
public static void AddToQueue(Message message, bool completeAdding = true)
{
if (MessageQueue.IsAddingCompleted)
MessageQueue = new BlockingCollection<Message>();
if (completeAdding && message.ProcessImmediately)
StartQueue(message);
else
MessageQueue.Add(message);
}
public static void StartQueue(Message message = null)
{
if (message != null)
{
if(!string.IsNullOrEmpty(message.ID))
MessageQueue.Add(message);
Logic logic = GetLogic(message.Environment);
try
{
var messages = MessageQueue.TakeWhile(x => logic.IsPartOfGroup(x.GroupName, message.GroupName));
if (messages.Count() > 0)
MessageQueue.CompleteAdding();
int i = 0;
foreach (var msg in messages)
{
i++;
Process(msg);
}
}
catch (InvalidOperationException) { MessageQueue.CompleteAdding(); }
}
}
public static void Process(Message message)
{
Var logic = GetLogic(message.Environment);
var record = logic.GetRecord(message.ID);
record.Status = Status.Processed;
logic.Save(record);
}
Logic Class
private readonly DataAccess DataAccess;
public Logic(SqlConnectionFactory factory)
{
DataAccess = new DataAcess(factory);
}
public void LoadAndProcess(string groupName)
{
var groups = DataAccess.GetGroups();
var records = DataAccess.GetRecordsReadyToProcess(groups);
for(int i = 0; i < records.Count; i++)
{
Message message = new Message();
message.Enviornment = environment.ToString();
message.ID = records[i].ID;
message.User = user;
message.Group = groupName;
message.ProcessImmediately = true;
StaticProcessingFactory.AddToQueue(message, i + 1 == records.Count);
}
}
Any ideas how I might ensure that all traffic from all threads have access to the Data Access Logic without threads being systematically aborted?
I'm setting up my architechture to use Cef.Offscreen. In order to make it easy to work with I have divided some parts. But I run into a problem that controller loading finshes and serves a view before everything has been able to load.
Here's my structure --> Controller
public ActionResult InitBrowser()
{
ICefSharpRenderer renderer = RendererSingelton.GetInstance();
//Try to render something in default appdomain
renderer.LoginToTradingView(null, null);
ViewBag.SiteTitle = BrowserActions.RunScriptInNamedBrowser("loginbrowser", #"(function() {return document.title;} )();");
ViewBag.ImagesixtyfourUrl = BrowserActions.TakeScreenshot("loginbrowser");
//this is returned to fast, we have to wait for all
return View();
}
I have this class to get do some basic actions and initialize if needed.
public class CefSharpRenderer : MarshalByRefObject, ICefSharpRenderer
{
private ChromiumWebBrowser _browser;
private TaskCompletionSource<JavascriptResponse> _taskCompletionSource;
private string _name;
public void LoginToTradingView(string url, string browserName)
{
CheckIfCefIsInitialized();
BrowserFactory.GetBrowserInstance(#"https://se.tradingview.com/", "loginbrowser");
}
public void CreateBrowserAndGoToUrl(string url, string browserName)
{
CheckIfCefIsInitialized();
BrowserFactory.GetBrowserInstance(url, "browserName");
}
public void CheckIfCefIsInitialized()
{
if (!Cef.IsInitialized)
{
var settings = new CefSettings();
var assemblyPath = Path.GetDirectoryName(new Uri(GetType().Assembly.CodeBase).LocalPath);
settings.BrowserSubprocessPath = Path.Combine(assemblyPath, "CefSharp.BrowserSubprocess.exe");
settings.ResourcesDirPath = assemblyPath;
settings.LocalesDirPath = Path.Combine(assemblyPath, "locales");
var osVersion = Environment.OSVersion;
//Disable GPU for Windows 7
if (osVersion.Version.Major == 6 && osVersion.Version.Minor == 1)
{
// Disable GPU in WPF and Offscreen examples until #1634 has been resolved
settings.CefCommandLineArgs.Add("disable-gpu", "1");
}
//Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(settings, performDependencyCheck: false, cefApp: null);
}
}
}
I get my browserinstance here and connected the events to be fired.
public static class BrowserFactory
{
public static ChromiumWebBrowser GetBrowserInstance(string _url, string browsername)
{
if (!BrowserContainer.CheckIfBrowserExists(browsername))
{
ChromiumWebBrowser _browser = new ChromiumWebBrowser(_url);
_browser.LoadingStateChanged += BrowserEvents.OnLoadingStateChanged;
BrowserContainer.AddDataHolder(browsername, new DataBrowserHolder { BrowserName = browsername, ChromiumWebBrow = _browser });
return _browser;
}
return null;
}
}
Browserevent loads correct page.
public static class BrowserEvents
{
public static void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
if (args.IsLoading == false)
{
ChromiumWebBrowser cwb = (ChromiumWebBrowser)sender;
if (cwb.Address == "https://se.tradingview.com/")
{
BrowserActions.LogInToTradingView("xxxxx", "yyyyyyy", "loginbrowser");
}
}
}
}
Last my browseractions, spare med for the thread sleeps it's just under construction and it works atm.
public static class BrowserActions
{
public static void LogInToTradingView(string twusername, string twpassword, string browserName)
{
ChromiumWebBrowser _dataholder = BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow;
IFrame ifww = _dataholder.GetMainFrame();
// var lull = #"(function() { var serielength = TradingView.bottomWidgetBar._widgets.backtesting._reportWidgetsSet.reportWidget._data.filledOrders.length; return serielength; })();";
// JavascriptResponse _js = Task.Run(async () => { return await _browser.GetMainFrame().EvaluateScriptAsync(lull); }).Result;
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-header__link tv-header__link--signin js-header__signin')[0].click();})();");
// var loginusernamescript =
var loginpasswordscript = #"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].value= " + twpassword + "; })();";
var clkloginbtn = #"(function() { document.getElementsByClassName('tv-button tv-button--no-border-radius tv-button--size_large tv-button--primary_ghost tv-button--loader')[0].click();})();";
Thread.Sleep(300);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[0].click();})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[0].value = '" + twusername + "';})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].click();})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].value = '" + twpassword + "';})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { document.getElementsByClassName('tv-button tv-button--no-border-radius tv-button--size_large tv-button--primary_ghost tv-button--loader')[0].click();})();");
}
public static string TakeScreenshot(string browserName)
{
try
{
Bitmap img = Task.Run(async () => { return await BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow.ScreenshotAsync(); }).Result;
// object mgss = img.Clone();
string baseen = ExtraFunctions.ToBase64String(img, ImageFormat.Png);
return baseen;
}
catch (Exception e)
{
var x = e.InnerException;
return null;
}
}
public static string RunScriptInNamedBrowser(string browserName, string script)
{
try
{
string str = Task.Run(async () => { return await BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow.GetMainFrame().EvaluateScriptAsync(script); }).Result.ToString();
// object mgss = img.Clone();
return str;
}
catch (Exception e)
{
var x = e.InnerException;
return null;
}
}
}
How can I get my browser actions to report back to my controller so that I can wait for them to finish?
For a Task asynchronous operation to report back, it's possible to use Progress<T>. How that's done is detailed in Enabling Progress and Cancellation in Async APIs. The key is:
var progressIndicator = new Progress<int>(ReportProgress);
This creates a Progress<T> object that can indicate how far a task is complete, and also call a custom method (ReportProgress) at set intervals. You can create a custom class if necessary instead of using int.
So your browser actions can report back to the controller with the progress reporting method until everything is complete.
I am trying to make a simple Windows 8/RT app for the store and i have a question about adding items to a ListBox.
In my MainPage i have this code:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.brain = new MainController();
LoadData();
}
public void LoadData()
{
brain.GetNotesRepoFile().ReadFile();
Debug(""+brain.GetNotesRepoFile().GetNotesList().Count);
for(int i = 0; i < brain.GetNotesRepoFile().GetNotesList().Count; i++)
{
notesListBox.Items.Add( // code here );
}
}
}
public class NotesRepositoryFile
{
// CONSTRUCTOR
public NotesRepositoryFile()
{
this.notesRepository = new List<Note>();
}
// Read from file
public async void ReadFile()
{
// settings for the file
var path = #"Files\Notes.txt";
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
// acquire file
var file = await folder.GetFileAsync(path);
var readThis = await Windows.Storage.FileIO.ReadLinesAsync(file);
foreach (var line in readThis)
{
notesRepository.Add(new Note(line.Split(';')[0], line.Split(';')[1]));
// check if the item was added
Debug.WriteLine("Added: " + notesRepository[notesRepository.Count - 1].ToString());
}
Debug.WriteLine("File read successfully");
}
}
My Output is:
0
Added: Test1
Added: Test2
File read successfully
What am i trying to do here is read strings from a file and add them using Items.Add to a listBox. But since the size of the array is 0, even though the items were added successfully that doesnt work.
I dont understand why Debug(""+brain.GetNotesRepoFile().GetNotesList().Count); is executed before brain.GetNotesRepoFile().ReadFile(); since clearly that is not the case.
Also why does this solution work, and the above doesnt ??
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.brain = new MainController();
ReadFile();
}
// Read from file
public async void ReadFile()
{
// settings for the file
var path = #"Files\Notes.txt";
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
// acquire file
var file = await folder.GetFileAsync(path);
var readThis = await Windows.Storage.FileIO.ReadLinesAsync(file);
foreach (var line in readThis)
{
brain.AddNote(line.Split(';')[0], line.Split(';')[1]);
notesListBox.Items.Add(brain.GetNotesRepoFile().GetNotesList()[brain.GetNotesRepoFile().GetNotesList().Count - 1].ToString());
}
Debug.WriteLine("File read successfully");
}
}
Well, usage of async and await is wrong is you code, please change according to following codes
First, in NotesRepositoryFile class
public async Task<bool> ReadFile()
{
//Your code
if (notesRepository.Count > 0) return true;
return false;
}
Second in the MainPage
public async void LoadData()
{
bool HasNote = await brain.GetNotesRepoFile().ReadFile();
if (HasNote)
{
for (int i = 0; i < brain.GetNotesRepoFile().notesRepository.Count; i++)
{
//Your code
}
}
}