Push Sharp Within Asp.Net Web Service - asp.net

This is more of a general Asp.Net / .Net lifecycle question.
I'm looking at using PushSharp within a Asp.Net Web Service to send notifications using APNS.
Given the nature of PushSharp using a queue to async send messages and then event callbacks to notify of 'OnNotificationSent' / 'OnServiceException' etc.. how would this work within Asp.net?
The Web Service exposes a method that instantiates PushSharp, registers for the various callback events and queues Notification Messages.
The consumer calls the web service
Once The Web service method returns, does that method continue to receive the event callbacks or is it disposed and the events will not be called?
Thanks
for your help.

Not highly recommended in Asp.net, due to application pool interfering in the process (PushSharp author says notifications in the queue but not get sent). I have implemented this though in an Asp.net website and it works.
I have moved this to a Windows service since.
Global.asax.cs file:
using PushSharp;
using PushSharp.Core;
public class Global : System.Web.HttpApplication
{
private static PushBroker myPushBroker;
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
myPushBroker = new PushBroker();
myPushBroker.OnNotificationSent += NotificationSent;
myPushBroker.OnChannelException += ChannelException;
myPushBroker.OnServiceException += ServiceException;
myPushBroker.OnNotificationFailed += NotificationFailed;
myPushBroker.OnDeviceSubscriptionExpired += DeviceSubscriptionExpired;
myPushBroker.OnDeviceSubscriptionChanged += DeviceSubscriptionChanged;
myPushBroker.OnChannelCreated += ChannelCreated;
myPushBroker.OnChannelDestroyed += ChannelDestroyed;
HttpContext.Current.Application["MyPushBroker"] = myPushBroker;
}
//IMPLEMENT PUSHBROKER DELEGATES HERE
}
aspx.cs file (example Notifications.aspx.cs):
using PushSharp;
using PushSharp.Apple;
using PushSharp.Core;
public partial class Notifications : System.Web.UI.Page {
private PushBroker myPushBroker = HttpContext.Current.Application["MyPushBroker"] as PushBroker;
//SO I CAN SWITCH FROM DEVELOPMENT TO PRODUCTION EASILY I SET THIS IN THE DATABASE
private string pushCertificate = "";
private string certPass = "";
private bool isProduction = false;
protected void btnSendNotification_Click(object sender, EventArgs e)
{
bool hasError = false;
lblError.Text = "";
if (!string.IsNullOrEmpty(txtMessage.Text))
{
try
{
GetCertificate();
//GET DEVICE TOKENS TO SEND MESSAGES TO
//NOT THE BEST WAY TO SEND MESSAGES IF YOU HAVE HUNDREDS IF NOT THOUSANDS OF TOKENS. THAT'S WHY A WINDOWS SERVICE IS RECOMMENDED.
string storedProcUser = "sp_Token_GetAll";
string userTableName = "User_Table";
DataSet dsUser = new DataSet();
UserID = new Guid(ID.Text);
dsUser = srvData.GetDeviceToken(UserID, storedProcUser, userTableName, dataConn);
DataTable userTable = new DataTable();
userTable = dsUser.Tables[0];
if (userTable.Rows.Count != 0)
{
string p12FileName = Server.MapPath(pushCertificate); //SET IN THE GET CERTIFICATE
var appleCert = File.ReadAllBytes(p12FileName);
string p12Password = certPass;
//REGISTER SERVICE
myPushBroker.RegisterAppleService(new ApplePushChannelSettings(isProduction, appleCert, p12Password));
DataRow[] drDataRow;
drDataRow = userTable.Select();
string savedDeviceToken = "";
for (int i = 0; i < userTable.Rows.Count; i++)
{
if (drDataRow[i]["DeviceToken"] is DBNull == false)
{
savedDeviceToken = drDataRow[i]["DeviceToken"].ToString();
myPushBroker.QueueNotification(new AppleNotification()
.ForDeviceToken(savedDeviceToken)
.WithAlert(txtMessage.Text)
.WithBadge(1)
.WithSound("sound.caf"));
//NOTHING TO DO ANYMORE. CAPTURE IN THE PUSH NOTIFICATION DELEGATE OF GLOBAL ASCX FILE WHAT HAPPENED TO THE SENT MESSAGE.
}
}
}
}
catch(Exception ex)
{
}
finally
{
}
}
}
}

