I am working with the xamarin.forms app generated when you download the sample for working with Azure Mobile Services. I have made some modifications. Firstly, I have changed Todo, to entry.cs:
public class entry
{
string id;
[JsonProperty("ID")]
public string ID { get; set; }
[JsonProperty("Time")]
public int Time { get; set; }
[JsonProperty("Percentage")]
public int Percentage { get; set; }
//I have omitted Device, Replacement, Use_profile, Longitude, Latitude, Battery
}
I try to add a new line to the table in my SQL database, by calling the following code from my page in cs:
var data = new entry{ Longitude = await GetLongitude(), Latitude = await GetLatitude(), Percentage = bpm }; // initialise new data entry
await AddItem (data);
When this is called, the app crashes.
Here is a gist of the log when the exception is thrown. It gives a Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOpperationException has been thrown
Explanation:
The resource you are looking for has been removed, had it's name changed, or is temporarily unavailable
This is thrown on the UIApplication.Main (args, null, "AppDelegate"); line in main.cs under the ios project.
Any thoughts on why this is happening would be much appreciated.
UPDATE:
Just to add a little more info, I have a web service setup at http://project.azurewebsites.net this is the address referenced in the constants section of the mobile application I am building in xamarin. However, the SQL database is at http://project-db.database.windows.net how do I get around this? Can I create a database on the original domain, or change the reference in the app?
It looks like you are getting a 404 error when you are calling your Mobile Backend. You need to add a new Table Controller to handle the "entry" class, because your client will be trying to post to https://yourservice.azurewebsites.net/tables/entry, which doesn't exist.
In your server project, you need to add a new class Entry that inherits from EntityData. Then you add this type to your DbContext class and add a table controller. This tutorial for Mobile Services controllers might be helpful. If you're using Mobile Apps, you would use Add -> New Scaffolded Item -> Azure Mobile Apps -> Mobile Apps Table Controller.
Then, deploy your server project so that the new REST endpoint is available and then your client app should be able to connect.
Edited to add: you specify the connect to the SQL Database in the MS_TableConnectionString setting in web.config. Whatever value is the Connection Strings section of the Azure Portal will override this. For more information, see https://azure.microsoft.com/en-us/documentation/articles/web-sites-configure/.
Related
I spent several hours reading through many different examples and documentation for setting up a portable area with ASP.NET MVC, with the intent of sharing a common login page with authentication for multiple applications. I got it all together and it works nicely so far, but one thing I'm having trouble with is the use of message bus. I see it is a way of communicating between the Host and Portable components, but I don't see a clear way of how to do this.
For instance; if my Portable login is successful, how do I tell the Host so it can do something (set a cookie, redirect to a specific page, etc.)? Also, if I want to send something to the Portable (like the title or Assembly Version of the Host application) how would I do that? I haven't tried anything yet because I cannot seem to find a complete example.
I got it all figured out. There was a MvcContrib source code archive that I was unable to download since Google Chrome was blocking the .zip file, but I was able to get it using Internet Explorer.
Here are the important bits after adapting it to my application. Hopefully this can help someone. I did my best to format my answer properly, this is my first time posting on StackOverflow:
In the Portable class library
Create a LoginResult class using the ICommandResult interface
public class LoginResult: MvcContrib.PortableAreas.ICommandResult
{
public bool Success { get; set; }
public string Message { get; set; }
public string Username { get; set; }
}
Create a LoginMessage class that also uses the ICommandResult interface with LoginResult. The LoginMessage class has a property for a LoginViewModel I use in my Login.cshtml view (it has Username, Password, and some other additional fields I needed for the view)
public class LoginMessage : ICommandMessage<LoginResult>
{
public LoginResult Result { get; set; }
public LoginViewModel Input { get; set; }
}
In the HttpPost action of the Login controller, create an instance of LoginMessage, passing in the LoginViewModel from the login view, and send it to the Host with MvcContrib.Bus.Send
[HttpPost]
public ActionResult Login(LoginViewModel mdl)
{
// TODO: Do basic auth here first, then send to Host for additional validation
// Create and send message to the Host
var message = new LoginMessage { Input = mdl, Result = new LoginResult() };
MvcContrib.Bus.Send(message);
if (message.Result.Success)
{
// Redirect to defaultUrl set in the Host's web.config
FormsAuthentication.RedirectFromLoginPage(mdl.Username, false);
}
return View("Login", "_Layout", mdl);
}
Note: LoginMessage sets a new empty LoginResult and then waits for Success. The Success is set by the Host (shown below). I do this because certain Host applications have specific additional logic that only apply to that application, so I let the Host do what it needs and return to the Portable to let it know if it passed or failed. Eventually, I will have the basic authentication logic in the Portable first and then let the Host do the extra work, but for the sake of this example I am keeping it simple.
In the Host web application (which has a reference to my Portable dll)
Create a handler for the Portable.LoginMessage so we can read it in the Host. Note that IsValidLogin is where I will eventually do my additional authentication logic to see if the user is valid
public class LoginHandler : MvcContrib.PortableAreas.MessageHandler<Portable.LoginMessage>
{
public override void Handle(Portable.LoginMessage message)
{
if (IsValidLogin(message.Input.Username, message.Input.Password))
{
message.Result.Success = true;
message.Result.Username = message.Input.Username;
}
else
{
message.Result.Message = "Username or Password was incorrect";
}
}
private bool IsValidLogin(string username, string password)
{
// TODO: Replace with actual authentication
return username.Equals("admin") && password.Equals("password");
}
}
In the web.config, set the defaultUrl that the Portable will redirect to in the controller I described earlier, when message.Result.Success is True. You do not need to be using Forms Authentication, the mode can be set to None, but you need to have the defaultUrl for this to work.
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Portable/Login" defaultUrl="~/Home/Index" />
</authentication>
</system.web>
That's it! This was a great exercise and learning experience for me. I am still figuring out the second part of my question where I need to send information to the Portable first (like the application title and assembly version) but I'm thinking I can do pretty much the same thing but in reverse, where I send an ICommandMessage to the Portable when my Host starts up (global.asax).
I want to use Application Insights to log exceptions only. How can I do that?
I tried searching for ways turning other settings off such as this and it says that there is no way to turn it off.
I tried ITelemetryProcessor and encountered the same problem as this question. I tried both config and code ways of registering ITelemetryProcessor but it is not hit even if I explicitly throw an exception in my Web API controller.
I am using VS 2017 and created a new .Net Framework 4.6.2 Web API. I also have an InstrumentationKey and can see the exception logged in Azure portal.
First of all, the first link you referenced is nothing to do with your issue.
You want to only log the exceptions, but that link means that remove the old telemetry data like Trace in repository(where the telemetry data is stored after upload to app insights).
You can take use of ITelemetryProcessor to log exceptions only. Please follow my steps as below:
1.Add Application insights to your web api project by right clicking your project name -> select Configure Application Insights:
After SDK added, do not select the Enable trace collection:
2.Add a .cs file in your project, then implement your custom ITelemetryProcessor class, code is as below:
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
namespace WebApplicationWebApi
{
public class ExceptionsFilter:ITelemetryProcessor
{
private ITelemetryProcessor Next { get; set; }
public ExceptionsFilter(ITelemetryProcessor next)
{
this.Next = next;
}
public void Process(ITelemetry item)
{
string s = item.GetType().Name;
//if it's not exception telemetry, just return without log it to app insights.
if (s != "ExceptionTelemetry")
{
return;
}
this.Next.Process(item);
}
}
}
3.Register your custom ITelemetryProcessor in the ApplicationInsights.config. In the node, add <Add Type="WebApplicationWebApi.ExceptionsFilter,WebApplicationWebApi"/> :
4.Then run your code. To make sure the custom ITelemetryProcessor class is called, you can set a breakpoint in that class to see if it's hit when running.
And for the testing purpose, I add some telemetry data in the HomeController.cs:
public class HomeController : Controller
{
TelemetryClient client = new TelemetryClient();
public ActionResult Index()
{
RequestTelemetry r1 = new RequestTelemetry();
r1.Name = "request message for testing";
client.TrackRequest(r1);
client.TrackTrace("trace message for testing wwwww.");
client.TrackException(new Exception("exception message for testing wwwww."));
ViewBag.Title = "Home Page";
return View();
}
}
5.In your visual studio output window, you should see these messages:
6.Then in visual studio, nav to Application Insights Search (in vs -> view -> other windows -> Application Insights Search), then check if there are some values here(if it has values like "4" in screenshot below, click on it):
7.If it has values in step 6, please click the update button, then check All:
8.Then you can see that only the Exceptions are logged:
Background
I've created a working bot in C# but I'm failing to expand it to be a multi-tenant bot. I have created multiple bots in the Microsoft portal using this technique to identify themselves from the messaging endpoint:
https://example.com/api/messages/bot1
https://example.com/api/messages/bot2
https://example.com/api/messages/bot3
I can grab the LastSegment from the URL while in the MessagesController and store it in PrivateConversationData so I know which bot is talking in the current conversation. I intended use this stored 'bot id' in order to retrieve the Microsoft AppId & Password from the web.config (the bot's credentials are stored as a series of custom entries and not the standard appSettings as that only works for a single bot).
Credentials Problem
The authentication works well (nearly) as described here except when using async code with .ConfigureAwait(false) I can't get the HttpContext.Current as it becomes null when running on a different thread. This means I can't get the authenticated user's credentials either by looking them up in the web.config or by calling GetCredentialsFromClaims() since I've lost the authenticated user. If I use .ConfigureAwait(true) I just get deadlocks all over the place.
I have the credentials in the web.config but they are stored per bot and I need the 'bot id' from the URL above in order to get the credentials.
Question
The crux of the problem is: I need the URL to get the 'bot id' and I need the 'bot id' to get the credentials from the web.config but I can never reliably get access to the URL once I've passed a .ConfigureAwait(false) in the code. On the flip side, I can't get the 'bot id' from the PrivateConversationData since I need the bot's credentials in order to load it. A bit chicken and egg :-(
If anyone has any ideas of what I may be doing wrong or has an alternative approach to know which 'bot id' is currently executing I'd very much appreciate it.
Thanks
Please find below given the sample code.
public class StartUp {
public void Configuration(IAppBuilder app) {
var builder = new ContainerBuilder();
//Note: Initialize / register the Metadata Service that can bring the tenant details from the corresponding store
builder.RegisterType<TenantMetadataService>().As<ITenantMetadataService>();
//Note: This helps you in accessing the TenantMetadata from any constructor going forward after the below registry
builder.Register(ti => TenantMetadata.GetTenantMetadataFromRequest()).InstancePerRequest();
//TODO: Register the various services / controllers etc which may require the tenant details here
}
}
public class TenantMetadata {
public Guid TenantId { get;set; }
public Uri TenantUrl { get;set; }
public string TenantName { get;set; }
public static TenantMetadata GetTenantMetadataFromRequest() {
var context = HttpContext.Current;
//TODO: If you have any header like TenantId coming from the request, you can read and use it
var tenantIdFromRequestHeader = "";
//TODO: There will be a lazy cache that keeps building the data as new tenant's login or use the application
if(TenantCache.Contains(...))return TenantCache[Key];
//TODO: Do a look-up from the above step and then construct the metadata
var tenantMetadata = metadataSvc.GetTenantMetadata(...);
//TODO: If the data match does not happen from the Step2, build the cache and then return the value.
TenantCache.Add(key,tenantMetadata);
return tenantMetadata;
}
}
Note
The above code snippet uses the various service placeholders, cache and the other methods which will require to be used based on the designed application services. If you wish not to cache the tenant metadata, if it may contain some sensitive data, you can remove the caching implementation parts.
This implementation can be spread across all your web facing portals like your Web UI, Web Api and WebJobs etc so that it is same across all apps and it is easy to test and consume.
HTH.
I have a code which changes the UserId as it is written all over.
I've implemented the IUserIdProvider:
public class CustomUserIdProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
string userId = "userName";
return userId;
}
}
And added this provider to the startup class (Configuration function):
var idProvider = new CustomUserIdProvider();
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);
I expect that all User Ids in all the hub will be "userName".
I know (or maybe not?) that in order to obtain the UserId in one of the Hub functions, I need to get the value of:
Context.User.Identity.Name
But in fact, this value doesn't contain "userName", but different values:
Empty when it's anonymous authentication
The Windows user name in case of Windows Authentication.
In the Hub, when I look at Clients.User("userName"), I see that it's the real connection Id, so something happened (and the debugger stops at the implemented GetUserId function), but still, In the production code I won't know anything about the Custom User Id, and I want to fetch it somehow...
More Info: VS2012, IIS Express.
I am using NServicebus(version 4.6.3) with SQLTransport in my ASP.net web api project. I have different connectionstrings for the queues for different environments (Dev,QA,etc). My configuration looks like below:
public class BusConfigurator
{
public static IStartableBus Bus { get; private set; }
public static void DisposeBus()
{
if (Bus == null)
return;
Bus.Shutdown();
Bus.Dispose();
Bus = null;
}
public static void InitializeServiceBus(string connectionString)
{
var configure = Configure.With()
.DefineEndpointName("MyEndPoint")
.Log4Net(new DebugAppender { Threshold = Level.Warn })
.UseTransport<SqlServer>(connectionString)
.PurgeOnStartup(false)
.SetDefaultTransactionLevel()
.UnicastBus(); // Error is thrown here on second call
configure.MyCustomSQLServerPersistence();
Bus = configure.CreateBus();
}
public static void StartBus()
{
Bus.Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install());
}
}
I have a dropdown in the app so that the user can select the environment. Based on the selection, I want to reconfigure the bus. So, I call DisposeBus then pass the connection string to the IntializeServiceBus method followed by the startBus. It works first time but throws error below when it gets called again with different connectionstring:
Unable to set the value for key: NServiceBus.Transport.ConnectionString. The settings has been locked for modifications. Please move any configuration code earlier in the configuration pipeline
Source=NServiceBus.Core
Line=0
BareMessage=Unable to set the value for key: NServiceBus.Transport.ConnectionString. The settings has been locked for modifications. Please move any configuration code earlier in the configuration pipeline
Is NServicebus intended to be used/configured this way? (I am guessing probably not) If not then is there a workaround/different approach for this?
In V4 or below, there is no way to do it by normal human means. There is only one Bus per AppDomain. All of the configuration API is static, so if you try, you get exactly the problems you ran into.
By "human means", I mean that it might be possible to do something crazy with spinning up a new AppDomain within your process, setting up a Bus within that, and then tearing it down when you're finished. It might be possible. I haven't tried it. I wouldn't recommend it.
In V5, the configuration API is completely redesigned, is not static, and so this is possible:
var cfg = new BusConfiguration();
// Set up all the settings with the new V5 Configuration API
using (var justOneBus = NServiceBus.Bus.Create(cfg).Start())
{
// Use justOneBus, then it gets disposed when done.
}
That's right. It's disposable. Then you can do it again. In your case you wouldn't want to put it in a using block - you would want to set it up somewhere, and when the dropdown gets switched, call Dispose on the current instance and rebuild it with the new parameters.
Keep in mind, however, that the Bus is still pretty expensive to create. It's definitely still something you want to treat as an application-wide singleton (or singleton-like) instance. You definitely wouldn't want to spin up a separate one per web request.