WPF, Excel AddIn, C#,
I have multiple asychronous calls to get data from web service on main thread, then in call back,
I will plot the data in Excel. I tracked call back and they run on main thread, too.
but I still get COMException 0x800AC472, googled and it seems this is a multi-thread issue.
but I am confused why this happened.
I think there is only one main thread and since all callback are run on main thread and there is no reason to have the exception?
Edit:
On main UI thread, ribbon/button is clicked, it will call web service BuildMetaData,
once it is returned back, in its callback MetaDataCompleteCallback, another web service call is sent
Once it is returned back, in its callback DataRequestJobFinished, it will call plot to plot data on Excel. see below
On Main UI class:
Btn_Click()
{
...
_reportObjs[index].GenerateReport();
}
on Class to GenerateReport
public void GenerateReport()
{
Request.ParseFunction();
Request.MetacompleteCallBack = MetaDataCompleteCallback;
Request.BuildMetaData();
}
public void MetaDataCompleteCallback(int id)
{
try
{
if (Request.IsRequestCancelled)
{
Request.FormulaCell.Dispose();
return;
}
ErrorMessage = Request.ErrorMessage;
if (string.IsNullOrEmpty(Request.ErrorMessage))
{
_queryJob = new DataQueryJob(UnityContainer, Request.BuildQueryString(), DataRequestJobFinished, Request);
}
else
{
ModifyCommentOnFormulaCellPublishRefreshEvent();
}
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
ModifyCommentOnFormulaCellPublishRefreshEvent();
}
finally
{
Request.MetacompleteCallBack = null;
}
}
public void DataRequestJobFinished(DataRequestResponse response)
{
Dispatcher.Invoke(new Action<DataRequestResponse>(DataRequestJobFinishedUI), response);
}
public void DataRequestJobFinished(DataRequestResponse response)
{
try
{
if (Request.IsRequestCancelled)
{
return;
}
if (response.status != Status.COMPLETE)
{
ErrorMessage = ManipulateStatusMsg(response);
}
else // COMPLETE
{
// TODO: Convert this into factory pattern
var tmpReq = Request as DataRequest;
if (tmpReq == null) return;
new VerticalTemplate(tmpReq, response, IsOffice2003).Plot();
}
}
catch (Exception e)
{
ErrorMessage = e.Message;
MIMICShared.Helper.LogError(e);
}
finally
{
//if (token != null)
// this.UnityContainer.Resolve<IEventAggregator>().GetEvent<DataQueryJobComplete>().Unsubscribe(token);
ModifyCommentOnFormulaCellPublishRefreshEvent();
Request.FormulaCell.Dispose();
}
}
on plot class
public void Plot()
{
...
attributeRange.Value2 = headerArray;
DataRange.Value2 = ....
DataRange.NumberFormat = ...
}
I saw this stackoverflow.com/questions/5246288/errormessage-in-excel, social.msdn.microsoft.com/forums/en-US/vsto/thread/… It seems there is no solution to the issue except wait/retry.
THis post talks about how to check if Excel is in edit. http://www.add-in-express.com/creating-addins-blog/2011/03/23/excel-check-user-edit-cell/
Related
I am working on a function to send console window output to a discord channel.
i managed to make it work but i can't get the main goal of the function.
i want to get the output directly from the console instead of getting the output from the logger class which i have.
i have a separate logger class which controls all logging, that would be standard way but i have never tried to get the output directly from console.
i have searched and found Console.Out() method but i guess its mainly used for writing to text file.
i am using Discord.Net.
here is the sample function which i call from the logger class.
public sealed class LogToChannel : DiscordHandler
{
public static async Task SendToChannel(string message)
{
try
{
if (!IsServerOnline || string.IsNullOrEmpty(message) || string.IsNullOrWhiteSpace(message))
{
return;
}
string shortDate = DateTime.Now.ToShortDateString();
string shortTime = DateTime.Now.ToShortTimeString();
string LogFormat = $"[{shortDate} {shortTime}] {message}";
await Task.Delay(200).ConfigureAwait(false);
await Client.GetGuild(4646556464646464546).GetTextChannel(546464654646546465).SendMessageAsync(LogFormat).ConfigureAwait(false);
}
catch (Exception ex)
{
if(ex is NullReferenceException)
{
throw;
}
else
{
SGF.SGFLogger.LogException(ex);
return;
}
}
}
}
I am trying to see Loading progress as follows, but it does not show up.
View.cs
ViewModel.SelectedCommand.Execute(null);
ViewModel.cs
public ICommand SelectedCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
// the following does not show loading
using (UserDialogs.Instance.Loading("Loading..."))
{
var task = await _classroomService.GetClassRoomAsync(SelectedClassroom.Id);
ObservableCollection<ClassroomViewModel> class = new ObservableCollection<ClassroomViewModel>(task.ConvertAll(x => new ClassViewModel(x)));
}
});
}
}
Another example
public ICommand ReloadCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
await RefreshList();
});
}
}
// the following also does not show loading
private async Task RefreshList()
{
using (UserDialogs.Instance.Loading("Loading..."))
{
var task = await _classService.GetClasses();
}
}
If you are using Acr.MvvmCross.Plugins.UserDialogs see that it's depreated and you should use directly Acr.UserDialogs.
Check if you have correctly initialized it as follows:
You have to register it in App.cs of your PCL project:
Mvx.RegisterSingleton<IUserDialogs>(() => UserDialogs.Instance);
And init from the android platform project in your main activity:
UserDialogs.Init(() => Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity)
Another thing to take into account is that you should inject it in your constructor as an IUserDialogs (you can use the static Instance way but it adds more flexibility and it is more testable by injecting it):
private readonly IUserDialogs _dialogs;
public ProgressViewModel(IUserDialogs dialogs)
{
this._dialogs = dialogs;
}
and use it like
private async Task RefreshList()
{
using (this._dialogs.Loading("Loading..."))
{
try
{
var task = await this._classService.GetClasses();
}
catch(Exception exc)
{
// This is done only for debugging to check if here lies the problem
throw exc;
}
}
}
You can check if it is properly working by calling something like
public ICommand MyTestCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
// the following should should Loading for 3 seconds
using (this._dialogs.Loading("Loading..."))
{
await Task.Delay(TimeSpan.FromSeconds(3));
}
});
}
}
HIH
I dont like this approuch but it works
Device.BeginInvokeOnMainThread(async () =>
{
try
{
using (UserDialogs.Instance.Loading(("Loading...")))
{
await Task.Delay(300);
await _syncController.SyncData();
//Your Service code
}
}
catch (Exception ex)
{
var val = ex.Message;
UserDialogs.Instance.Alert("Test", val.ToString(), "Ok");
}
});
I have a requirement of compose the log message through the path taken by a code in a user click. Let me give an example:
Imagine the classical example: A user clicks in a button in a View, that calls code from the Business Layer that call code from Data Access Layer, that returns data to the Business, that return to a View.
I want to compose my log message through these layers. The caller method (in a View) that started the whole process will receive the full message. Here are some code sample just to help me explain what i am trying to achieve.
public void ViewMethod()
{
try
{
BussinessMethod();
}
catch (Exception ex)
{
Logger.Enqueue("exception occured");
Logger.Print();
}
}
public void BussinessMethod()
{
try
{
DataAcessMethod();
}
catch (Exception ex)
{
Logger.Enqueue("exception occured in the bussiness method")
}
}
public void DataAcessMethod()
{
try
{
// some code that executes an SQL command
// Log the SQL Command 1
// Log the SQL Command 2 and do on...
}
catch (Exception ex)
{
Logger.Enqueue("Error occurred, sqls executed are ...", sqlExecuted);
}
}
EDIT: The reason i am needing it is that i need to log all the SQL's executed in the whole process. If an error occurs in any point of the whole process, the user cant be warned, i need to store as much as possible information becouse the support technician will need it later.
My question is if there is any design pattern to develop it or passing a Logger reference across the "layers" are acceptable?
I would do something like this
public class Context
{
[ThreadStatic]
private static LogStore _store;
public static Log(....)
{
.....
}
}
public void ViewMethod()
{
var response = BussinessMethod();
if (response.Status = ResponseStatus.Success)
// do something with response.Data
else
// show message?
}
public BusinessMethodResponse BussinessMethod()
{
var response = new BusinessMethodResponse() {Status = ResponseStatus.Failure};
SomeData data;
try
{
data = DataAcessMethod();
}
catch (Exception ex)
{
Context.Log(....);
response.Message = "Data retrieval failed";
return response;
}
try
{
// massage the data here
response.Status = ResponseStatus.Success;
response.Data = myMassagedData;
}
catch (Exception ex)
{
Context.Log(....);
response.Message = "Something failed";
}
return response;
}
public void DataAcessMethod()
{
// some code that executes an SQL command
}
What this do? Now you can call your business objects from MVC, WPF, WinForms, Web Forms, etc...
Retry action Image
I am building a Xamarin iOS & android App, and I want to implement a retry function to all the failed webcall or in case of disconnection, I already use Polly in the BLL side, and I want to give the user the possibility to retry manually as shown on the above image.
protected List<Task> _taskList;
_taskList.Add(Task.Run(async () =>
{
try
{
**// Webservice Call**
Task<UtilisateurDTO> utilisateurTask = UserFactory.Login(username, pwd,
App.Hardware.GetDeviceId());
UtilisateurDTO utilisateur = await utilisateurTask;
if (utilisateur != null)
{
InvokeOnMainThread(() =>
{
**// Set result to ui component**
});
}
}
catch (Exception ex)
{
InvokeOnMainThread(() =>
{
// Add action button "Retry" to snackBar
_snackBar = new TTGSnackbar("ex.Message", TTGSnackbarDuration.Forever, "Retry", (obj) => {
// **Retry all tasks**
Parallel.ForEach(_taskList, task => task.Start());
});
_snackBar.Show();
});
}
}));
I know that it's not possible to retry completed tasks, and I can't call my web service outside a task (to not block the UI thread), so what's the alternative?
Update with a solution
If you want to handle exceptions and retry in one place, here is my solution (not the best cause it reload everything)
// BaseViewClass
public abstract class BaseViewController:UIViewController
{
// Function to override in child controllers
protected abstract void ReloadData(TTGSnackbar obj);
public void HandleExceptions(Exception e)
{
// On commence par cacher la ProgressBar
InvokeOnMainThread(HideLoadigProgressBar);
if (e is ConnectionLostException)
{
Console.WriteLine("ConnectionLostException: " + e.ToString());
InvokeOnMainThread(() =>
{
_snackBar = new TTGSnackbar("Connection lost !", TTGSnackbarDuration.Forever, "retry", ReloadData);
_snackBar.Show();
});
}
else if (e is TimeoutException)
{
Console.WriteLine("TimeoutException: "+ e.ToString());
InvokeOnMainThread(() =>
{
_snackBar = new TTGSnackbar("TimeoutException", TTGSnackbarDuration.Forever, "Retry", ReloadData);
_snackBar.Show();
});
}
.....................
}
// Other ViewController
public partial class HomeController : BaseViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
LoadData();
}
public void LoadData(){
Task.Run(async () =>
{
try
{
// Web calls
Task<UtilisateurDTO> getUserTask = AccueilFactory.GetUser();
UtilisateurDTO utilisateur = await getUserTask;
// Set UI result
}
catch(Exception ex) {
HandleExceptions(ex);
}
}
}
protected override void ReloadData(TTGSnackbar obj)
{
LoadData();
}
}
You have to call your function again on exception, not only rerun last task.
Your code will be like this:
private void Login()
{
try
{
LoginInner(); // here you call service and update UI
}
catch (Exception ex)
{
InvokeOnMainThread(() =>
{
// Add action button "Retry" to snackBar
_snackBar = new TTGSnackbar("ex.Message", TTGSnackbarDuration.Forever, "Retry", (obj) => {
// **Retry all tasks**
Parallel.ForEach(_taskList, LoginInner); // ** call again loginInner **
});
_snackBar.Show();
});
}
}
I have written my own ResourceHandler. ProcessRequest() works asynchrone. After updating CefSharp from 43 (WPF) to 49 (WinForms) I have some problems with IRequest.IsDisposed.
It seems that the request is disposed before my Task is started. And if the request is disposed I have no more access to the post data.
public class MySchemeHandler : IResourceHandler {
// ...
public bool ProcessRequest(IRequest request, ICallback callback) {
// copy request???
Task.Run(() => {
try {
if (request.IsDisposed == true) // Copy post data before Task.Run()???
throw new ExpressDisposedException();
// ...
// Process(request, callback);
} catch(Exception ex) {
callback.Cancel();
} finally {
callback.Dispose();
}
});
return true;
}
}
So is there a way to avoid the disposing of IRequest. Is anywhere a complete example how to make it better.