How to make app settings in .NET Core that translate to Azure app settings? - azure

I can't remember where I saw this but I followed the advice on a blog when setting up my app configuration for my .NET Core MVC application. I created a model like this to hold some settings my app needed:
public class BasePathSettings
{
public string BaseImageFolder { get; set; }
public string BaseApiUrl { get; set; }
}
My StartUp has this...
public void ConfigureServices(IServiceCollection services)
{
...
// this adds the base paths to container
services.Configure<BasePathSettings>(Configuration.GetSection("BasePathSettings"));
....
}
And the appsettings.json has this in it:
"BasePathSettings": {
"BaseImageFolder": "D:\\Images\\",
"BaseApiUrl": "http://localhost:50321/"
},
I inject the controllers that need this info like so....
private readonly BasePathSettings _settings;
public ClientsController(IOptions<BasePathSettings> settings)
{
_settings = settings.Value;
_client = new HttpClient();
_client.BaseAddress = new Uri(_settings.BaseApiUrl);
}
Running this on my localhost everything runs fine.
However, when I deploy this application to Azure I assumed I needed to create an application setting in the General Settings of the app service. So I made an app setting called BasePathSettings and copied the json for the setting into the value:
{ "BaseImageFolder": "imagePath", "BaseApiUrl": "apiUrl" }
It appears that Azure barfs when it's in the ConfigureServices code claiming that the web.config does not have the correct permissions in NTFS. I'm guessing the real culprit is how the json value is being read from the Azure application settings.
Can I even use json there? If so, does it need formatted differently?

Can I even use json there? If so, does it need formatted differently?
To add hierarchical structure settings to Azure web app, we could place a colon between the section name and the key name. For example,
use BasePathSettings:BaseImageFolder to set your folder
use BasePathSettings:BaseApiUrl to set your url

I made an app setting called BasePathSettings and copied the json for the setting into the value
Format should be -
basepathsettings:baseimagefolder (just single slash)
basepathsettings:baseapiurl

If you try to define "BasePathSettings" in a single WebApp setting that takes a json value, the GetSection will return null.
As a workarround, I use this extension method as a replacement of GetSection() :
public static T GetWebAppSection<T>(this IConfiguration config, string key)
where T:class
{
T configValue = config.GetSection(key).Get<T>();
if(configValue == default(T))
{
configValue = JsonConvert.DeserializeObject<T>(config[key]);
}
return configValue;
}

Related

Azure Search .NET SDK: SearchIndexClient & Dependency Injection

What's the recommended way to register a SearchIndexClient into DI container? (let's say we only have a single index)
Register it as singleton or transient?
The short answer is that you should register it as a singleton, as long as you make sure to avoid using properties and methods that aren't thread-safe. Most of them are thread-safe; just avoid setting mutable properties from multiple threads and you should be fine.
For a more in-depth discussion of why this is the recommended practice and how you can extend it for more complex scenarios, see this related question.
In an ASP.NET Core app, you can inject the client as follows.
Install the packages: Microsoft.Extensions.Azure, Azure.Search.Documents (this is the latest)
In the ConfigureServices method in startup.cs, register the client:
using Microsoft.Extensions.Azure;
public void ConfigureServices(IServiceCollection services)
{
services.AddAzureClients(builder =>
{
builder.AddSearchIndexClient(new
Uri("my resource url"), new
AzureKeyCredential("my resource key"));
});
services.AddControllers();
}
Now say you want to use the client in your controller, you can inject it like so:
public class MyApiController : ControllerBase
{
private readonly SearchIndexClient _searchIndexClient;
public MyApiController(SearchIndexClient searchIndexClient)
{
_searchIndexClient = searchIndexClient;
}
}
You may not want to put your credentials directly in ConfigureServices though, in which case you could store them in appsettings.Development.json (or appsettings.Production.json):
"SearchDocument": {
"endpoint": "my resource url",
"credential": { "key": "my resource key" }
}
and do this in ConfigureServices:
services.AddAzureClients(builder =>
{
builder.AddSearchIndexClient(
Configuration.GetSection("SearchDocument"));
});
Read more about Dependency injection with the Azure SDK for .NET.

How to read configuration from appsetings.json for custom serilog sink in asp.net core 2.0

