I have tried first time Azure push notifications, now after implementation, i'm get stuck on onMessage receive. OnMessage receive is not getting hit. App getting register successfully. I did not made any change in AndroidManifest file, I'm uploading the code. Few settings below:-
Package name:- pACKAGE_NAME
[assembly: Permission(Name = "pACKAGE_NAME.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "pACKAGE_NAME.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")] [assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")] [assembly: UsesPermission(Name = "android.permission.INTERNET")] [assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
namespace App1.Droid
{
[BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE },
Categories = new string[] { "pACKAGE_NAME" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK },
Categories = new string[] { "pACKAGE_NAME" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY },
Categories = new string[] { "pACKAGE_NAME" })]
public class MyBroadcastReceiver : GcmBroadcastReceiverBase<PushHandlerService>
{
public static string[] SENDER_IDS = new string[] { Constants.SenderID };
public const string TAG = "MyBroadcastReceiver-GCM";
}
[Service] // Must use the service tag
public class PushHandlerService : GcmServiceBase
{
public static string RegistrationID { get; private set; }
private NotificationHub Hub { get; set; }
public PushHandlerService() : base(Constants.SenderID)
{
Log.Info(MyBroadcastReceiver.TAG, "PushHandlerService() constructor");
}
protected override void OnRegistered(Context context, string registrationId)
{
Log.Verbose(MyBroadcastReceiver.TAG, "GCM Registered: " + registrationId);
RegistrationID = registrationId;
createNotification("PushHandlerService-GCM Registered...",
"The device has been Registered!");
Hub = new NotificationHub(Constants.NotificationHubName, Constants.ListenConnectionString,
context);
try
{
Hub.UnregisterAll(registrationId);
}
catch (Exception ex)
{
Log.Error(MyBroadcastReceiver.TAG, ex.Message);
}
//var tags = new List<string>() { "falcons" }; // create tags if you want
var tags = new List<string>() { };
try
{
var hubRegistration = Hub.Register(registrationId, tags.ToArray());
}
catch (Exception ex)
{
Log.Error(MyBroadcastReceiver.TAG, ex.Message);
}
}
protected override void OnMessage(Context context, Intent intent)
{
Log.Info(MyBroadcastReceiver.TAG, "GCM Message Received!");
var msg = new StringBuilder();
if (intent != null && intent.Extras != null)
{
foreach (var key in intent.Extras.KeySet())
msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
}
string messageText = intent.Extras.GetString("message");
if (!string.IsNullOrEmpty(messageText))
{
createNotification("New hub message!", messageText);
}
else
{
createNotification("Unknown message details", msg.ToString());
}
}
void createNotification(string title, string desc)
{
//Create notification
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
//Create an intent to show UI
var uiIntent = new Intent(this, typeof(MainActivity));
//Create the notification
var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title);
//Auto-cancel will remove the notification once the user touches it
notification.Flags = NotificationFlags.AutoCancel;
//Set the notification info
//we use the pending intent, passing our ui intent over, which will get called
//when the notification is tapped.
notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, 0));
//Show the notification
notificationManager.Notify(1, notification);
dialogNotify(title, desc);
}
protected void dialogNotify(String title, String message)
{
MainActivity.instance.RunOnUiThread(() => {
AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.instance);
AlertDialog alert = dlg.Create();
alert.SetTitle(title);
alert.SetButton("Ok", delegate {
alert.Dismiss();
});
alert.SetMessage(message);
alert.Show();
});
}
protected override void OnUnRegistered(Context context, string registrationId)
{
Log.Verbose(MyBroadcastReceiver.TAG, "GCM Unregistered: " + registrationId);
createNotification("GCM Unregistered...", "The device has been unregistered!");
}
protected override bool OnRecoverableError(Context context, string errorId)
{
Log.Warn(MyBroadcastReceiver.TAG, "Recoverable Error: " + errorId);
return base.OnRecoverableError(context, errorId);
}
protected override void OnError(Context context, string errorId)
{
Log.Error(MyBroadcastReceiver.TAG, "GCM Error: " + errorId);
}
}
}
I've prepared a XAMARIN forums sample APP with Notification Hub. Here is link: https://1drv.ms/u/s!AuKd5Hvq8SOlo8Fukh4KcX6GtIoPmQ.
Compare with your code and see if you missed anything. This App uses the Xamarin.GooglePlayServices.Gcm package version 25.0.0 (instead of latest version: 29.0.0.2).
Thanks,
Sateesh
Related
I am implementing webhook using asp.net core 3.1 webhook package. This is a custom webhook poc and I need to expose this webhook to external users. During runtime I am facing below error and unable to solve it.
What can I try next?
Error:
Unable to find WebHook filters for the 'jr4o27tr2r472' receiver. Add the required configuration by calling a receiver-specific method that calls 'Microsoft.Extensions.DependencyInjection.IMvcBuilder.AddWebHooks' or 'IMvcCoreBuilder.AddWebHooks' in the application startup code. For example, call 'IMvcCoreBuilder.AddGitHubWebHooks' to configure a minimal GitHub receiver.
When I hit this url (http://localhost:49846/api/webhooks/incoming/jr4o27tr2r472/teleported), I am getting this issue in eventviewer.
Note: I have added required webhook services as part of configurationservice method.
public static class UnicornServiceCollectionSetup
{
public static void AddUnicornServices(IServiceCollection services)
{
WebHookMetadata.Register<UnicornMetadata>(services);
services.AddSingleton<UnicornSignatureFilter>();
}
}
public static class UnicornMvcCoreBuilderExtensions
{
public static IMvcCoreBuilder AddUnicornWebHooks(this IMvcCoreBuilder builder)
{
UnicornServiceCollectionSetup.AddUnicornServices(builder.Services);
return builder.AddWebHooks();
}
}
public class UnicornMetadata : WebHookMetadata, IWebHookFilterMetadata
{
private readonly UnicornSignatureFilter _verifySignatureFilter;
public UnicornMetadata(UnicornSignatureFilter verifySignatureFilter)
: base(UnicornConstants.ReceiverName)
{
_verifySignatureFilter = verifySignatureFilter;
}
public override WebHookBodyType BodyType => WebHookBodyType.Json;
public void AddFilters(WebHookFilterMetadataContext context)
{
context.Results.Add(_verifySignatureFilter);
}
}
public class UnicornSignatureFilter : WebHookVerifySignatureFilter,
IAsyncResourceFilter
{
private readonly byte[] _secret;
public UnicornSignatureFilter(//IOptions<UnicornConfig> options,
IConfiguration configuration,
IHostingEnvironment hostingEnvironment,
ILoggerFactory loggerFactory)
: base(configuration, hostingEnvironment, loggerFactory)
{
//_secret = Encoding.UTF8.GetBytes(options.Value.SharedSecret);
_secret = Encoding.UTF8.GetBytes("secret");
}
public override string ReceiverName => UnicornConstants.ReceiverName;
public async Task OnResourceExecutionAsync(ResourceExecutingContext context,
ResourceExecutionDelegate next)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (next == null) throw new ArgumentNullException(nameof(next));
var request = context.HttpContext.Request;
if (!HttpMethods.IsPost(request.Method))
{
await next();
return;
}
var errorResult = EnsureSecureConnection(ReceiverName, request);
if (errorResult != null)
{
context.Result = errorResult;
return;
}
var header = GetRequestHeader(request,
UnicornConstants.SignatureHeaderName,
out errorResult);
if (errorResult != null)
{
context.Result = errorResult;
return;
}
byte[] payload;
using (var ms = new MemoryStream())
{
HttpRequestRewindExtensions.EnableBuffering(request);
await request.Body.CopyToAsync(ms);
payload = ms.ToArray();
request.Body.Position = 0;
}
if (payload == null || payload.Length == 0)
{
context.Result = new BadRequestObjectResult("No payload");
return;
}
var digest = FromBase64(header, UnicornConstants.SignatureHeaderName);
var secretPlusJson = _secret.Concat(payload).ToArray();
using (var sha512 = new SHA512Managed())
{
if (!SecretEqual(sha512.ComputeHash(secretPlusJson), digest))
{
context.Result =
new BadRequestObjectResult("Signature verification failed");
return;
}
}
await next();
}
}
Note: I am attaching source code in this webhookpoc.
I'm using Azure Notification Hub with Firebase Cloud Messaging and Xamarin. When I have the app in the background or in use, I can receive notifications. But when I kill the app from the Android current app list, I'm able to receive one notification but after that, I have the error "my_app stopped (close the application)". Any idea why ? I don't have error log because it happens when the app is killed.
This is my code, from the documentation
using System;
using System.Collections.Generic;
using System.Text;
using Android.App;
using Android.Content;
using Android.Util;
using WindowsAzure.Messaging;
using Gcm.Client;
[assembly: Permission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name ="com.google.android.c2dm.permission.RECEIVE")]
//GET_ACCOUNTS is needed only for Android versions 4.0.3 and below
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
namespace Waka.Droid
{
[BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE },
Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK },
Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY },
Categories = new string[] { "#PACKAGE_NAME#" })]
public class MyBroadcastReceiver : GcmBroadcastReceiverBase<PushHandlerService>
{
public static string[] SENDER_IDS = new string[] { Constants.SenderID };
public const string TAG = "MyBroadcastReceiver-GCM";
}
[Service] // Must use the service tag
public class PushHandlerService : GcmServiceBase
{
public static string RegistrationID { get; private set; }
private NotificationHub Hub { get; set; }
public PushHandlerService() : base(Constants.SenderID)
{
}
protected override void OnRegistered(Context context, string registrationId)
{
RegistrationID = registrationId;
Hub = new NotificationHub(Constants.NotificationHubName, Constants.ListenConnectionString,
context);
try
{
Hub.UnregisterAll(registrationId);
}
catch (Exception ex)
{
Log.Error(MyBroadcastReceiver.TAG, ex.Message);
}
//var tags = new List<string>() { "falcons" }; // create tags if you want
var tags = new List<string>() { };
try
{
var hubRegistration = Hub.Register(registrationId, tags.ToArray());
}
catch (Exception ex)
{
Log.Error(MyBroadcastReceiver.TAG, ex.Message);
}
}
protected override void OnMessage(Context context, Intent intent)
{
var msg = new StringBuilder();
if (intent != null && intent.Extras != null)
{
foreach (var key in intent.Extras.KeySet())
msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
}
string messageText = intent.Extras.GetString("message");
if (!string.IsNullOrEmpty(messageText))
{
createNotification("Shotgun!", messageText);
}
else
{
createNotification("Undefined", msg.ToString());
}
}
void createNotification(string title, string desc)
{
//Create notification
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
//Create an intent to show UI
var uiIntent = new Intent(this, typeof(MainActivity));
PendingIntent resultPendingIntent =
PendingIntent.GetActivity(
this,
0,
uiIntent,
0
);
Notification noti = new Notification.Builder(this)
.SetContentTitle("" + title)
.SetContentText(desc)
.SetSmallIcon(Android.Resource.Drawable.SymActionEmail)
.SetContentIntent(resultPendingIntent)
.SetDefaults(NotificationDefaults.All)
.Build();
//Show the notification
notificationManager.Notify(1, noti);
dialogNotify(title, desc);
}
protected void dialogNotify(String title, String message)
{
MainActivity.instance.RunOnUiThread(() =>
{
AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.instance);
AlertDialog alert = dlg.Create();
alert.SetTitle(title);
alert.SetButton("Ok", delegate
{
alert.Dismiss();
});
alert.SetMessage(message);
alert.Show();
});
}
protected override void OnUnRegistered(Context context, string registrationId)
{
}
protected override bool OnRecoverableError(Context context, string errorId)
{
return base.OnRecoverableError(context, errorId);
}
protected override void OnError(Context context, string errorId)
{
}
}
}
MainActivity.cs:
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Util;
using Gcm.Client;
namespace Waka.Droid
{
[Activity(Label = "Waka.Droid", Icon = "#drawable/icon", Theme = "#style/MyTheme", MainLauncher = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity instance;
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
instance = this;
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
RegisterWithGCM();
}
private void RegisterWithGCM()
{
// Check to ensure everything's set up right
GcmClient.CheckDevice(this);
GcmClient.CheckManifest(this);
// Register for push notifications
GcmClient.Register(this, Constants.SenderID);
}
}
}
I am developing a location tracking windows mobile app and I want to send my current location to azure service as a background task. Getting current location is done. Currently I show the current location in the app tile. Likewise showing location in the tile I want to send location to the azure service. But how can I send my current location to azure service continuously so rest of the devices can see the device's location.
So far my code
public sealed class BackgroundGeofenceTask : IBackgroundTask
{
//Azure Service
//private MobileServiceCollection<Location, Location> items;
//private IMobileServiceTable<Location> LocationTable = App.WayToSchool5Client.GetTable<Location>();
BackgroundTaskDeferral _deferral = null;
Accelerometer _accelerometer = null;
Geolocator _locator = new Geolocator();
public void Run(IBackgroundTaskInstance taskInstance)
{
_deferral = taskInstance.GetDeferral();
try
{
// force gps quality readings
_locator.DesiredAccuracy = PositionAccuracy.High;
taskInstance.Canceled += taskInstance_Canceled;
_accelerometer = Windows.Devices.Sensors.Accelerometer.GetDefault();
_accelerometer.ReportInterval = _accelerometer.MinimumReportInterval > 5000 ? _accelerometer.MinimumReportInterval : 5000;
_accelerometer.ReadingChanged += accelerometer_ReadingChanged;
}
catch (Exception ex)
{
// Add your chosen analytics here
System.Diagnostics.Debug.WriteLine(ex);
}
}
void taskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
_deferral.Complete();
}
async void accelerometer_ReadingChanged(Windows.Devices.Sensors.Accelerometer sender, Windows.Devices.Sensors.AccelerometerReadingChangedEventArgs args)
{
try
{
if (_locator.LocationStatus != PositionStatus.Disabled)
{
try
{
Geoposition pos = await _locator.GetGeopositionAsync();
XmlDocument xml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare150x150Text03);
var tileElements = xml.GetElementsByTagName("text");
tileElements[0].AppendChild(xml.CreateTextNode(pos.Coordinate.Point.Position.Latitude.ToString("f5")));
tileElements[1].AppendChild(xml.CreateTextNode(pos.Coordinate.Point.Position.Longitude.ToString("f5")));
tileElements[2].AppendChild(xml.CreateTextNode(pos.Coordinate.Point.Position.Altitude.ToString("f0")));
TileNotification tile = new TileNotification(xml);
TileUpdater updater = TileUpdateManager.CreateTileUpdaterForApplication();
updater.Update(tile);
//Send locationto azure
//var db = new Location { ServiceId = "sID", CurrentLatitude = pos.Coordinate.Point.Position.Longitude.ToString(), CurrentLongitude = pos.Coordinate.Point.Position.Longitude.ToString() };
//await LocationTable.InsertAsync(db);
}
catch (Exception ex)
{
if (ex.HResult != unchecked((int)0x800705b4))
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
public void Dispose()
{
if (_accelerometer != null)
{
_accelerometer.ReadingChanged -= accelerometer_ReadingChanged;
_accelerometer.ReportInterval = 0;
}
}
}
Location.cs
public class Location
{
public string ServiceId { get; set; }
public string CurrentLatitude { get; set; }
public string CurrentLongitude { get; set; }
}
Hi I am working on web api as back-end service where I am using Azure notification hub. I need to notify logged in user according to conditional business logic, in short target specific user. I extract code from this article. Everything works fine but tags is not creating nor updating. I need help. Here is my code snippet
// It returns registrationId
public async Task<OperationResult<string>> GetRegistrationIdAsync(string handle)
{
........
}
// actual device registration and tag update
public async Task<OperationResult<RegistrationOutput>> RegisterDeviceAsync(string userName, string registrationId, Platforms platform, string handle)
{
...........
registration.Tags.Add(string.Format("username : {0}", userName)); // THIS TAG IS NOT CREATING
await _hub.CreateOrUpdateRegistrationAsync(registration);
...........
}
// Send notification - target specific user
public async Task<OperationResult<bool>> Send(Platforms platform, string userName, INotificationMessage message)
{
...........
}
Just after submitting this question I tried updating tags from VS notification explorer. There I found that tags does not allowed blank spaces and my tags format in api call has spaces. These spaces are the main culprit. API call silently ignore these invalid tag formats
Here is complete working implementation. Modify according to your need
public class MyAzureNotificationHubManager
{
private Microsoft.ServiceBus.Notifications.NotificationHubClient _hub;
public MyAzureNotificationHubManager()
{
_hub = MyAzureNotificationClient.Instance.Hub;
}
private const string TAGFORMAT = "username:{0}";
public async Task<string> GetRegistrationIdAsync(string handle)
{
if (string.IsNullOrEmpty(handle))
throw new ArgumentNullException("handle could not be empty or null");
// This is requied - to make uniform handle format, otherwise could have some issue.
handle = handle.ToUpper();
string newRegistrationId = null;
// make sure there are no existing registrations for this push handle (used for iOS and Android)
var registrations = await _hub.GetRegistrationsByChannelAsync(handle, 100);
foreach (RegistrationDescription registration in registrations)
{
if (newRegistrationId == null)
{
newRegistrationId = registration.RegistrationId;
}
else
{
await _hub.DeleteRegistrationAsync(registration);
}
}
if (newRegistrationId == null)
newRegistrationId = await _hub.CreateRegistrationIdAsync();
return newRegistrationId;
}
public async Task UnRegisterDeviceAsync(string handle)
{
if (string.IsNullOrEmpty(handle))
throw new ArgumentNullException("handle could not be empty or null");
// This is requied - to make uniform handle format, otherwise could have some issue.
handle = handle.ToUpper();
// remove all registration by that handle
var registrations = await _hub.GetRegistrationsByChannelAsync(handle, 100);
foreach (RegistrationDescription registration in registrations)
{
await _hub.DeleteRegistrationAsync(registration);
}
}
public async Task RegisterDeviceAsync(string userName, string registrationId, Platforms platform, string handle)
{
if (string.IsNullOrEmpty(handle))
throw new ArgumentNullException("handle could not be empty or null");
// This is requied - to make uniform handle format, otherwise could have some issue.
handle = handle.ToUpper();
RegistrationDescription registration = null;
switch (platform)
{
case Platforms.MPNS:
registration = new MpnsRegistrationDescription(handle);
break;
case Platforms.WNS:
registration = new WindowsRegistrationDescription(handle);
break;
case Platforms.APNS:
registration = new AppleRegistrationDescription(handle);
break;
case Platforms.GCM:
registration = new GcmRegistrationDescription(handle);
break;
default:
throw new ArgumentException("Invalid device platform. It should be one of 'mpns', 'wns', 'apns' or 'gcm'");
}
registration.RegistrationId = registrationId;
// add check if user is allowed to add these tags
registration.Tags = new HashSet<string>();
registration.Tags.Add(string.Format(TAGFORMAT, userName));
// collect final registration
var result = await _hub.CreateOrUpdateRegistrationAsync(registration);
var output = new RegistrationOutput()
{
Platform = platform,
Handle = handle,
ExpirationTime = result.ExpirationTime,
RegistrationId = result.RegistrationId
};
if (result.Tags != null)
{
output.Tags = result.Tags.ToList();
}
}
public async Task<bool> Send(Platforms platform, string receiverUserName, INotificationMessage message)
{
string[] tags = new[] { string.Format(TAGFORMAT, receiverUserName) };
NotificationOutcome outcome = null;
switch (platform)
{
// Windows 8.1 / Windows Phone 8.1
case Platforms.WNS:
outcome = await _hub.SendWindowsNativeNotificationAsync(message.GetWindowsMessage(), tags);
break;
case Platforms.APNS:
outcome = await _hub.SendAppleNativeNotificationAsync(message.GetAppleMessage(), tags);
break;
case Platforms.GCM:
outcome = await _hub.SendGcmNativeNotificationAsync(message.GetAndroidMessage(), tags);
break;
}
if (outcome != null)
{
if (!((outcome.State == NotificationOutcomeState.Abandoned) || (outcome.State == NotificationOutcomeState.Unknown)))
{
return true;
}
}
return false;
}
}
public class MyAzureNotificationClient
{
// Lock synchronization object
private static object syncLock = new object();
private static MyAzureNotificationClient _instance { get; set; }
// Singleton inistance
public static MyAzureNotificationClient Instance
{
get
{
if (_instance == null)
{
lock (syncLock)
{
if (_instance == null)
{
_instance = new MyAzureNotificationClient();
}
}
}
return _instance;
}
}
public NotificationHubClient Hub { get; private set; }
private MyAzureNotificationClient()
{
Hub = Microsoft.ServiceBus.Notifications.NotificationHubClient.CreateClientFromConnectionString("<full access notification connection string>", "<notification hub name>");
}
}
public interface INotificationMessage
{
string GetAppleMessage();
string GetAndroidMessage();
string GetWindowsMessage();
}
public class SampleMessage : INotificationMessage
{
public string Message { get; set; }
public string GetAndroidMessage()
{
var notif = JsonObject.Create()
.AddProperty("data", data =>
{
data.AddProperty("message", this.Message);
});
return notif.ToJson();
}
public string GetAppleMessage()
{
// Refer - https://github.com/paultyng/FluentJson.NET
var alert = JsonObject.Create()
.AddProperty("aps", aps =>
{
aps.AddProperty("alert", this.Message ?? "Your information");
});
return alert.ToJson();
}
public string GetWindowsMessage()
{
// Refer - http://improve.dk/xmldocument-fluent-interface/
var msg = new XmlObject()
.XmlDeclaration()
.Node("toast").Within()
.Node("visual").Within()
.Node("binding").Attribute("template", "ToastText01").Within()
.Node("text").InnerText("Message here");
return msg.GetOuterXml();
}
}
I have this base class structure:
Base:
public abstract class BackgroundTask
{
protected readonly Logger Logger = LogManager.GetCurrentClassLogger();
protected virtual void Initialize()
{
// initialize database access
}
public void Run()
{
Initialize();
try
{
Execute();
// insert to database or whatever
}
catch (Exception ex)
{
Logger.ErrorException(string.Format("Error proccesing task: {0}\r\n", ToString()), ex);
Exceptions.Add(ex);
}
finally
{
TaskExecuter.Discard();
}
}
protected abstract void Execute();
public abstract override string ToString();
public IList<Exception> Exceptions = new List<Exception>();
}
Task executor:
public static class TaskExecuter
{
private static readonly ThreadLocal<IList<BackgroundTask>> TasksToExecute
= new ThreadLocal<IList<BackgroundTask>>(() => new List<BackgroundTask>());
public static void ExecuteLater(BackgroundTask task)
{
TasksToExecute.Value.Add(task);
}
public static void StartExecuting()
{
foreach (var backgroundTask in TasksToExecute.Value)
{
Task.Factory.StartNew(backgroundTask.Run);
}
}
public static void Discard()
{
TasksToExecute.Value.Clear();
TasksToExecute.Dispose();
}
}
FileTask:
public class FileTask : BackgroundTask
{
protected static string BaseFolder = #"C:\ASCII\";
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
private readonly string _folder;
private IHistoryRepository _historyRepository;
public string Folder
{
get { return _folder; }
}
public FileTask(string folder)
{
_folder = string.Format("{0}{1}", BaseFolder, folder);
}
protected override void Initialize()
{
_historyRepository = new HistoryRepository();
}
protected override void Execute()
{
// todo: Get institute that are active,
var institute = MockInstitute(); // todo: uncomment _historyRepository.FindInstituteByFolderName(Folder);
// todo: Update institute, lastupdate - [date] | [files amount] | [phonenumbers amount]
if (institute == null)
{
Logger.Warn("Not found data", Folder);
return;
}
// todo: read file get encoding | type and parse it
Task.Factory.StartNew(ReadFile);
}
private void ReadFile()
{
var list = GetFilesByFolder();
StreamReader sr = null;
try
{
Lock.EnterReadLock();
foreach (var fi in list)
{
var fileName = fi.FullName;
Logger.Info("Line: {0}:=> Content: {1}", fileName, Thread.CurrentThread.ManagedThreadId);
sr = new StreamReader(fileName, DetectEncoding(fileName));
string currentLine;
while ((currentLine = sr.ReadLine()).ReturnSuccess())
{
if (string.IsNullOrEmpty(currentLine)) continue;
Logger.Info("Line: {0}:=> Content: {1}", fileName, currentLine);
}
}
Lock.ExitReadLock();
}
finally
{
if (sr != null) sr.Dispose();
Logger.Info("Finished working" + Folder);
}
}
protected IEnumerable<FileInfo> GetFilesByFolder()
{
return Directory.GetFiles(Folder).Select(fileName => new FileInfo(fileName));
}
protected Encoding DetectEncoding(string file)
{
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
var cdet = new Ude.CharsetDetector();
cdet.Feed(fs);
cdet.DataEnd();
return cdet.With(x => x.Charset)
.Return(x => Encoding.GetEncoding(cdet.Charset),
Encoding.GetEncoding("windows-1255"));
}
}
private Institute MockInstitute()
{
return new Institute
{
FromFolderLocation = string.Format("{0}{1}", BaseFolder, Folder)
};
}
public override string ToString()
{
return string.Format("Folder: {0}", Folder);
}
}
When don't read the file every thing ok, the Log is populated and every thing runs smooth,
but when i attach the Task.Factory.StartNew(ReadFile); method i have an exception.
Exception:
Cannot access a disposed object.
Object name: 'The ThreadLocal object has been disposed.'.
How do i solve that issue? might i need to change the LocalThread logic, or what - i have been trying to handle that issue, for almost a day.
BTW: It's an MVC4 project, and C# 5.0 and i'm trying to TDD it all.
You shouldn't be calling TasksToExecute.Dispose();
there.