I am using the code below to setup MassTransit to use ServiceBus
private static ServiceProvider SetupServiceCollection()
{
var connectionString = ConfigurationManager.AppSettings["AzureServiceBusConnectionString"];
var services = new ServiceCollection()
.AddMassTransit(x =>
{
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host(connectionString);
cfg.ConfigureEndpoints(context);
cfg.Message<MyMessage>(x =>
{
x.SetEntityName("my-topic");
});
});
});
return services.BuildServiceProvider();
}
I use the following code to send a message
var message = new MyMessage()
{
MessageIdentifier = Guid.NewGuid().ToString(),
};
await _busControl.Publish(message);
I want my message to be sent to my-topic only
However, MassTransit is creating topic, the names seem to be getting generated using the type names. How do I totally stop this?
I am setting up the receiver as below
public static void SetupMassTransit(this ServiceCollection services, string connectionString)
{
services.AddMassTransit(x =>
{
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host(connectionString);
cfg.ConfigureEndpoints(context);
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host(connectionString);
cfg.SubscriptionEndpoint<MyMessage>("low", e =>
{
e.Consumer<MyMessageConsumer>(context);
e.PrefetchCount = 100;
e.MaxConcurrentCalls = 100;
e.LockDuration = TimeSpan.FromMinutes(5);
e.MaxAutoRenewDuration = TimeSpan.FromMinutes(30);
e.UseMessageRetry(r => r.Intervals(100, 200, 500, 800, 1000));
e.UseInMemoryOutbox();
e.ConfigureConsumeTopology = false;
});
});
});
}
I can see that the message is being sent correctly, as its shown inside the subscription in Service Bus Explorer. However, the receiver is not picking it up? There are no errors or anything to go on? Really frustrating
Paul
You're calling ConfigureEndpoints, which will by default create receive endpoints for the consumers, sagas, etc. that have been added. However, your code sample doesn't show any .AddConsumer methods. If you don't have any consumers, don't call ConfigureEndpoints.
For your receiver, you should use:
public static void SetupMassTransit(this ServiceCollection services, string connectionString)
{
services.AddMassTransit(x =>
{
x.AddConsumer<MyMessageConsumer>();
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host(connectionString);
cfg.SubscriptionEndpoint("your-topic-name", "your-subscription-name", e =>
{
e.PrefetchCount = 100;
e.MaxConcurrentCalls = 100;
e.LockDuration = TimeSpan.FromMinutes(5);
e.MaxAutoRenewDuration = TimeSpan.FromMinutes(30);
e.UseMessageRetry(r => r.Intervals(100, 200, 500, 800, 1000));
e.UseInMemoryOutbox();
e.ConfigureConsumer<MyMessageConsumer>(context);
});
});
});
}
For your producer, you can simply use:
private static ServiceProvider SetupServiceCollection()
{
var connectionString = ConfigurationManager.AppSettings["AzureServiceBusConnectionString"];
var services = new ServiceCollection()
.AddMassTransit(x =>
{
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host(connectionString);
});
});
return services.BuildServiceProvider();
}
Then, to publish, using your IServiceProvider that was created above:
var bus = serviceProvider.GetRequiredService<IBus>();
var endpoint = await bus.GetSendEndpoint(new Uri("topic:your-topic-name"));
await endpoint.Send(new MyMessage());
That should do the absolute minimum required that you need.
Related
I'm trying to get a dual authentication approach working for my .NET6 website. For the front-end, I'm implementing Azure AD B2C, and for the back-end, Azure AD. Here's my code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication()
.AddMicrosoftIdentityWebApp(options => {
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.Instance = "Instance1";
options.TenantId = "TenantId1";
options.ClientId = "ClientId1";
options.ClientSecret = "ClientSecret1";
options.CallbackPath = "/signin-oidc/aadb2b";
options.Scope.Clear();
options.Scope.Add(OpenIdConnectScope.OpenId);
options.Scope.Add(OpenIdConnectScope.OfflineAccess);
options.Scope.Add(OpenIdConnectScope.Email);
options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
options.MapInboundClaims = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "preferred_username",
ValidateIssuer = false
};
options.Events.OnRedirectToIdentityProvider = ctx =>
{
if (ctx.Response.StatusCode == 401)
{
ctx.HandleResponse();
}
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = ctx =>
{
ctx.HandleResponse();
ctx.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(ctx.Exception.Message));
return Task.CompletedTask;
};
}, options => {
options.Events.OnSignedIn = async ctx =>
{
if (ctx.Principal?.Identity is ClaimsIdentity claimsIdentity)
{
// Syncs user and roles so they are available to the CMS
var synchronizingUserService = ctx
.HttpContext
.RequestServices
.GetRequiredService<ISynchronizingUserService>();
await synchronizingUserService.SynchronizeAsync(claimsIdentity);
}
};
}, "AADB2B.OpenIdConnect", "AADB2B.Cookies");
services.AddAuthentication()
.AddMicrosoftIdentityWebApp(options => {
options.Instance = "Instance2";
options.Domain = "Domain2";
options.TenantId = "TenantId2";
options.ClientId = "ClientId2";
options.ClientSecret = "ClientSecret2";
options.SignUpSignInPolicyId = "USUIP";
options.ResetPasswordPolicyId = "RPP";
options.EditProfilePolicyId = "EPP";
options.CallbackPath = "/signin-oidc/aadb2c";
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "roles"
};
options.Events.OnRedirectToIdentityProvider = ctx =>
{
if (ctx.Response.StatusCode == 401)
{
ctx.HandleResponse();
}
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = ctx =>
{
ctx.HandleResponse();
ctx.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(ctx.Exception.Message));
return Task.CompletedTask;
};
}, options => {
options.Events.OnSignedIn = async ctx =>
{
if (ctx.Principal?.Identity is ClaimsIdentity claimsIdentity)
{
// Syncs user and roles so they are available to the CMS
var synchronizingUserService = ctx
.HttpContext
.RequestServices
.GetRequiredService<ISynchronizingUserService>();
await synchronizingUserService.SynchronizeAsync(claimsIdentity);
}
};
}, "AADB2C.OpenIdConnect", "AADB2C.Cookies");
// Added as an experiment, doesn't seem to help
services.AddAuthorization(options =>
options.DefaultPolicy =
new AuthorizationPolicyBuilder("AADB2B.OpenIdConnect")
.RequireAuthenticatedUser()
.Build());
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseNotFoundHandler();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseGetaCategories();
app.UseGetaCategoriesFind();
app.UseAnonymousId();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/LoginPath", async ctx => ctx.Response.Redirect("/")).RequireAuthorization(authorizeData: new AuthorizeAttribute { AuthenticationSchemes = "AADB2B.OpenIdConnect" });
endpoints.MapGet("/LogoutPath", async ctx => await MapLogout(ctx));
endpoints.MapControllerRoute(name: "Default", pattern: "{controller}/{action}/{id?}");
endpoints.MapControllers();
endpoints.MapRazorPages();
endpoints.MapContent();
});
}
public async Task MapLogout(HttpContext ctx)
{
await ctx.SignOutAsync("AADB2B.OpenIdConnect");
await ctx.SignOutAsync("AADB2B.Cookies");
ctx.Response.Redirect("/");
}
Controller.cs
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLogin(string scheme, string returnUrl)
{
return Challenge(new AuthenticationProperties { RedirectUri = string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl });
}
Controller is receiving a hyperlink with the QueryString scheme=AADB2B.OpenIdConnect and scheme=AADB2C.OpenIdConnect respectively.
Upon clicking the hyperlinks, the browser is properly redirected to the signin page for AAD B2C or AAD respectively, and then properly redirected back to the website. A breakpoint in the OnSignedIn event properly shows that the Principal.Identity is indeed a ClaimsIdentity, and IsAuthenticated is true. When arriving in the website, the cookies seem to exist:
However, after the page finishes loading, checking IHttpContextAccessor on subsequent pages shows that the HttpContext.User seems to be a brand-new one, and not the one that exists after the above authentication call.
I tried changing to this:
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLogin(string scheme, string returnUrl)
{
return Challenge(new AuthenticationProperties { RedirectUri = Url.Action("ExternalLoginCallback", new { scheme = scheme, returnUrl = returnUrl }) }, scheme);
}
[Authorize(AuthenticationSchemes = "AADB2B.OpenIdConnect,AADB2C.OpenIdConnect")]
public async Task<ActionResult> ExternalLoginCallback(string scheme, string returnUrl)
{
var authenticate = await HttpContext.AuthenticateAsync(scheme);
if (authenticate.Succeeded)
User.AddIdentity((ClaimsIdentity)authenticate.Principal.Identity);
return Redirect(string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl);
}
On the authenticate.Succeeded line, I see that my user was properly authenticated. The User.AddIdentity line properly adds the identity to that user. However, when I look on the subsequent page load, the above identity is gone.
I'm at wits end. Any suggestions would be greatly appreciated. Thanks!
Update 1
Navigating directly to a page that is decorated with [Authorize(AuthenticationSchemes = "AADB2C.OpenIdConnect")] DOES properly result in the page recognizing the user as being authenticated. However, from there, navigating anywhere else then shows them no longer being authenticated.
Update 2
Calling IHttpContextAccessor.HttpContext?.AuthenticateAsync("AADB2C.OpenIdConnect") in places where I couldn't decorate with the Authorize flag (due to requiring access for non-authenticated users as well) properly fetches the authenticated user and their information. So, now the only piece of this puzzle I need to solve is finding a way to get Authorize into areas of the code which I can't access, due to being hidden behind proprietary third-party code.
Update 3
I'm unsure why, but it appears as though if I use AddOpenIdConnect instead of AddMicrosoftIdentityWebApp, it ... works? It defaults to that and my back-end now properly recognizes my authentication.
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = null;
options.DefaultSignInScheme = null;
}).AddCookie(options =>
{
options.Events.OnSignedIn = async ctx =>
{
if (ctx.Principal?.Identity is ClaimsIdentity claimsIdentity)
{
// Syncs user and roles so they are available to the CMS
var synchronizingUserService = ctx
.HttpContext
.RequestServices
.GetRequiredService<ISynchronizingUserService>();
await synchronizingUserService.SynchronizeAsync(claimsIdentity);
}
};
}).AddOpenIdConnect(options =>
{
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.Authority = $"MyAuthority";
options.ClientId = "MyClientId";
options.ClientSecret = "MyClientSecret";
options.CallbackPath = "/signin-oidc/aadb2b";
options.Scope.Clear();
options.Scope.Add(OpenIdConnectScope.OpenId);
options.Scope.Add(OpenIdConnectScope.OfflineAccess);
options.Scope.Add(OpenIdConnectScope.Email);
options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
options.MapInboundClaims = false;
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "roles",
NameClaimType = "preferred_username",
ValidateIssuer = false
};
options.Events.OnRedirectToIdentityProvider = ctx =>
{
if (ctx.Response.StatusCode == 401)
{
ctx.HandleResponse();
}
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = ctx =>
{
ctx.HandleResponse();
ctx.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(ctx.Exception.Message));
return Task.CompletedTask;
};
});
So, to summarize the steps that I took to resolve this:
Adding [Authorize(AuthenticationSchemes = "MyScheme")] to controllers will properly force authentication when navigating using that controller route.
Calling IHttpContextAccessor.HttpContext?.AuthenticateAsync("MyScheme") returns details of the authenticated principal, allowing code-based control in places where the [Authorize] approach won't work (because it needs to allow both anonymous and authenticated users, and renders differently based on that condition).
For the specific back-end code I couldn't access due to it being hidden behind third-party proprietary code (EPiServer in this case), I was able to resolve the issue by switching to use AddOpenIdConnect instead of AddMicrosoftIdentityWebApp. I'm unsure why this worked, but for the moment I'm not going to question it further.
The following code is a serial port event inside a winforms form ( so obviously running on it's own thread ). The slow line of code is run elsewhere ( in Nodejs ) and takes about 10 seconds. In this code the same line takes 45 seconds - sometimes 60 seconds.
private async void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadLine().Replace("?","").Replace(" ","");
if(indata != null)
{
var weightMatch = Regex.Match(indata, #"(\d+\.\d+)kg$");
var weight = weightMatch.Groups[1].Value;
var message = barcode + " Weight:" + weight;
this.Invoke(new Action(() => { scanData.Add(new ScanDataItem(scanData.Count + 1, message, "")); }));
this.Invoke(new Action(() => { mainList.SelectedIndex = mainList.Items.Count - 1; }));
string jsonData = "";
// ***************************************************************
// The following line is about 30 seconds slower than it should be - inner function calls a web service
// ***************************************************************
var jsonString = await TrayWeighScan.doOrder(barcode, Decimal.Parse(weight));
try
{
var jsonDoc = JsonDocument.Parse(jsonString);
jsonData = JsonSerializer.Serialize(jsonDoc, new JsonSerializerOptions { WriteIndented = true });
}
catch (Exception)
{
jsonData = jsonString;
}
this.Invoke(new Action(() => { scanData.Last().responseData = jsonData; }));
this.Invoke(new Action(() => { responseTextBox.Text = jsonData; }));
this.Invoke(new Action(() => { statusTextBox.Text = "Ready to Scan"; }));
this.Invoke(new Action(() => { busy = false; }));
}
}
public class TrayWeighScan
{
//private static readonly HttpClient httpClient = new();
public static async Task<string> doOrder(string orderNumber, decimal weight )
{
HttpClient httpClient = new();
var url = "http://r2hserver/logistics/weighscan?orderNumber="+orderNumber+"&weight="+weight.ToString("F2");
HttpResponseMessage response = await httpClient.GetAsync(url);
string responseText = await response.Content.ReadAsStringAsync();
return responseText;
}
}
If I run the "slow" line directly from a form button, 11 seconds.
private async void btnUrlTest_Click(object sender, EventArgs e)
{
string orderNumber = "ORD100302338";
decimal weight = 1.606m;
//following line - 11 seconds.
var a = await TrayWeighScan.doOrder(orderNumber,weight);
var b = "";
}
Hans Passant as pointed out that most likely the cause isn't in the code structure at all, and most likely he's right. To test the hypothesis, descend into trayWeighScan.doOrder and change the async HttpClient async call into a sync call. If the problem goes away, my answer is good. If the problem remains, look elsewhere, say the network or the client machine's TCP stack or AV or somesuch.
I've not seen this exact thing before, but I've learned that launching async operations in a WinForms app doesn't do the expected when launched in the naive way. The following should work much better
Task.Run(async () => {
var jsonString = await trayWeighScan.doOrder(barcode, Decimal.Parse(weight);
try
{
var jsonDoc = JsonDocument.Parse(jsonString);
jsonData = JsonSerializer.Serialize(jsonDoc, new JsonSerializerOptions { WriteIndented = true });
}
catch (Exception)
{
jsonData = jsonString;
}
this.Invoke(new Action(() => { scanData.Last().responseData = jsonData; }));
this.Invoke(new Action(() => { responseTextBox.Text = jsonData; }));
this.Invoke(new Action(() => { statusTextBox.Text = "Ready to Scan"; }));
this.Invoke(new Action(() => { busy = false; }));
});
Wrapping winforms-originating async in Task.Run seems to completely fix the problem.
A lot of stuff talks about .ConfigureAwait() being the solution; but it's overly complex. The simplest and best solution is to only spawn async/await from threadpool threads, which means starting an async/await chain from Task.Run.
As the title suggests,Examples on the official website are console project but is it really impossible to apply to asp.net core web?
My web project as a consumer or producer but does not output received information,But the rabbitmq page console displays the queue messages sent,so What are consumers or producer in the actual production environment?windows server?console?
this is my api code:
[HttpGet]
public ActionResult sendMgs()
{
string message = string.Empty;
//var uri = new Uri("amqp://192.168.150.129:5672");
var factory = new ConnectionFactory()
{
UserName = "admin",
Password = "admin",
Port=5672,
HostName= "192.168.150.129",
RequestedHeartbeat = 0,
VirtualHost= "/vhost_mmr"
//Endpoint = new AmqpTcpEndpoint(uri)
};
using (var connection=factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue:"hello",
durable:false,
exclusive:false,
autoDelete:false,
arguments:null);
message = "Hello World";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey:"hello",
basicProperties:null,
body:body);
}
}
return Json(new {message = message });
}
and this my mvc web code:
public IActionResult MqTest()
{
System.Diagnostics.Debug.Write("test begin:");
GetQueueMsg();
return View();
}
public void GetQueueMsg()
{
var factory = new ConnectionFactory()
{
UserName = "admin",
Password = "admin",
Port = 5672,
HostName = "192.168.150.129",
RequestedHeartbeat = 0,
VirtualHost = "/vhost_mmr"
};
using (var connection = factory.CreateConnection())
{
using (var channel =connection.CreateModel())
{
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var msg = Encoding.UTF8.GetString(body);
ViewBag.msg = msg;
System.Diagnostics.Debug.Write("test:" + msg);
};
var ret = channel.BasicConsume(queue: "hello",
autoAck: true,
consumer: consumer);
}
}
}
I've been trying to convert the functions in this https://blog.jeremylikness.com/build-a-serverless-link-shortener-with-analytics-faster-than-finishing-your-latte-8c094bb1df2c to a WebAPI equivalent. This is my webapi call:
[HttpPost]
public async Task<IActionResult> PostAsync([FromBody] ShortRequest shortRequest)
{
_logger.LogInformation($"ShrinkUrl api called with req: {shortRequest}");
if(!Request.IsHttps && !Request.Host.Host.Contains("localhost"))
return StatusCode(StatusCodes.Status400BadRequest);
if (string.IsNullOrEmpty(shortRequest.Input))
return StatusCode(StatusCodes.Status404NotFound);
try
{
var result = new List<ShortResponse>();
var analytics = new Analytics();
// determine whether or not to process analytics tags
bool tagMediums = analytics.Validate(shortRequest);
var campaign = string.IsNullOrWhiteSpace(shortRequest.Campaign) ? DefaultCampaign : shortRequest.Campaign;
var url = shortRequest.Input.Trim();
var utm = analytics.TagUtm(shortRequest);
var wt = analytics.TagWt(shortRequest);
_logger.LogInformation($"URL: {url} Tag UTM? {utm} Tag WebTrends? {wt}");
// get host for building short URL
var host = Request.Scheme + "://" + Request.Host;
await _tableOut.CreateIfNotExistsAsync();
if (_keyTable == null)
{
_logger.LogInformation($"Keytable is null, creating initial partition key of 1");
_keyTable = new NextId
{
PartitionKey = "1",
RowKey = "KEY",
Id = 1024
};
var keyAdd = TableOperation.Insert(_keyTable);
await _tableOut.ExecuteAsync(keyAdd);
}
// strategy for getting a new code
string getCode() => Utility.Encode(_keyTable.Id++);
// strategy for logging
void logFn(string msg) => _logger.LogInformation(msg);
// strategy to save the key
async Task saveKeyAsync()
{
var operation = TableOperation.Replace(_keyTable);
await _tableOut.ExecuteAsync(operation);
}
// strategy to insert the new short url entry
async Task saveEntryAsync(TableEntity entry)
{
var operation = TableOperation.Insert(entry);
await _tableOut.ExecuteAsync(operation);
}
// strategy to create a new URL and track the dependencies
async Task saveWithTelemetryAsync(TableEntity entry)
{
await TrackDependencyAsync(
"AzureTableStorageInsert",
"Insert",
async () => await saveEntryAsync(entry),
() => true);
await TrackDependencyAsync(
"AzureTableStorageUpdate",
"Update",
async () => await saveKeyAsync(),
() => true);
}
if (tagMediums)
{
// this will result in multiple entries depending on the number of
// mediums passed in
result.AddRange(await analytics.BuildAsync(
shortRequest,
Source,
host,
getCode,
saveWithTelemetryAsync,
logFn,
HttpUtility.ParseQueryString));
}
else
{
// no tagging, just pass-through the URL
result.Add(await Utility.SaveUrlAsync(
url,
null,
host,
getCode,
logFn,
saveWithTelemetryAsync));
}
_logger.LogInformation($"Done.");
//return req.CreateResponse(HttpStatusCode.OK, result);
}
catch (Exception ex)
{
_logger.LogError("An unexpected error was encountered.", ex);
//return req.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
return null;
}
And this is the function params:
[FunctionName("ShortenUrl")]
public static async Task<HttpResponseMessage>([HttpTrigger(AuthorizationLevel.Function, "post")],HttpRequestMessage req,
[Table(Utility.TABLE, "1", Utility.KEY, Take = 1)]NextId keyTable,
[Table(Utility.TABLE)]CloudTable, TraceWriter log)
The azure function takes care of ensuring that the keyTable contains the next Id in the counter but I cannot figure out how to do the same in the webapi call.
Any ideas?
I tried to integrate with Push Notifications to my forms application. Azure messaging component is used for achieving this.
Below is the code i am using. I am getting the trigger to RegisteredForRemoteNotifications method. But RegisterNativeAsync method doesn't seem to be doing the job.
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var push = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(push);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
const UIRemoteNotificationType not = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(not);
}
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Hub = new SBNotificationHub(conStirng, NotifHubPath);
Hub.UnregisterAllAsync(deviceToken, (error) =>
{
//Get device token
var id = deviceToken.ToString();
var tag = "username";
var tags = new List<string> { tag };
Hub.RegisterNativeAsync(id, new NSSet(tags.ToArray()), (errorCallback) =>
{
if (errorCallback != null)
{
//Log to output
}
});
});
}
What am i doing wrong here? How can i confirm if the Register function is success or failure.?
You need to check if the error from the response of the register method is null or not. if it is null means the it is a success.
var hub = new SBNotificationHub (cs, "your-hub-name");
hub.RegisterNativeAsync (deviceToken, null, err => {
if (err != null)
Console.WriteLine("Error: " + err.Description);
else
Console.WriteLine("Success");
});
In the case of windows universal apps we can check the registrationId property of the response.
private async void InitNotificationsAsync()
{
var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
var hub = new NotificationHub("<hub name>", "<connection string with listen access>");
var result = await hub.RegisterNativeAsync(channel.Uri);
// Displays the registration ID so you know it was successful
if (result.RegistrationId != null)
{
var dialog = new MessageDialog("Registration successful: " + result.RegistrationId);
dialog.Commands.Add(new UICommand("OK"));
await dialog.ShowAsync();
}
}