Owin SignalR WebApi Self Hosted
Table of contents |
---|
Introduction |
Source Code |
Project Outline |
Setting up the project |
OwinSignalR.Data |
OwinSignalR.Pulse |
OwinSignalR.Client |
OwinSignalR.Notificator |
Introduction
What is the Problem? – You are responsible for multiple applications that need to show real time notifications, show real time graphs that update as the data becomes available, update dashboards as live events happen via the backend processes. You also need to keep track of all the connected clients (users) and authenticate the connections.
The solution you came up with (if you are already using SignalR)? – Implement SignalR in all of your applications (Hubs that your JavaScript client connects to and waits for a callback/notification). You have multiple code bases that implement the same functionality; that is notifying connected clients (users) that a specific event took place, and passing on the data for that event.
The solution that I propose? – Implement one standalone/isolated SignalR project/solution that manages all your clients (users) connections, knows who is connected at all time, receives notifications from your backend process and forwards on the event(s) to the intended connected client(s) (user(s)). This standalone/isolated project/solution will also authenticate all your connected clients via unique API tokens and authorized referral URLS (allowed domains).
Here is a graph of how the Website/Web Application connects and receives data.
Source Code
You can download the code here
Project Outline
The solution consists of the following projects:
- OwinSignalR.Data
- OwinSignalR.Pulse
- OwinSignalR.Common
- OwinSignalR.PulseHost
- OwinSignalR.Notificator
- OwinSignalR.Client
OwinSignalR.Data
This project contains the data layer and database access code
OwinSignalR.Pulse
This project receives the SignalR connections, and also receives the data that is sent from any backend process or backend service.
OwinSignalR.Common
This project contains common code used by the other projects. It also contains the code used to connect to the OwinSignalR.Pulse project from any backend process or backend service.
OwinSignalR.PulseHost
This is a console application that hosts the OwinSignalR.Pulse project.
OwinSignalR.Notificator
This project acts as a backend service/process that is consintly sending data to the Website/WebApplication
OwinSignalR.Client
This is a demo Website/Webapplication that connects to OwinSignalR.Pulse and receives constant data updates from OwinSignalR.Notificator.
Setting up the project:
- Step1: Download and build the application to resolve the packages
- Step2: Run the CreateDatabase.sql script found in the root on your SQL server instance. This will create the database structure and will also insert a test application with the relevant information
- Step3: Right click on the solution and select “Multiple startup projects”, it should look similar to this:
- Step4: Expand the OwinSignalR.Client project and set Demo1.html as the startup page
- Step5: Run the solution
2 console applications should start up and 1 website. After the projects start you should see the following (the line below “OwinSignalR…” is a unique id for the SignalR connection. Yours will probably be different)
If you don’t see a connection Id in the OwinSignalR.PulseHost console window, please refresh the demo.html page (f5 or ctr+f5)
When you see the unique Id in the first console window the demo.html page connected successfully with the OwinSignalR.PulseHost. You can now hit the enter key in the OwinSignalR.Notificator console window to start sending the notifications to the demo.html page. The gauges should start moving and the”Activity feed” should start receiving notifications of events as they happen.
Let’s discuss the projects:
OwinSignalR.Data
OwinSignalR.Data /Configuration/AutomapperConfiguration.cs
This file contains the mapping code to map EntityFrOwinSignalR.Pulseamework Models to DTO’s
public static class AutomapperConfiguration { public static void Configure() { Mapper.CreateMap<Application, ApplicationDto>() .ForMember(dest => dest.ApplicationId , opt => opt.MapFrom(src => src.ApplicationId)) .ForMember(dest => dest.ApplicationName , opt => opt.MapFrom(src => src.ApplicationName)) .ForMember(dest => dest.ApiToken , opt => opt.MapFrom(src => src.ApiToken)) .ForMember(dest => dest.ApplicationSecret , opt => opt.MapFrom(src => src.ApplicationSecret)) .ForMember(dest => dest.ApplicationReferralUrls, opt => opt.MapFrom(src => src.ApplicationReferralUrls)); Mapper.CreateMap<ApplicationReferralUrl, ApplicationReferralUrlDto>() .ForMember(dest => dest.ApplicationReferralUrlId, opt => opt.MapFrom(src => src.ApplicationReferralUrlId)) .ForMember(dest => dest.ApplicationId , opt => opt.MapFrom(src => src.ApplicationId)) .ForMember(dest => dest.Url , opt => opt.MapFrom(src => src.Url)); } }
OwinSignalR.Data/DataAccessors/ApplicationDataAccessor.cs
This file contains the data access code that fetches the data from the database.
public interface IApplicationDataAccessor { Application FetchApplication(string apiToken); } public class ApplicationDataAccessor : DataAccessorBase, IApplicationDataAccessor { public Application FetchApplication( string apiToken) { var query = (from a in OwinSignalrDbContext.Applications where a.ApiToken == apiToken select a).Include("ApplicationReferralUrls"); return query.FirstOrDefault(); } }
OwinSignalR.Data/DataAccessors/DataAccessorBase.cs
This is the base class from where all DataAccessors enherit from to access the DbContext
public class DataAccessorBase { protected IDataContext DataContext { get { return ObjectFactory.GetInstance<IDataContext>(); } } protected IOwinSignalrDbContext OwinSignalrDbContext { get { return DataContext.OwinSignalrDbContext; } } }
OwinSignalR.Data/Models/IDataContext.cs
This file contains the IDataContext interface and the implementation for it. This interface is used when we need to access the IOwinSignalrDbContext interface.
public interface IDataContext { IOwinSignalrDbContext OwinSignalrDbContext { get; } void Initialize(IOwinSignalrDbContext owinSignalrDbContext); } public class DataContext : IDataContext { #region Private Members private IOwinSignalrDbContext _owinSignalrDbContext; #endregion public IOwinSignalrDbContext OwinSignalrDbContext { get { return _owinSignalrDbContext; } } public void Initialize( IOwinSignalrDbContext owinSignalrDbContext) { _owinSignalrDbContext = owinSignalrDbContext; } }
OwinSignalR.Data/Models/IOwinSignalrDbContext.cs
This file contains the IOwinSignalrDbContext interface and the implementation for it. This interface is used when we need to access the EntityFramework DbContext
public interface IOwinSignalrDbContext { DbSet<Application> Applications { get; set; } DbSet<ApplicationReferralUrl> ApplicationReferralUrls { get; set; } } public partial class OwinSignalrDbContext : IOwinSignalrDbContext { }
OwinSignalR.Data/Models/OwinSignalrDbContext.edmx
This file is the EntityFramework edmx that is generated from the database. You might have noticed that the OwinSignalrDbContext is marked as partial, the EntityFramework edmx contains the other partial class that actually implements the rest of IOwinSignalrDbContext
OwinSignalR.Data/Services/ApplicationService.cs
This service is used by all referencing projects to fetch Application related information.
public interface IApplicationService { ApplicationDto FetchApplication(string apiToken); } public class ApplicationService : IApplicationService { public IApplicationDataAccessor ApplicationDataAccessor { get; set; } public ApplicationDto FetchApplication( string apiToken) { return Mapper.Map<ApplicationDto>(ApplicationDataAccessor.FetchApplication(apiToken)); } }
OwinSignalR.Pulse
OwinSignalR.Pulse/Attributes/ApiAuthorizeAttribute.cs
This file contains code to check if the API call contains a valid API Token, Application secret or if it is coming from one of the predefined URL's
public class ApiAuthorizeAttribute : AuthorizeAttribute { public IApplicationService ApplicationService { get; set; } public ApiAuthorizeAttribute() { StructureMap.ObjectFactory.BuildUp(this); } protected override bool IsAuthorized( HttpActionContext actionContext) { var queryString = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value); string token; string applicationSecret; if (!queryString.TryGetValue(Constants.TOKEN_QUERYSTRING, out token)) { return false; } if (!queryString.TryGetValue(Constants.APPLICATION_SECRET, out applicationSecret)) { return false; } if (String.IsNullOrEmpty(applicationSecret)) { if (!IsValidReferer(token, actionContext)) { return false; } } else if (!IsApplicationSecretValidForToken(token, applicationSecret)) { return false; } return true; } private bool IsApplicationSecretValidForToken( string token , string applicationSecret) { var application = ApplicationService.FetchApplication(token); if (application == null) { return false; } return application.ApplicationSecret == applicationSecret; } private bool IsValidReferer( string token , HttpActionContext actionContext) { var application = ApplicationService.FetchApplication(token); if (application == null) { return false; } IEnumerable<string> headerValue; if (!actionContext.Request.Headers.TryGetValues("Referer", out headerValue)) { return false; } else { var urlReferral = Helpers.HttpHelper.GetUrlReferer(headerValue.ElementAt(0)); return application.ApplicationReferralUrls.Any(x => x.Url == urlReferral); } } }
OwinSignalR.Pulse/Attributes/HubAuthorizeAttribute.cs
This file contains code to check if the Hub conenction contains a valid API Token and if it is coming from one of the predefined URL's
public class HubAuthorizeAttribute : AuthorizeAttribute { public IApplicationService ApplicationService { get; set; } public HubAuthorizeAttribute() { StructureMap.ObjectFactory.BuildUp(this); } public override bool AuthorizeHubConnection( Microsoft.AspNet.SignalR.Hubs.HubDescriptor hubDescriptor , IRequest request) { var authorizeHubConnection = base.AuthorizeHubConnection(hubDescriptor, request); var tokenValues = request.QueryString.GetValues(Constants.TOKEN_QUERYSTRING); var clientIdValues = request.QueryString.GetValues(Constants.UNIQUE_CLIENT_ID); if ((tokenValues.Count() == 0 || tokenValues.Count() > 1) || (clientIdValues.Count() == 0 || clientIdValues.Count() > 1)) { return false; } var application = ApplicationService.FetchApplication(tokenValues.ElementAt(0)); if (application == null) { return false; } var urlReferer = GetUrlReferer(request); return application.ApplicationReferralUrls.Any(x => x.Url == urlReferer); } protected override bool UserAuthorized( System.Security.Principal.IPrincipal user) { return true; } private string GetUrlReferer( IRequest request) { return Helpers.HttpHelper.GetUrlReferer(request.Headers["Referer"]); } }
OwinSignalR.Pulse/Attributes/DependencyInjectionConfiguration.cs
This file contains the setup code for the Dependency Injection used throughout the application
public static class DependencyInjectionConfiguration { public static void Configure() { ObjectFactory.Initialize(x => { x.For<Data.Models.IDataContext>().HybridHttpOrThreadLocalScoped().Use<Data.Models.DataContext>(); x.For<Data.Services.IApplicationService>().Use<Data.Services.ApplicationService>(); x.For<Data.DataAccessors.IApplicationDataAccessor>().Use<Data.DataAccessors.ApplicationDataAccessor>(); x.SetAllProperties(y => { y.OfType<Data.Services.IApplicationService>(); y.OfType<Data.DataAccessors.IApplicationDataAccessor>(); }); }); } }
OwinSignalR.Pulse/Helpers/HttpHelper.cs
This file contains code to easily extract the URL referrer (domain from where the request is coming from)
public static class HttpHelper { public static string GetUrlReferer( string referer) { if (string.IsNullOrEmpty(referer)) { return string.Empty; } referer = referer.IndexOf("://") > -1 ? referer.Substring(referer.IndexOf("://") + 3) : referer; referer = referer.IndexOf("/") > -1 ? referer.Substring(0, referer.IndexOf("/") - 1) : referer; referer = referer.IndexOf(":") > -1 ? referer.Split(':')[0] : referer; return referer; } }
OwinSignalR.Pulse/ConnectionManager.cs
This file contains code to keep track of all the connections made via SignalR
public class ConnectionManager { #region Private Members private static ConcurrentDictionary<Tuple<string, string>, ClientConnection> _clients; #endregion #region Public Properties public static ClientConnection ActiveConnections( string token , string clientId) { if (_clients == null) { return new ClientConnection(); } else { ClientConnection value; if (_clients.TryGetValue(new Tuple<string, string>(token, clientId), out value)) { return value; } else { return new ClientConnection(); } } } #endregion #region Static Methods public static void Add( string token , string clientId , string connectionId) { if (_clients == null) { _clients = new ConcurrentDictionary<Tuple<string, string>, ClientConnection>(); } var user = _clients.GetOrAdd(new Tuple<string, string>(token, clientId), new ClientConnection { Connections = new List<ClientConnectionDetials>() }); lock (user.Connections) { user.Connections.Add(new ClientConnectionDetials { ConnectionId = connectionId }); } } public static void Remove( string token , string clientId , string connectionId) { ClientConnection clientConnection; _clients.TryGetValue(new Tuple<string, string>(token, clientId), out clientConnection); if (clientConnection != null) { lock (clientConnection.Connections) { clientConnection.Connections.RemoveAll(x => x.ConnectionId == connectionId); if (!clientConnection.Connections.Any()) { ClientConnection removedClient; _clients.TryRemove(new Tuple<string, string>(token, clientId), out removedClient); } } } } public static void ClearConnections() { lock (_clients) { _clients = new ConcurrentDictionary<Tuple<string, string>, ClientConnection>(); } } #endregion public class ClientConnection { public List<ClientConnectionDetials> Connections { get; set; } public ClientConnection() { Connections = new List<ClientConnectionDetials>(); } } public class ClientConnectionDetials { public string ConnectionId { get; set; } } }
OwinSignalR.Pulse/Host.cs
This file contains the code that starts up the WebApp
public class Host { private static IDisposable _webServer; public static void Start() { var url = System.Configuration.ConfigurationManager.AppSettings["PulseUrl"]; _webServer = WebApp.Start(url); Console.WriteLine(String.Format("OwinSignalR notifications now running on {0}", url)); } public static void Stop() { if (_webServer != null) { _webServer.Dispose(); } } }
OwinSignalR.Pulse/PulseController.cs
Handles all the Api requests sent to the OwinSignalR.Pulse project
[ApiAuthorize] public class PulseController : ApiController { public IApplicationService ApplicationService { get; set; } public PulseController() { StructureMap.ObjectFactory.BuildUp(this); } [HttpPost] public IHttpActionResult Connect( string token , string clientId , string method , [System.Web.Http.FromBody]object value , string applicationSecret = "") { try { InvokeClientHubMethod(token, clientId, method, value); return Ok(); } catch (Exception excp) { Startup.Logger.Error("Error on PulseController.Connect", excp); return InternalServerError(excp); } } #region Private Methods private void InvokeClientHubMethod( string token , string clientId , string method , params object[] args) { var context = Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<Pulse.PulseHub>(); IClientProxy clientProxy; if (String.IsNullOrEmpty(clientId)) { clientProxy = context.Clients.All; } else { clientProxy = context.Clients.Clients(ConnectionManager.ActiveConnections(token, clientId).Connections.Select(x => x.ConnectionId).ToList()); } clientProxy.Invoke(method, args); } #endregion }
OwinSignalR.Pulse/PulseHub.cs
Handles all the SignalR request/connections
[HubAuthorize] public class PulseHub : Hub { #regionPrivate Properties private string Token { get { var token = Context.QueryString[Constants.TOKEN_QUERYSTRING]; if (String.IsNullOrEmpty(token)) { throw new UnauthorizedAccessException("Token not specified"); } return token; } } private string ClientId { get { var clientId = Context.QueryString[Common.Constants.UNIQUE_CLIENT_ID]; if (String.IsNullOrEmpty(clientId)) { throw new UnauthorizedAccessException("ClientId not specified"); } return clientId; } } #endregion #region IHub Interface public override System.Threading.Tasks.Task OnConnected() { try { Console.WriteLine(Context.ConnectionId); #if DEBUG Startup.Logger.Info(String.Format("OnConnected: Token {0} ClientId {1}", Token, ClientId)); #endif ConnectionManager.Add(Token, ClientId, Context.ConnectionId); } catch (Exception excp) { Startup.Logger.Error(excp.Message); } return base.OnConnected(); } public override System.Threading.Tasks.Task OnDisconnected( bool stopCalled) { try { #if DEBUG Startup.Logger.Info(String.Format("SignalR OnDisconnected: Token {0} ClientId {1}", Token, ClientId)); #endif ConnectionManager.Remove(Token, ClientId, Context.ConnectionId); } catch (Exception excp) { Startup.Logger.Error(excp.Message); } return base.OnDisconnected(stopCalled); } #endregion }
OwinSignalR.Pulse/Startup.cs
This file contains all the startup code required for the OwinSignalR.Pulse project. It registers the WebApi Controllers and the SignalR Hubs.
public class Startup { #region Private Members private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); #endregion #region Public Properties public static log4net.ILog Logger { get { return _log; } } #endregion public void Configuration( IAppBuilder app) { Pulse.Configuration.DependencyInjectionConfiguration.Configure(); OwinSignalR.Data.Configuration.AutomapperConfiguration.Configure(); log4net.Config.XmlConfigurator.Configure(); RegsiterHubs(); app.UseCors(CorsOptions.AllowAll); app.Use<StructureMapOWINMiddleware>(); app.MapSignalR(); HttpConfiguration config = new HttpConfiguration(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); app.UseWebApi(config); } #region Private Methods private void RegsiterHubs() { GlobalHost.DependencyResolver.Register(typeof(PulseHub), () => new PulseHub()); } private void SetJsonFormatting( HttpConfiguration config) { config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false; } #endregion }
OwinSignalR.Pulse/Structuremap.Owin.cs
OWIN middleware that sets up the EntityFramework DbContext per request.
public class StructureMapOWINMiddleware : OwinMiddleware { public StructureMapOWINMiddleware( OwinMiddleware next) : base(next) { } public async override Task Invoke(IOwinContext context) { StructureMap.ObjectFactory.GetInstance<IDataContext>().Initialize(new Data.Models.OwinSignalrDbContext()); await Next.Invoke(context); } }
OwinSignalR.Client
OwinSignalR.Client/Content/bootstrap-theme.min.cs
This is the bootstrap theme css file
OwinSignalR.Client/Content/bootstrap.min.cs
This is the bootstrap library css file
OwinSignalR.Client/Scripts/bootstrap.min.js
This is the bootstrap library javascript file
OwinSignalR.Client/Scripts/jquery-2.2.0.min.js
This is the jquery library javascript file
OwinSignalR.Client/Scripts/jquery.signalR-2.2.0.min.js
This is the SignalR library javascript file
OwinSignalR.Client/Scripts/OwinSignalR.js
This is the main javascript file to connect to the PulseHub
function connect(token, clientId, callbacks) { $.connection.hub.stop(); $.connection.hub.qs = { 'Token': token, 'ClientId': clientId }; $.connection.hub.url = signalrRoot + '/signalr'; var pulseHub = $.connection.pulseHub; if (pulseHub !== null && pulseHub !== undefined) { for (var i = 0; i < callbacks.length; i++) { for (var method in callbacks[i]) { pulseHub.client[method] = callbacks[i][method]; } } } $.connection.hub.start({ transport: 'longPolling' }); }
OwinSignalR.Client/Demo.html
This is the demo html page
<head> <title></title> <meta charset="utf-8" /> <link href="Content/bootstrap.min.css" rel="stylesheet" /> <link href="Content/bootstrap-theme.min.css" rel="stylesheet" /> <script src="Scripts/jquery-2.2.0.min.js"></script> <script src="Scripts/jquery.signalR-2.2.0.min.js"></script> <script src="http://localhost:8014/signalr/hubs"></script> <script> var signalrRoot = 'http://localhost:8014'; </script> <script src="Scripts/OwinSignalR.js"></script> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script type="text/javascript"> google.charts.load('current', { 'packages': ['gauge'] }); google.charts.setOnLoadCallback(drawChart); var chart; var data; var options; function drawChart() { data = google.visualization.arrayToDataTable([ ['Label', 'Value'], ['Memory', 80], ['CPU', 55], ['Network', 68] ]); options = { width: 200, height: 700, redFrom: 90, redTo: 100, yellowFrom: 75, yellowTo: 90, minorTicks: 5 }; chart = new google.visualization.Gauge(document.getElementById('chart_div')); chart.draw(data, options); } </script> <script type="text/javascript"> connect('48a2000467394b008938cc77b4529e3a', 'testUser', [{ testCallBack: function (args) { $('#activityFeedMessage').hide(); $('#activityFeed').prepend('<li class="list-group-item"><label class="label label-success">' + args.createDate + '</label><br />' + args.message + '</li>'); }, serverLoadNetwork: function (args) { data.setValue(2, 1, args); chart.draw(data, options); }, serverLoadCPU: function (args) { data.setValue(1, 1, args); chart.draw(data, options); }, serverLoadMemory: function (args) { data.setValue(0, 1, args); chart.draw(data, options); } }]); </script> <style type="text/css"> .list-group { max-height: 500px; overflow-y: scroll; } </style> </head> <body> <nav class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Demo</a></li> </ul> </div> </div> </nav> <div class="container"> <div class="row"> <div class="col-sm-offset-3 col-sm-6"> <div id="chart_div"></div> </div> <div class="col-sm-3"> <div class="panel panel-primary"> <div class="panel-heading">Activity feed</div> <div class="panel-body" id="activityFeedMessage"> <span class="label label-warning">no activity to display</span> </div> <ul class="list-group" id="activityFeed"></ul> </div> </div> </div> </div> </body>
OwinSignalR.Notificator
NotificationHelper.cs
This file contains the code that updates the "Activity Feed"
public static class NotificationHelper { private static Timer _notificationTimer; private static List<string> _possibleMessages = new List<string> { { "Batch process complete" }, { "SQL Indexes rebuild complete" }, { "Integrity check complete" }, { "Temp files deleted" }, { "File ready for processing" }, { "Server now available" }, { "Inactive accounts deactivated" }, { "Welcome emails job complete" }, { "Annual renew program emails sent" } }; private static Random _randomMessage = new Random(); public static void Start() { _notificationTimer = new Timer(OnNotificationTimerCallBack, null, 1000, 2500); } private static void OnNotificationTimerCallBack( object state) { var service = new NotificationService(); var token = System.Configuration.ConfigurationManager.AppSettings["Token"]; var applicationSecret = System.Configuration.ConfigurationManager.AppSettings["ApplicationSecret"]; service.Send(token, applicationSecret, "testUser", "testCallBack", new TestClass { CreateDate = DateTime.Now.ToString("yyyy/MM/dd HH:ss"), Message = _possibleMessages.ElementAt(_randomMessage.Next(_possibleMessages.Count - 1)) }); Console.WriteLine("Sent {0}", DateTime.Now); } public class TestClass { public string CreateDate { get; set; } public string Message { get; set; } } }
Program.cs
ServerLoadHelper.cs
This file contains the code that updated the Graphs/Gauges with the fake server load data.
public class ServerLoadHelper { private static Timer _serverLoadMemoryTimer; private static Timer _serverLoadCPUTimer; private static Timer _serverLoadNetworkTimer; private static Random _serverLoad = new Random(); public static void Start() { _serverLoadMemoryTimer = new Timer(OnServerLoadMemoryTimer , null, 1000, 1000); _serverLoadCPUTimer = new Timer(OnServerLoadCPUTimer , null, 1000, 1700); _serverLoadNetworkTimer = new Timer(OnServerLoadNetworkTimer, null, 1000, 2300); } private static void OnServerLoadNetworkTimer( object state) { var service = new NotificationService(); var token = System.Configuration.ConfigurationManager.AppSettings["Token"]; var applicationSecret = System.Configuration.ConfigurationManager.AppSettings["ApplicationSecret"]; service.Send(token, applicationSecret, "testUser", "serverLoadNetwork", _serverLoad.Next(100)); } private static void OnServerLoadCPUTimer( object state) { var service = new NotificationService(); var token = System.Configuration.ConfigurationManager.AppSettings["Token"]; var applicationSecret = System.Configuration.ConfigurationManager.AppSettings["ApplicationSecret"]; service.Send(token, applicationSecret, "testUser", "serverLoadCPU", _serverLoad.Next(100)); } private static void OnServerLoadMemoryTimer( object state) { var service = new NotificationService(); var token = System.Configuration.ConfigurationManager.AppSettings["Token"]; var applicationSecret = System.Configuration.ConfigurationManager.AppSettings["ApplicationSecret"]; service.Send(token, applicationSecret, "testUser", "serverLoadMemory", _serverLoad.Next(100)); } }