I have been refactoring some of my code, and I can't help but feeling it's very cluttered, complex and confusing. After looking into it more and more, I've begun to notice that the code isn't so much complex as it is verbose.
We're using NLog and providing very verbose output to a debug log, as the application is incredibly prone to failure, and we're trying to be very thorough.
Here's an example of one of the more simple methods
[HttpGet]
public ActionResult Zendesk(long? id)
{
if (!GlobalVariables.AllSyncSettings.SyncEnabled || !GlobalVariables.AllSyncSettings.ZdSyncEnabled)
return Json(new { Enabled = "False" });
if (id == null || id == 0)
return Json(new { Error = "Missing or malformed ticket ID." }, AG);
if (CheckZdIdExists((long)id))
return Json(new { status = "error, in queue." }, AG);
GlobalVariables.TicketsInQueue.Add((long)id);
Log("-------------- STARTING NEW CASE [ZENDESK]["+ id +"] --------------");
var zdHelper = new ZendeskHelpers();
var zdTicket = zdHelper.GetTicketById((long)id);
if (zdTicket == null)
{
Log("--------------- ENDING CASE [ZENDESK][" + id + "] ---------------");
GlobalVariables.TicketsInQueue.Remove((long)id);
return Json(new { Error = "Error fetching ticket" }, AG);
}
var sfHelper = new SalesForceHelpers();
if (!sfHelper.checkTicketOwner(zdTicket))
{
Warn(id + " | Case generation not necessary. Ticket doesn't meet criteria.");
Log("--------------- ENDING CASE [ZENDESK][" + id + "] ---------------");
GlobalVariables.TicketsInQueue.Remove((long)id);
return Json(new { success = "case was not generated" }, AG);
}
Log(id + " | Generating SalesForce case for Zendesk");
var sfCase = sfHelper.GenerateCase(zdTicket);
if (sfCase == null)
{
Warn(id + " | Case was not generated successfully. Cannot continue.");
Log("--------------- ENDING CASE [ZENDESK][" + id + "] ---------------");
GlobalVariables.TicketsInQueue.Remove((long)id);
return Json(new { Error = "Case was not generated successfully. Cannot continue." }, AG);
}
GetZDAttachments(zdHelper, sfHelper, (long)id, sfCase);
if (!zdTicket.Status.ToLower().Contains("closed") && ZendeskHelpers.GetCustomField(zdTicket, ZdCustomFields.SfCaseNo) == null)
zdHelper.SetSfId(zdTicket, sfCase.CaseNumber);
Log("--------------- ENDING CASE [ZENDESK][" + id + "] ---------------");
GlobalVariables.TicketsInQueue.Remove((long)id);
return Json(new { SFCase = sfCase }, AG);
}
Your code is full of conditionals (too many ifs) and that is the reason for verbosity. You should replace your conditionals with polymorphism. A good article for that is provided here
http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
Related
So I am trying to create a stored procedure for a prototype web application.
My class/document name is "Plan" and it has a Description property and an OrderingNumber property as int -- the actual functions of these aren't exactly important to the issue, so let's leave that out.
The database is called "PlanDB" and the collection I wish to execute the stored procedure on is called "Plans", where I, of course, have also saved the stored procedure.
This is the function from my C# app:
PlanService.cs
public async Task SwapOrderingNumbers(string id1, string id2)
{
try
{
await client.ExecuteStoredProcedureAsync<bool>(UriFactory.CreateStoredProcedureUri("PlanDB", "Plans", "swapOrderNumberSproc"), id1, id2);
}
catch (DocumentClientException de)
{
throw;
}
}
As you can see, I have the parameters id1 and id2 which I wish to use in the execution of said stored procedure -- ideally, it should swap around the OrderingNumber of the 2 Plans.
swapOrderNumberSproc
// Stored procedure for swapping ordering numbers
// #param planId1 - Plan 1's ID
// #param planId2 - Plan 2's ID
var swapOrderNumberSproc = {
id: "swapOrderNumberSproc",
serverScript: function (planId1, planId2) {
var context = getContext();
var collection = context.getCollection();
var response = context.getResponse();
var plan1Document, plan2Document;
// query for Plans
var filterQuery = 'SELECT * FROM Plans a where a.id = "' + planId1 + '"';
var accept = collection.queryDocuments(collection.getSelfLink(), filterQuery, {},
function (err, documents, responseOptions) {
if (err) throw new Error("Error" + err.message);
if (documents.length != 1) throw "Unable to find both names";
plan1Document = documents[0];
var filterQuery2 = 'SELECT * FROM Plans a where a.id = "' + planId2 + '"';
var accept2 = collection.queryDocuments(collection.getSelfLink(), filterQuery2, {},
function (err2, documents2, responseOptions2) {
if (err2) throw new Error("Error" + err2.message);
if (documents2.length != 1) throw "Unable to find both names";
plan2Document = documents2[0];
swapOrder(plan1Document, plan2Document);
return;
});
if (!accept2) throw "Unable to read Plan details, abort ";
});
if (!accept) throw "Unable to read Plan details, abort ";
// swap the two Plans’ OrderingNumbers
function swapOrder(plan1, plan2) {
var plan1NumberSave = plan1.OrderingNumber;
plan1.OrderingNumber = plan2.OrderingNumber;
plan2.OrderingNumber = plan1NumberSave;
var accept = collection.replaceDocument(plan1._self, plan1,
function (err, docReplaced) {
if (err) throw "Unable to update Plan 1, abort ";
var accept2 = collection.replaceDocument(plan2._self, plan2,
function (err2, docReplaced2) {
if (err) throw "Unable to update Plan 2, abort"
});
if (!accept2) throw "Unable to update Plan 2, abort";
});
if (!accept) throw "Unable to update Plan 1, abort";
}
}
The SwapOrderingNumbers() from C# is called via POST at the endpoint "/swapnumbers" in my controller:
PlansController
[Route("swapnumbers")]
[HttpPost]
public async Task SwapOrderingNumbers()
{
await activityService.SwapOrderingNumbers("ca35e414-f1b8-49dc-89e7-61e2e100d14a", "dd4a8298-55b8-425b-b16b-f73229399107");
}
For now, the IDs given as parameters are hardcoded to simplify.
Whenever I try to execute the stored procedure through the POST, it returns an error 500. What am I doing wrong?
EDITS
Trace
Microsoft.Azure.Documents.BadRequestException: Message: {"Errors":
["Encountered exception while compiling Javascript. Exception =
SyntaxError: Syntax error\r\nSource information: line: 5, column: 1,
source line:\r\nvar swapOrderNumberSproc = {"]}
ActivityId: bf1bacba-0375-4a51-9c94-07c89dfb4868, Request URI:
/apps/DocDbApp/services/DocDbServer19/partitions/a4cb495f-38c8-11e6-
8106-8cdcd42c33be/replicas/1p/
at Microsoft.Azure.Documents.TransportClient.ThrowIfFailed(String
resourceAddress, StoreResponse storeResponse, Uri physicalAddress, Guid
activityId)
at Microsoft.Azure.Documents.RntbdTransportClient.
<InvokeStoreAsync>d__3.MoveNext()
The part I removed from the stored procedure
var swapOrderNumberSproc = {
id: "swapOrderNumberSproc",
serverScript:
Replaced with
function swapOrderNumberSproc(activityId1, activityId2) {
It seems your SP script should be starting with function swapOrderNumberSproc (planId1, planId2){.
function swapOrderNumberSproc(planId1, planId2) {
var context = getContext();
var collection = context.getCollection();
var response = context.getResponse();
var plan1Document, plan2Document;
// query for Plans
var filterQuery = 'SELECT * FROM Plans a where a.id = "' + planId1 + '"';
var accept = collection.queryDocuments(collection.getSelfLink(), filterQuery, {},
function (err, documents, responseOptions) {
if (err) throw new Error("Error" + err.message);
if (documents.length != 1) throw "Unable to find both names";
plan1Document = documents[0];
var filterQuery2 = 'SELECT * FROM Plans a where a.id = "' + planId2 + '"';
var accept2 = collection.queryDocuments(collection.getSelfLink(), filterQuery2, {},
function (err2, documents2, responseOptions2) {
if (err2) throw new Error("Error" + err2.message);
if (documents2.length != 1) throw "Unable to find both names";
plan2Document = documents2[0];
swapOrder(plan1Document, plan2Document);
response.setBody(true);
});
if (!accept2) throw "Unable to read Plan details, abort ";
});
if (!accept) throw "Unable to read Plan details, abort ";
// swap the two Plans’ OrderingNumbers
function swapOrder(plan1, plan2) {
var plan1NumberSave = plan1.OrderingNumber;
plan1.OrderingNumber = plan2.OrderingNumber;
plan2.OrderingNumber = plan1NumberSave;
var accept = collection.replaceDocument(plan1._self, plan1,
function (err, docReplaced) {
if (err) throw "Unable to update Plan 1, abort ";
var accept2 = collection.replaceDocument(plan2._self, plan2,
function (err2, docReplaced2) {
if (err) throw "Unable to update Plan 2, abort"
});
if (!accept2) throw "Unable to update Plan 2, abort";
});
if (!accept) throw "Unable to update Plan 1, abort";
}
}
Also, you are not returning a bool at any point, you have to send the value in the response. I changed your return; to response.setBody(true);.
After that refactor, I was able to run it from C# without the compiling errors and returning a bool.
I am attempting to get a raw count from a foreach function inside of a result for an AJAX post response. The issue I am facing is that in my post response, I am receiving the total of all the iterations of the foreach, but not the individuals. The purpose of doing this is to show the progress of the upload by filling a progress bar at each iteration.
Controller:
public JsonResult progressFunction(int? SystemGeneralAnnouncementId)
{
var systemGeneralAnnouncement = (SystemGeneralAnnouncementId == null) ? null : _uow.SystemGeneralAnnouncementRepository.GetById(SystemGeneralAnnouncementId.Value);
List<Status> status = new List<Status>();
if (systemGeneralAnnouncement.Statuses.Length > 0)
{
status.AddRange(systemGeneralAnnouncement.Statuses.Split(',').Select(item => (Status) Enum.Parse(typeof (Status), item)));
}
var allPocEmailAddresses = new List<InstitutionPointOfContact>();
var pocEmailAddresses = new List<InstitutionPointOfContact>();
//retrieve all Point of contact based upon selected statuses per each loop
var result = new List<InstitutionPointOfContact>();
foreach (var item in status)
{
result = _uow.InstitutionPointOfContactRepository.GetAllByStatus(item).ToList();
allPocEmailAddresses.AddRange(result);
}
// Retrieve the poc email addresses based on the who is intended to receive the email message
if (systemGeneralAnnouncement.SendToRecipients.Contains("(1) All Three POCs"))
{
pocEmailAddresses = allPocEmailAddresses;
}
else
{
if (systemGeneralAnnouncement.SendToRecipients.Contains("(2) All POCs"))
{
pocEmailAddresses.AddRange(allPocEmailAddresses.Where(r => r.PointOfContactType == PointOfContactTypes.Primary).ToList());
}
if (systemGeneralAnnouncement.SendToRecipients.Contains("(3) All Compliance POCs"))
{
pocEmailAddresses.AddRange(allPocEmailAddresses.Where(r => r.PointOfContactType == PointOfContactTypes.Secondary).ToList());
}
if (systemGeneralAnnouncement.SendToRecipients.Contains("(4) All Authorities"))
{
pocEmailAddresses.AddRange(allPocEmailAddresses.Where(r => r.PointOfContactType == PointOfContactTypes.SigningAuthority).ToList());
}
if (systemGeneralAnnouncement.SendToRecipients.Contains("(5) All Rate POCs"))
{
pocEmailAddresses.AddRange(allPocEmailAddresses.Where(r => r.PointOfContactType == PointOfContactTypes.TuitionRates).ToList());
}
if (systemGeneralAnnouncement.SendToRecipients.Contains("(6) Specified Email Address"))
{
var pocs = new List<InstitutionPointOfContact>();
string[] emails = systemGeneralAnnouncement.EmailAddresses.Split(',');
foreach (string email in emails)
{
var addPoc = new InstitutionPointOfContact { Email = email };
User user = _uow.UserRepository.GetByEmail(email);
if (user == null)
{
addPoc.FirstName = "Not Created Account Yet";
}
else
{
addPoc.FirstName = user.FirstName;
addPoc.LastName = user.LastName;
}
List<InstitutionPointOfContact> opeidAssociatedToUser =
_uow.InstitutionPointOfContactRepository
.GetAllPocsByEmail(email)
.ToList();
if (opeidAssociatedToUser.Count == 0)
{
addPoc.IDNumber = "N/A";
}
else
{
string[] idArray = idAssociatedToUser
.Select(x => x.IDNumber)
.ToArray();
addPoc.IDNumber = string.Join(",", opeidArray);
}
pocs.Add(addPoc);
}
pocEmailAddresses.AddRange(pocs);
}
}
ViewBag.UploadProgress = 0;
// if any poc addresses were found...
if (pocEmailAddresses.Count > 0)
{
string emailBody = WebUtility.HtmlDecode(systemGeneralAnnouncement.EmailBody);
foreach (InstitutionPointOfContact emailAddress in pocEmailAddresses.Where(x => x.Email != "" && x.Email != null).ToList())
{
string firstName = emailAddress.FirstName == null ? "" : emailAddress.FirstName.Trim();
string lastName = emailAddress.LastName == null ? "" : emailAddress.LastName.Trim();
string userName = firstName + " " + lastName;
//iterative for progress bar
ViewBag.UploadProgress++;
}
}
return Json (ViewBag.UploadProgress, JsonRequestBehavior.AllowGet);
}
AJAX:
$
(document).ready(function(){
$.ajax({
type: "POST",
url: "progressFunction",
cache: false,
cacheControl: "no-cache",
statusCode: {
500: function () {
errorWhileSavingData()
}
},
success: function (data) {
alert()
GenerateProgressBar(data);
}
});
});
My question is, can I retrieve the individual count from the Controller (Viewbag.uploadProgress) in the post function so that I can pass it as a variable count in a progress bar?
Update: For clarity, what I need to do is get the individual count of the foreach (1 ~ n), not the completed count, which is what I am receiving now.
Update 2: SingalR is not really an option in this case, as it would be excessive for such a small process, a desired result would come from a "roll your own"
To answer your question, this isn't possible given your current setup. Nice attempt with using the ViewBag, but the ViewBag isn't actually passed between Controller and View until the Action is completed and returns to the View. As I mentioned, SignalR would be one way to approach this, but is overkill for your use case.
I have an action method which is working as I expect. Here is the code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Title,Content,Category,HeaderFilePath")] Blog blog, HttpPostedFileBase upload)
{
if (ModelState.IsValid)
{
if (upload != null && upload.ContentLength > 0)
{
if (blog.HeaderFilePath != string.Empty && blog.HeaderFilePath != null)
{
try
{
System.IO.File.Delete(HttpContext.Server.MapPath("~/") + "images/" + blog.HeaderFilePath);
}
catch (Exception)
{
//TODO: handle it properly
//throw;
}
}
blog.HeaderFilePath = Guid.NewGuid() + ".jpg";
CustomHttpPostedFile f = new CustomHttpPostedFile(upload.InputStream, "jpg", HttpContext.Server.MapPath("~/") + "images/" + blog.HeaderFilePath);
f.SaveAs(HttpContext.Server.MapPath("~/") + "images/" + blog.HeaderFilePath);
}
db.Entry(blog).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(blog);
}
This Action method is doing its job no problem, Am I missing something?
The sign above the method body looks like "0 references | 1 request, 100% failed | 0 exceptions".
When i click on it it doesnt provide me any usefull information.
Edit: here is the screenshot showing no information for the failed request.
I am a newbie to C# & am trying to use the Lync SDK to search for a Lync user programmatically to get their status. I am not sure how to pass the results to the webservice response when the async callback gets executed.
This is the code:
Webservice Controller GET endpoint:
// GET: api/Lync/5
public String Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);
Lync lync = new Lync();
////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();
if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
lync.Search("medina");
//lync.Search("medina",
//(lyncContacts) =>
//{
// log.Debug("Search Results Callback fired!");
// log.Info("Results found: " + lyncContacts.Count);
// return lyncContacts.ToString();
//});
//Console.WriteLine(name);
//Console.ReadLine();
return "testUser";
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}
The above method calls the business service lync.search(..) which is as follows:
public void Search(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
//List<ContactInformationType> ContactInformationList = new List<ContactInformationType>();
//ContactInformationList.Add(ContactInformationType.Activity);
//ContactInformationList.Add(ContactInformationType.Availability);
// ContactInformationList.Add(ContactInformationType.CapabilityString);
//ContactSubscription contactSubscription = LyncClient.GetClient().ContactManager.CreateSubscription();
Console.WriteLine("Searching for contacts on " + searchKey);
LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
if (searchResults.Contacts.Count > 0)
{
log.Info("Search results found: " + searchResults.Contacts.Count);
Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");
foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);
Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());
log.Debug("Display Name: " + displayName);
log.Debug("Availability: " + currentAvailability);
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();
contactList.Add(lyncContact);
//done(contactList);
}
return;
}
else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
} else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
}
I tried to use Task+await but I am having a hard time trying to figure out how can i return the results from the callback funtion as results from the search method. How do i capture this in the controller class?
If you would like to use the async/await pattern for this use Task.FromAsync https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.fromasync(v=vs.110).aspx
Google for examples. Like this:
public async Task<List<LyncContact>> SearchAsync(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
Console.WriteLine("Searching for contacts on " + searchKey);
var cm = LyncClient.GetClient().ContactManager;
var searchResults = await Task<SearchResults>.Factory.FromAsync<String>(
cm.BeginSearch,
cm.EndSearch, searchKey, null);
if (searchResults.Contacts.Count > 0)
{
Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");
foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);
Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();
contactList.Add(lyncContact);
}
}
return contactList
}
In the controller you then can do:
public async Task<String> Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);
Lync lync = new Lync();
////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();
if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
var lyncContacts = await SearchAsync("medina");
log.Info("Results found: " + lyncContacts.Count);
return lyncContacts.ToString();
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}
Or you can use a TaskCompletionSource, see http://blog.stephencleary.com/2012/07/async-interop-with-iasyncresult.html (older post but principle is still valid.
Warning
I cannot compile this since I do not have access to the libraries you use. It should work but there might be some compile errors
What does it do
Callbacks are not a nice model to work with, as you have found out. using Tasks and the async/await model are far easier to work with. Luckily for you and me they have created several methods that can act as a bridge between the old and the new model. Using Task.FromAsync you can transform IAsyncResult methods to an easier to use Task based methodology. Stephen Cleary has written some nice blogs about them. See the post I mentioned earlier.
It looks like, by looking at your commented out code, that you tried to provide some sort of callback to your Search function. That would work, it should be of type Action<Microsoft.Lync.Model.SearchResults>
public void Search(string searchKey, Action<SearchResults> callback)
{
....
LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
callback(searchResults);
});
....
}
Then just uncomment the code in your controller:
lync.Search("medina",
(lyncContacts) =>
{
log.Debug("Search Results Callback fired!");
log.Info("Results found: " + lyncContacts.AllResults.Count);
});
I have been using Azure Notification Hubs along with GCM to send notifications to the users of my app. This was all working great until I published the app to the Play Store.
Now it gives a 500 Server error whenever I try to post a notification. I have no idea why this error is happening. Perhaps the app is not picking up the RavenDB where the notifications are stored?
But it looks more like the service is not getting back users that are registered on the Hub. I really don't know... Any help would be so appreciated!
This is my stacktrace when run locally, it is the same but less detailed when published:
"Message": "An error has occurred.",
"ExceptionMessage": "Value cannot be null.\r\nParameter name: source",
"ExceptionType": "System.ArgumentNullException",
"StackTrace": " at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)\r\n
at AcademicAssistantService.Controllers.NotificationController.<GetRecipientNamesFromNotificationHub>d__8.MoveNext()
in C:\\Users\\Kenneth\\Documents\\College\\Semester 8\\AcademicAssistantService\\AcademicAssistantService\\Controllers\\NotificationController.cs:line 105
This is the Controller action:
// POST api/notification
public async Task<IHttpActionResult> Post([FromBody]Notification notification, String key)
{
var notificationToSave = new Notification
{
NotificationGuid = Guid.NewGuid().ToString(),
TimeStamp = DateTime.UtcNow,
Message = notification.Message,
SenderName = notification.SenderName
};
var recipientNames = await GetRecipientNamesFromNotificationHub(key);
var recipientNamesString = CreateCustomRecipientNamesString(recipientNames);
string notificationJsonPayload =
"{\"data\" : " +
" {" +
" \"message\": \"" + notificationToSave.Message + "\"," +
" \"senderName\": \"" + notificationToSave.SenderName + "\"," +
" \"recipientNames\": \"" + recipientNamesString + "\"" +
" }" +
"}";
if (key == null)
{
var result = await _hubClient.SendGcmNativeNotificationAsync(notificationJsonPayload);
notificationToSave.TrackingId = result.TrackingId;
notificationToSave.Recipients = recipientNames;
}
else
{
foreach (string r in recipientNames)
{
if ((r != notification.SenderName))
{
var result = await _hubClient.SendGcmNativeNotificationAsync(notificationJsonPayload, "user:" + r);
notificationToSave.TrackingId = result.TrackingId;
notificationToSave.Recipients = recipientNames;
}
}
}
await Session.StoreAsync(notificationToSave);
return Ok(notificationToSave);
}
To get names from hub:
public async Task<List<string>> GetRecipientNamesFromNotificationHub(String key)
{
var registrationDescriptions = await _hubClient.GetAllRegistrationsAsync(Int32.MaxValue);
var recipientNames = new List<String>();
foreach (var registration in registrationDescriptions)
{
if (registration is GcmRegistrationDescription)
{
var userName = registration.Tags
.Where(t => t.StartsWith("user"))
.Select(t => t.Split(':')[1].Replace("_", " "))
.FirstOrDefault();
userName = userName ?? "Unknown User";
Conversation convo = db.Conversations.Find(key);
foreach (User u in convo.Users)
{
if (u.Email == userName && !recipientNames.Contains(userName))
{
recipientNames.Add(userName);
}
}
}
}
return recipientNames;
}
Could you use Service Bus Explorer and verify indeed you have tags starts with "user". And I also see you are using GetAllRegistrationsAsync API, which is recommend to use only for debugging purpose. This is heavily throttled API.
Thanks,
Sateesh