Check out EasyServices it allows you to easily push notifications to various push servers using PushSharp without having to take care of un-received notifications even when using ASP.NET
var _pushNotificationService = EngineContext.Current.Resolve<IPushNotificationService>();
_pushNotificationService.InsertNotification(NotificationType type, string title, string message, int subscriberId, PushPriority Priority = PushPriority.Normal);
https://easyservices.codeplex.com

Related

SqlDependency with signalR not firing dependency_OnChange consistently

Setup
•Visual Studio 2010
•IIS 8.5
•.NET Framework 4.6
•Microsoft SQL Server 2014
•AppPool Account on IIS is domain\web
I have a web page that monitors changes in a database table. I am using dependency_OnChange to monitor the database and pass the data to the user via signalR. I set a breakpoint in the dependency_OnChange method and it is only getting hit a few times out of thousands of database updates.
In web.config... I am using Integrated Security=True.
My user is a sysadmin on the sql box. (This is just for proof of concept)
In Global.asax... specifying a queuename and stopping and starting sqldependency
void Application_Start(object sender, EventArgs e)
{
var queuename = "Q_Name";
var sConn = ConfigurationManager.ConnectionStrings["singalR_ConnString"].ConnectionString;
SqlDependency.Stop(sConn, queuename);
SqlDependency.Start(sConn, queuename);
}
void Application_End(object sender, EventArgs e)
{
var queuename = "Q_Name";
var sConn = ConfigurationManager.ConnectionStrings["singalR_ConnString"].ConnectionString;
SqlDependency.Stop(sConn, queuename);
}
In code behind...
public void SendNotifications()
{
//Identify Current User and Row No
string CurrentUser = GetNTName();
string message = string.Empty;
string conStr = ConfigurationManager.ConnectionStrings["singalR_ConnString"].ConnectionString;
using (SqlConnection connection = new SqlConnection(conStr))
{
string query = "SELECT [RowNo] FROM [dbo].[Test] WHERE [User] = #User";
string SERVICE_NAME = "Serv_Name";
using (SqlCommand command = new SqlCommand(query, connection))
{
// Add parameters and set values.
command.Parameters.Add("#User", SqlDbType.VarChar).Value = CurrentUser;
//Need to clear notification object
command.Notification = null;
//Create new instance of sql dependency eventlistener (re-register for change events)
SqlDependency dependency = new SqlDependency(command, "Service=" + SERVICE_NAME + ";", 0);
//SqlDependency dependency = new SqlDependency(command);
//Attach the change event handler which is responsible for calling the same SendNotifications() method once a change occurs.
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
message = reader[0].ToString();
}
}
}
//If query returns rows, read the first result and pass that to hub method - NotifyAllClients.
NotificationsHub nHub = new NotificationsHub();
nHub.NotifyAllClients(message);
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
//Check type to make sure a data change is occurring
if (e.Type == SqlNotificationType.Change)
{
// Re-register for query notification SqlDependency Change events.
SendNotifications();
}
}
NotificationsHub.cs page...
//Create the Hub
//To create a Hub, create a class that derives from Microsoft.Aspnet.Signalr.Hub.
//Alias that can call class from javascript. - i.e. var hub = con.createHubProxy('DisplayMessage');
[HubName("DisplayMessage")]
public class NotificationsHub : Hub //Adding [:Hub] let c# know that this is a Hub
{
//In this example, a connected client can call the NotifyAllClients method, and when it does, the data received is broadcasted to all connected clients.
//Create NotifyAllClients Method
//public means accessible to other classes
//void means its not returning any data
public void NotifyAllClients(string msg)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationsHub>();
//When this method gets called, every single client has a function displayNotification() that is going to be executed
//msg is the data that is going to be displayed to all clients.
context.Clients.All.displayNotification(msg);
}
}
The first thing I would do here is refactor the Sql Dependency setup out to a stand alone method and call it from your send notification. (SoC and DRY) because if you are creating other SqlDependencies in other places they are going to trip each other up. Secondly your are creating a new NotificationsHub, You should be getting the currently active hub.
DefaultHubManager hubManager = new DefaultHubManager();
hub = hubManager.ResolveHub("NotificationsHub");
hub.NotifyAllClients(message);
There is also an older way to get the hub but I am not sure it will still work
GlobalHost.ConnectionManager.GetHubContext<NotificationsHub>()
I also have an example of a simpler version in this answer.
Polling for database changes: SqlDependency, SignalR is Good
Let me know if you have any questions.