I created the custom sentry sink inside my API project.
public class SentrySink : ILogEventSink {...}
I also created an extension method for my sink so that I can connect my sink
through logging configuration.
public static LoggerConfiguration Sentry(this LoggerSinkConfiguration loggerConfiguration,
string dsn,
string release = null,
string environment = null,
LogEventLevel restrictedToMinimumLevel = LogEventLevel.Information,
IFormatProvider formatProvider = null)
{...}
When I use
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Sentry(configuration["Sentry:Dsn"], restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Error)
Everything works fine and log's are in sentry.
But when I set up the configuration in appsetting.json as
"WriteTo": [
{ "Name": "Sentry" }, ...
without defining config in LoggerConfiguration the log's are not sent to sentry.
Do I need to implement something more so that I can use configs from appsetting.json?
Thanks.
You'll need to include at least "Args": {"dsn": ...}, because the dsn parameter is not optional.
You may also need a "Using": ["YourAssemblyContainingSink"] statement to ensure the configuration method is found, depending on the deployment target, and to check that the sink assembly is copied to your project's output directory.

CRM 2011 PLUGIN to update another entity

My PLUGIN is firing on Entity A and in my code I am invoking a web service that returns an XML file with some attributes (attr1,attr2,attr3 etc ...) for Entity B including GUID.
I need to update Entity B using the attributes I received from the web service.
Can I use Service Context Class (SaveChanges) or what is the best way to accomplish my task please?
I would appreciate it if you provide an example.
There is no reason you need to use a service context in this instance. Here is basic example of how I would solve this requirement. You'll obviously need to update this code to use the appropriate entities, implement your external web service call, and handle the field updates. In addition, this does not have any error checking or handling as should be included for production code.
I made an assumption you were using the early-bound entity classes, if not you'll need to update the code to use the generic Entity().
class UpdateAnotherEntity : IPlugin
{
private const string TARGET = "Target";
public void Execute(IServiceProvider serviceProvider)
{
//PluginSetup is an abstraction from: http://nicknow.net/dynamics-crm-2011-abstracting-plugin-setup/
var p = new PluginSetup(serviceProvider);
var target = ((Entity) p.Context.InputParameters[TARGET]).ToEntity<Account>();
var updateEntityAndXml = GetRelatedRecordAndXml(target);
var relatedContactEntity =
p.Service.Retrieve(Contact.EntityLogicalName, updateEntityAndXml.Item1, new ColumnSet(true)).ToEntity<Contact>();
UpdateContactEntityWithXml(relatedContactEntity, updateEntityAndXml.Item2);
p.Service.Update(relatedContactEntity);
}
private static void UpdateContactEntityWithXml(Contact relatedEntity, XmlDocument xmlDocument)
{
throw new NotImplementedException("UpdateContactEntityWithXml");
}
private static Tuple<Guid, XmlDocument> GetRelatedRecordAndXml(Account target)
{
throw new NotImplementedException("GetRelatedRecordAndXml");
}
}

Breeze & EFContextProvider - How to properly return $type when using expand()?

I am using Breeze with much success in my SPA, but seem to be stuck when trying to return parent->child data in a single query by using expand().
When doing a single table query, the $type in the JSON return is correct:
$type: MySPA.Models.Challenge, MySPA
However if I use expand() in my query I get the relational data, but the $type is this:
System.Collections.Generic.Dictionary 2[[System.String, mscorlib],[System.Object, mscorlib]]
Because of the $type is not the proper table + namespace, the client side code can't tell that this is an entity and exposes it as JSON and not a Breeze object (with observables, entityAspect, etc.).
At first I was using my own ContextProvider so that I could override the Before/After saving methods. When I had these problems, I reverted back to the stock EFContextProvider<>.
I am using EF5 in a database first mode.
Here's my controller code:
[BreezeController]
public class DataController : ApiController
{
// readonly ModelProvider _contextProvider = new ModelProvider();
readonly EFContextProvider<TestEntities> _contextProvider = new EFContextProvider<TestEntities>();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
[HttpGet]
public IQueryable<Challenge> Challenges()
{
return _contextProvider.Context.Challenges;
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<ChallengeNote> ChallengeNotes()
{
return _contextProvider.Context.ChallengeNotes;
}
}
Here's my BreezeWebApiConfig.cs
public static void RegisterBreezePreStart()
{
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{controller}/{action}"
);
}
Is there a configuration setting that I am missing?
Did you try "expanding" on server side? Is it needed to do expand on client side? I tried to do expand before but failed for me as well, did some research and decided I'd rather place it on server:
[HttpGet]
public IQueryable<Challenge> ChallengesWithNotes()
{
return _contextProvider.Context.Challenges.Include("ChallengeNotes");
}
This should be parsed as expected. On client side you would query for "ChallengeNotes" instead of "Challenges" and you wouldn't need to write expand part.
I strongly suspect that the problem is due to your use of the [Queryable] attribute.
You must use the [BreezeQueryable] attribute instead!
See the documentation on limiting queries.
We are aware that Web API's QueryableAttribute has been deprecated in favor of EnableQueryAttribute in Web API v.1.5. Please stick with BreezeQueryable until we've had a chance to write a corresponding derived attribute for EnableQuery. Check with the documentation for the status of this development.

WCF Data Service with EF complex type

I'm just playing with EF5 and Data Services. Decided to test exposing SP. Mapped it to FirmInfo complex type. Running in this stupid error. Cannot seem to figure it out.
I have this complex type .tt template created for me
public partial class FirmInfo
{
public int FirmID { get; set; }
public string Name { get; set; }
}
I added this to expose it to MyDataService.svc.cs class:
[WebGet]
public IQueryable<FirmInfo> pSPTest(int id)
{
return CurrentDataSource.pSPTest(id).AsQueryable();
}
I can see it in browser as such:
- <pSPTest xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
- <element m:type="DB.FirmInfo">
<FirmID m:type="Edm.Int32">1</FirmID>
<Name>Firm Name</Name>
</element>
</pSPTest>
but when consuming by c# client app I keep getting this error:
The property 'element' does not exist on type 'Client.ServiceReference.FirmInfo'. Make sure to only use property names that are defined by the type.
any help appreciated
How are you consuming the result with the C# client app? If you're using the WCF Data Services client, you should be calling Execute<T>() on the DataServiceContext.
For guidance on how to use the WC Data Services client to call service operations, check out this documentation: http://msdn.microsoft.com/en-us/library/hh230677.aspx
You could also achieve this by stating the result of the operation as the collection type that you expect, like this.
var query = context.CreateQuery<ObservableCollection<wsAccountView.organisation>>("GetOrganisationsByUserName").AddQueryOption("UserName", #"'SFN\AO'");
var Organisations = query.ToList();

Resources