How to solve the issue like app service will not be call on every time?

I am working on Microsoft band technology, in one of my current scenario as I am sending Sms in background (means even app is not open in foreground also its working like sending Sms successfully).
For that I created the appservice by taking with Windows Runtime Component as a template.
In that app service I wrote the code for how to connect to the band and how to create the tile and how to register the Events.
And also wrote the code in Button_Pressed event for sending the Sms to others.
After that I configured the app service in package. Manifest file like this below figure.
enter image description here
My issue is appservice will not be call on every time.it will be call on two or three times per day.
Is the problem in my code or issue of Microsoft health app?
using Microsoft.Band;
using Microsoft.Band.Notifications;
using Microsoft.Band.Tiles;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.Foundation.Collections;
using Windows.UI.Popups;
namespace AppServiceEvents
{
public sealed class MyTileEventHandler : IBackgroundTask
{
private BackgroundTaskDeferral backgroundTaskDeferral;
private AppServiceConnection appServiceConnection;
IBandClient bandClient;
//BandTile tile;
//IEnumerable<BandTile> tiles;
//Guid tileGuid;
public void Run(IBackgroundTaskInstance taskInstance)
{
//throw new NotImplementedException();
this.backgroundTaskDeferral = taskInstance.GetDeferral(); taskInstance.Canceled += OnTaskCanceled;
//await ConnectBand();
// Add handlers for tile events
BackgroundTileEventHandler.Instance.TileOpened += EventHandler_TileOpened;
BackgroundTileEventHandler.Instance.TileClosed += EventHandler_TileClosed;
BackgroundTileEventHandler.Instance.TileButtonPressed += EventHandler_TileButtonPressed;
// Set up handler for incoming app service request messages
var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
this.appServiceConnection = details.AppServiceConnection;
this.appServiceConnection.RequestReceived +=OnRequestReceived;
}
private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
//throw new NotImplementedException();
var messageDeferral = args.GetDeferral();
ValueSet response = new ValueSet();
ValueSet request = args.Request.Message;
// Decode the received message and call the appropriate handler
BackgroundTileEventHandler.Instance.HandleTileEvent(request);
// Send the response
await args.Request.SendResponseAsync(response);
messageDeferral.Complete();
}
private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
//throw new NotImplementedException();
DisconnectBand();
if (this.backgroundTaskDeferral != null)
{
this.backgroundTaskDeferral.Complete();
}
}
private void EventHandler_TileOpened(object sender, BandTileEventArgs<IBandTileOpenedEvent> e)
{
// TODO: Handle the tile opening
LogEvent(String.Format("EventHandler_TileOpened: TileId={0} Timestamp={1}", e.TileEvent.TileId, e.TileEvent.Timestamp));
// We create a Band connection when the tile is opened and keep it connected until the tile closes.
ConnectBand();
}
private void EventHandler_TileClosed(object sender, BandTileEventArgs<IBandTileClosedEvent> e)
{
// TODO: Handle the tile closing
LogEvent(String.Format("EventHandler_TileClosed: TileId={0} Timestamp={1}", e.TileEvent.TileId, e.TileEvent.Timestamp));
// Disconnect the Band now that the user has closed the tile.
DisconnectBand();
}
private void EventHandler_TileButtonPressed(object sender, BandTileEventArgs<IBandTileButtonPressedEvent> e)
{
// TODO: Handle the button push
LogEvent(String.Format("EventHandler_TileButtonPressed: TileId={0} PageId={1} ElementId={2}", e.TileEvent.TileId, e.TileEvent.PageId, e.TileEvent.ElementId));
// We should have a Band connection from the tile open event, but in case the OS unloaded our background code
// between that event and this button press event, we restore the connection here as needed.
ConnectBand();
var tileid = e.TileEvent.TileId;
SendMessage(tileid);
//await new MessageDialog("This is an background task"+tileid).ShowAsync();
}
private void ConnectBand()
{
if (this.bandClient == null)
{
// Note that we specify isBackground = true here to avoid conflicting with any foreground app connection to the Band
Task<IBandInfo[]> getBands = BandClientManager.Instance.GetBandsAsync(isBackground: true);
getBands.Wait();
IBandInfo[] pairedBands = getBands.Result;
if (pairedBands.Length == 0)
{
LogEvent("ERROR - No paired Band");
}
try
{
Task<IBandClient> connect = BandClientManager.Instance.ConnectAsync(pairedBands[0]);
connect.Wait();
this.bandClient = connect.Result;
}
catch
{
LogEvent("ERROR - Unable to connect to Band");
}
}
}
/// <summary>
/// If currently connected to the Band, then disconnect.
/// </summary>
private void DisconnectBand()
{
if (bandClient != null)
{
bandClient.Dispose();
bandClient = null;
}
}
const string LogFileName = "EventLog.txt";
/// <summary>
/// Log event strings to a text file
/// </summary>
/// <param name="eventString">String describing the event</param>
private void LogEvent(string eventString)
{
using (FileStream stream = new FileStream("EventLog.txt", FileMode.Append))
{
string outputString = String.Format("{0}: {1}\r\n", DateTime.Now, eventString);
byte[] outputASCII = Encoding.ASCII.GetBytes(outputString);
stream.Write(outputASCII, 0, outputASCII.Length);
}
}
private async void SendMessage(Guid tileGuid)
{
try
{ // Send a message to the Band for one of our tiles,
// and show it as a dialog.
await bandClient.NotificationManager.SendMessageAsync(tileGuid, "Task", "This is an AppService Task", DateTimeOffset.Now, MessageFlags.ShowDialog);
}
catch (BandException ex)
{
// handle a Band connection exception
}
}
}
}
Please tell me how to resolve the above issue and tell me how to debug the appservice in VS 2015 but not a background task.
Regards,
Pradeep

EWS Notification Hub for multiple users

I am trying to create a service to push exchange notifications to asp.net applications, eventually using SignalR.
My plan is to create a notification hub that subscribes each user as they log in to the asp application and listen for notifications for them. As a notification is received the second part of the project is to then use signalR to only send the correct notifications to each user. Once they log out or time out the notification hub will unsubscribe them.
So far I have done a little basic testing and can receive notifications in a little console app for myself with my credentials hard coded. What I am struggling with is how to do this for multiple people simultaneously. For example would I have to create separate threads of this for each user or is there a better way?
I guess regardless I am going to have to use impersonation rather than holding each users credentials right? I'm also going to have to work out a way to auto refresh the timeout for each user if they have an active session.
Below is a little code I found and have been playing with, I would be grateful for any ideas or example anyone could share of how I could best achieve this.
Many thanks
Andy
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Exchange.WebServices.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
service.Url = new Uri("https://server/EWS/Exchange.asmx");
service.Credentials = new NetworkCredential("user", "pass", "domain");
SetStreamingNotifications(service);
while (true)
{ }
}
static void SetStreamingNotifications(ExchangeService service)
{
// Subscribe to streaming notifications on the Inbox folder, and listen
// for "NewMail", "Created", and "Deleted" events.
StreamingSubscription streamingsubscription = service.SubscribeToStreamingNotifications(
new FolderId[] { WellKnownFolderName.Calendar, WellKnownFolderName.Inbox },
EventType.Created,
EventType.Modified);
StreamingSubscriptionConnection connection = new StreamingSubscriptionConnection(service, 9);
connection.AddSubscription(streamingsubscription);
// Delegate event handlers.
connection.OnNotificationEvent +=
new StreamingSubscriptionConnection.NotificationEventDelegate(OnEvent);
connection.OnSubscriptionError +=
new StreamingSubscriptionConnection.SubscriptionErrorDelegate(OnError);
connection.OnDisconnect +=
new StreamingSubscriptionConnection.SubscriptionErrorDelegate(OnDisconnect);
connection.Open();
Console.WriteLine("--------- StreamSubscription event -------");
}
static private void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
{
// Cast the sender as a StreamingSubscriptionConnection object.
StreamingSubscriptionConnection connection = (StreamingSubscriptionConnection)sender;
// Ask the user if they want to reconnect or close the subscription.
ConsoleKeyInfo cki;
Console.WriteLine("The connection to the subscription is disconnected.");
Console.WriteLine("Do you want to reconnect to the subscription? Y/N");
while (true)
{
cki = Console.ReadKey(true);
{
if (cki.Key == ConsoleKey.Y)
{
connection.Open();
Console.WriteLine("Connection open.");
break;
}
else if (cki.Key == ConsoleKey.N)
{
// The ReadKey in the Main() consumes the E.
Console.WriteLine("\n\nPress E to exit");
break;
}
}
}
}
static void OnEvent(object sender, NotificationEventArgs args)
{
StreamingSubscription subscription = args.Subscription;
// Loop through all item-related events.
foreach (NotificationEvent notification in args.Events)
{
switch (notification.EventType)
{
case EventType.NewMail:
Console.WriteLine("\n-------------Mail created:-------------");
break;
case EventType.Created:
Console.WriteLine("\n-------------Item or folder created:-------------");
break;
case EventType.Deleted:
Console.WriteLine("\n-------------Item or folder deleted:-------------");
break;
}
// Display the notification identifier.
if (notification is ItemEvent)
{
// The NotificationEvent for an e-mail message is an ItemEvent.
ItemEvent itemEvent = (ItemEvent)notification;
Console.WriteLine("\nItemId: " + itemEvent.ItemId.UniqueId);
}
else
{
// The NotificationEvent for a folder is an FolderEvent.
//FolderEvent folderEvent = (FolderEvent)notification;
//Console.WriteLine("\nFolderId: " + folderEvent.FolderId.UniqueId);
}
}
}
static void OnError(object sender, SubscriptionErrorEventArgs args)
{
// Handle error conditions.
Exception e = args.Exception;
Console.WriteLine("\n-------------Error ---" + e.Message + "-------------");
}
}
}
The way I solved this problem is by:
Having an account that has the right to impersonate all users.
I create a service for that account with giving a username and
password.
I impersonate a user and add the subscription of the user to the
connection
I create another service which is a close for the main service with
the same username and password, which will impersonate another user
and then add the subscription to the same connection before
Here are two parts of my code . Forget about the LogDevice it is just something internally.
The first part is the detailed impersonation and adding the service to the list of services
the list of services in my case is a dictionary with the userSMTP is the key , the UserSMTP key here is the impersonated account.
/// <summary>
/// Impersonate one user at a time and without using the autodiscovery method to find the proper url for the userSmtp,
/// and copy the provided url to the usersmtp.
/// </summary>
/// <param name="url"> </param>
/// <param name="userSmtp">user smtp</param>
/// <param name="enableTrace">to enable logging from the XML tracing </param>
/// <param name="exchangeVersion">Exchange server version used</param>
private Uri ImpersonateUser(Uri url, string userSmtp, bool enableTrace, ExchangeVersion exchangeVersion)
{
Uri result = url;
var log = "ImpersonateUser \n";
try
{
log += "0/8 Checking services redundancy\n";
if (Services.ContainsKey(userSmtp))
{
Services.Remove(userSmtp);
}
log += "1/8 Create a new service for " + userSmtp + "\n";
var service = new ExchangeService(exchangeVersion);
log += "2/8 Get credentials for the service\n";
var serviceCred = ((System.Net.NetworkCredential)(((WebCredentials)(Services.First().Value.Credentials)).Credentials));
log += "3/8 Assign credentials to the new service\n";
service.Credentials = new WebCredentials(serviceCred.UserName, serviceCred.Password);
log += "4/8 TraceEnabled is" + enableTrace.ToString() + "\n";
service.TraceEnabled = enableTrace;
log += "5/8 Get the Url for the service with AutodiscoverUrl \n";
service.Url = url;
log += "6/8 Assign a new ImpersonatedUserId to the new service for" + userSmtp + "\n";
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, userSmtp);
try
{
log += "7/8 Validating the impersonation\n";
RuleCollection rulecoll = service.GetInboxRules();
}
catch (Exception ex)
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUser: failed to validate the impersonation for {0}\n Exception: {1}\n", userSmtp, ex.Message);
int hr = System.Runtime.InteropServices.Marshal.GetHRForException(ex);
if (hr == -2146233088) // We do not have right to impersonate this user.
{
result = null;
return result;
}
else
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUser(2): trying to resolve {0} with Autodiscover instead...", userSmtp);
result = ImpersonateUser(userSmtp, enableTrace, exchangeVersion);
}
}
log += "8/8 Adding the service \n";
if (!Services.ContainsKey(userSmtp))
{
Services.Add(userSmtp, service);
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUser(2): {0} has been impersonated\n", service.ImpersonatedUserId.Id);
}
}
catch (Exception ex)
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUser(2): exception {0}\n The exception occured after the following steps: \n{1}", ex.Message, log);
}
return result;
}
And here is the code that calls the previous function (i.e. for all users) put in mind that you should somehow storing the email address for every account you want to impersonate.
/// <summary>
/// To Impersonate users in order to get the info from them.
/// </summary>
/// <param name="userSmtps">List of users to be impersonated</param>
/// <param name="enableTrace"> To enable logging from the XML tracing</param>
/// <param name="exchangeVersion">Exchange server version used </param>
public void ImpersonateUsers(ICollection<string> userSmtps)
{
var log = "ImpersonateUsers\n";
var firstUserSmtp = "";
if (userSmtps != null)
if (userSmtps.Count > 0)
{
//the url for the first smtp
try
{
log += "1/2 Impersonating the first userSmtp\n";
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUsers: Getting the Url from the autodiscovery for the first smtp {0} ", userSmtps.First());
bool enableTrace = Services.First().Value.TraceEnabled;
ExchangeVersion exchangeVersion = Services.First().Value.RequestedServerVersion;
firstUserSmtp = userSmtps.First();
var commonSmtpUrl = ImpersonateUser(userSmtps.First(), enableTrace, exchangeVersion);
if (commonSmtpUrl == null) userSmtps.Remove(firstUserSmtp);
// If the list contains other than the first one
log += "2/2 Impersonating " + (userSmtps.Count - 1) + " userSmtps\n";
if (userSmtps.Count >= 1)
{
foreach (var userSmtp in userSmtps)
{
try
{ //skip ther first one because it is already impersonated.
if (userSmtp == firstUserSmtp)
{
continue;
}
// Impersonate the users
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUsers: Impersonating {0} ...", userSmtp);
commonSmtpUrl = ImpersonateUser(userSmtp, enableTrace, exchangeVersion);
}
catch (Exception ex)
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUsers: Impersonating {1}\n exception {0}\n", ex.Message, userSmtp);
}
}
}
}
catch (Exception ex)
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUsers: exception {0}\n The exception occured after the following steps: \n{1}", ex.Message, log);
}
}
}
I would have put the subscription part and adding to the connection , but it is a bit ugly and hard to get. but the idea is simply that you should have a connection, and then you go to each service you made and then `connection.AddSubscription(streamingSubscription);
Where streamingSubscription is extracted from the service.

Getting a status update from WCF to an asp.net page

I'm wondering if I can access a static variable in a WCF service for status updates and how to do this if you can?
I have a long process that updates 1000s of products. This process is fired off from an asp.net page and the user wants status updates from the service as the process runs, i.e. 'processing product 123'. I have the service and I have a timer control inside an update panel that I want to use to poll the service for status updates. I was thinking as the timer control posts back, I'd query the service using a GetStatus method to get the current status but I think the status message would always be the same because a new instance of the service would be created on Page_Load. So, how can I get a running status variable from a WCF service to an asp.net page?
protected void Timer1_Tick(object sender, EventArgs e)
{
if (statusMessage == null)
{
// feed operation finished or timer is running invalid
Timer1.Enabled = false;
}
try
{
UpdateStatusDisplay();
}
catch (Exception exp)
{
....
}
}
In my WCF service
public string Status = "";
public void BeginImport(ImportOptions options)
{
_options = options;
string conString = ConfigurationManager.ConnectionStrings["String1"].ConnectionString;
using (var con = new SqlConnection(conString))
{
con.Open();
var products = con.Query("SELECT * FROM PRODUCTS");
foreach (var product in products)
{
Status = "Processing product " + product.ProductName);
}
}
}
public string GetStatus()
{
return Status;
}
EDIT:
I just read about [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)], is this all I'd need to do?

Async calls in WP7

I have been experimenting with WP7 apps today and have hit a bit of a wall.
I like to have seperation between the UI and the main app code but Ive hit a wall.
I have succesfully implemented a webclient request and gotten a result, but because the call is async I dont know how to pass this backup to the UI level. I cannot seem to hack in a wait for response to complete or anything.
I must be doing something wrong.
(this is the xbox360Voice library that I have for download on my website: http://www.jamesstuddart.co.uk/Projects/ASP.Net/Xbox_Feeds/ which I am porting to WP7 as a test)
here is the backend code snippet:
internal const string BaseUrlFormat = "http://www.360voice.com/api/gamertag-profile.asp?tag={0}";
internal static string ResponseXml { get; set; }
internal static WebClient Client = new WebClient();
public static XboxGamer? GetGamer(string gamerTag)
{
var url = string.Format(BaseUrlFormat, gamerTag);
var response = GetResponse(url, null, null);
return SerializeResponse(response);
}
internal static XboxGamer? SerializeResponse(string response)
{
if (string.IsNullOrEmpty(response))
{
return null;
}
var tempGamer = new XboxGamer();
var gamer = (XboxGamer)SerializationMethods.Deserialize(tempGamer, response);
return gamer;
}
internal static string GetResponse(string url, string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
Client.Credentials = new NetworkCredential(userName, password);
}
try
{
Client.DownloadStringCompleted += ClientDownloadStringCompleted;
Client.DownloadStringAsync(new Uri(url));
return ResponseXml;
}
catch (Exception ex)
{
return null;
}
}
internal static void ClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
ResponseXml = e.Result;
}
}
and this is the front end code:
public void GetGamerDetails()
{
var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");
var xboxGamer = xboxManager.GetGamer();
if (xboxGamer.HasValue)
{
var profile = xboxGamer.Value.Profile[0];
imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));
txtUserName.Text = profile.GamerTag;
txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");
txtZone.Text = profile.PlayerZone;
}
else
{
txtUserName.Text = "Failed to load data";
}
}
Now I understand I need to place something in ClientDownloadStringCompleted but I am unsure what.
The problem you have is that as soon as an asynchronous operation is introduced in to the code path the entire code path needs to become asynchronous.
Because GetResponse calls DownloadStringAsync it must become asynchronous, it can't return a string, it can only do that on a callback
Because GetGamer calls GetResponse which is now asynchronous it can't return a XboxGamer, it can only do that on a callback
Because GetGamerDetails calls GetGamer which is now asynchronous it can't continue with its code following the call, it can only do that after it has received a call back from GetGamer.
Because GetGamerDetails is now asynchronous anything call it must also acknowledge this behaviour.
.... this continues all the way up to the top of the chain where a user event will have occured.
Here is some air code that knocks some asynchronicity in to the code.
public static void GetGamer(string gamerTag, Action<XboxGamer?> completed)
{
var url = string.Format(BaseUrlFormat, gamerTag);
var response = GetResponse(url, null, null, (response) =>
{
completed(SerializeResponse(response));
});
}
internal static string GetResponse(string url, string userName, string password, Action<string> completed)
{
WebClient client = new WebClient();
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
client.Credentials = new NetworkCredential(userName, password);
}
try
{
client.DownloadStringCompleted += (s, args) =>
{
// Messy error handling needed here, out of scope
completed(args.Result);
};
client.DownloadStringAsync(new Uri(url));
}
catch
{
completed(null);
}
}
public void GetGamerDetails()
{
var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");
xboxManager.GetGamer( (xboxGamer) =>
{
// Need to move to the main UI thread.
Dispatcher.BeginInvoke(new Action<XboxGamer?>(DisplayGamerDetails), xboxGamer);
});
}
void DisplayGamerDetails(XboxGamer? xboxGamer)
{
if (xboxGamer.HasValue)
{
var profile = xboxGamer.Value.Profile[0];
imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));
txtUserName.Text = profile.GamerTag;
txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");
txtZone.Text = profile.PlayerZone;
}
else
{
txtUserName.Text = "Failed to load data";
}
}
As you can see async programming can get realy messy.
You generally have 2 options. Either you expose your backend code as an async API as well, or you need to wait for the call to complete in GetResponse.
Doing it the async way would mean starting the process one place, then return, and have the UI update when data is available. This is generally the preferred way, since calling a blocking method on the UI thread will make your app seem unresponsive as long as the method is running.
I think the "Silverlight Way" would be to use databinding. Your XboxGamer object should implement the INotifyPropertyChanged interface. When you call GetGamer() it returns immediately with an "empty" XboxGamer object (maybe with GamerTag=="Loading..." or something). In your ClientDownloadStringCompleted handler you should deserialize the returned XML and then fire the INotifyPropertyChanged.PropertyChanged event.
If you look at the "Windows Phone Databound Application" project template in the SDK, the ItemViewModel class is implemented this way.
Here is how you can expose asynchronous features to any type on WP7.

Resources