ClickOnce change WS URL dynamically - c#

I develop the ClickOnce application (C# Framework 4.5)
It access data through Web Service.
We have three environments: dev, test and prod.
Each environment - another server name.
I would like to make one publish and copy it to each environment with external tool.
Problem, to access correct url for web service for each environment.
I need to change url dynamically in code for each environment.
I add to app.config the WS references
<add key="ws_url_local" value="http://localhost/app/ws.asmx" />
<add key="ws_url_prod" value="http://server_prod/app/ws.asmx" />
<add key="ws_url_test" value="http://server_test/app/ws.asmx" />
<add key="ws_url_dvlp" value="http://server_dvlp/app/ws.asmx" />
I make a publish into folder, and then, another system copy the folder to
all environments.
How I can change dynamically the url by environment in code?
switch(fromWhereInstaled?){
case "prod":
url = ConfigurationManager.AppSettings["ws_url_prod"];
break;
case "test":
...
}
Thanks for help!

Found solution:
string url = ConfigurationManager.AppSettings["url_ws_local"];
if(ApplicationDeployment.IsNetworkDeployed)
{
ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
if(ad.ActivationUri.AbsolutePath.Contains("prodservername"))
{
url = ConfigurationManager.AppSettings["url_ws_prod"];
}
else if ad.ActivationUri.AbsolutePath.Contains("testservername")
.....
}
ws.Url = url;
But if you move your application without update ActivationUri, use this solution:
string url = ConfigurationManager.AppSettings["url_ws_local"];
if(AppDomain.CurrentDomain.SetupInformation.ActivationArgument != null && AppDomain.CurrentDomain.SetupInformation.ActivationArgument.ActivationData != null) // Network published
{
string strParam = AppDomain.CurrentDomain.SetupInformation.ActivationArgument.ActivationData[0];
if(strParam.Contains("prodservername"))
{
url = ConfigurationManager.AppSettings["url_ws_prod"];
}
else if ...
}
ws.Url = url;
But don't forget go to Properties=>Publish>Options=>Manifests, and check:
Allow URL parameters to be passed to application.
Also in this way you can change url for check updates.

Related

Cannot link bot to bot framework emulator

This is my current screen when I have tried to load my bot into the bot Framework Emulator:
And this is what I have entered within the setting for my bot:
But for some reason my bot framework emulator remains empty.
I have also tried setting the Endpoint URL to http://localhost:3979/api/messages but no luck. I am trying to run this locally off of visual studio.
Any help with this is much appreciated!
L. Full, if you followed the instructions from the Azure portal to create a QnA bot from a template, you will need to tweak the code a bit to have it work locally, and in turn work in the emulator.
After you have created your bot using the template (which it sounds like you have done), in ABS, going to Build (under Bot Management)> "Download zip file", you get a copy of your project locally.
If you look at the template Bot code, it works in Azure, because in summary, it is accessing your QnA credentials from within your Application Settings inside the Azure portal, but locally you will need to put the credentials somewhere like your .config file.
Ultimately what we'll have to do now is plug in your QnA credentials into your .config file of your project, as this is not automatically downloaded into the code when you download the zip.
Below I'm just using the QnA Template bot that you can find in the Azure portal (Create Resource > AI + Machine Learning > Web App Bot with Bot template of "Question and Answer")
In Web.config add key-value pairs for AzureWebJobsStorage (if using), QnAAuthKey, QnAKnowledgebaseId, and QnAEndpointHostName
Your own credential values can be found under Application Settings of the Azure portal
<appSettings>
<!-- update these with your Microsoft App Id and your Microsoft App Password-->
<add key="MicrosoftAppId" value="" />
<add key="MicrosoftAppPassword" value="" />
<add key="AzureWebJobsStorage" value="DefaultEndpointsProtocol=https...."/>
<add key="QnAAuthKey" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
<add key="QnAKnowledgebaseId" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
<add key="QnAEndpointHostName" value="https://YOURQNA.azurewebsites.net/qnamaker" />
<add key="QnASubscriptionKey" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
</appSettings>
In your Dialog (QnA template as of 7/5/18 has default dialog file named BasicQnAMakerDialog.cs), instead of Utils (default in template), we'll use ConfigurationManager.AppSettings["KeyName"] to access the values you just placed in your Web.config:
Below you can see I've changed the variables (commented out) in QnA template to retrieve values using ConfigurationManager.AppSettings. You may also have to edit the variables in your if-statement as well, depending on the logic your own app needs.
In Root Dialog
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result as Activity;
// OLD
//var qnaAuthKey = GetSetting("QnAAuthKey");
//var qnaKBId = Utils.GetAppSetting("QnAKnowledgebaseId");
//var endpointHostName = Utils.GetAppSetting("QnAEndpointHostName");
// NEW
var qnaAuthKey = ConfigurationManager.AppSettings["QnAAuthKey"];
var qnaKBId = ConfigurationManager.AppSettings["QnAKnowledgebaseId"];
var endpointHostName = ConfigurationManager.AppSettings["QnAEndpointHostName"];
// QnA Subscription Key and KnowledgeBase Id null verification
if (!string.IsNullOrEmpty(qnaAuthKey) && !string.IsNullOrEmpty(qnaKBId))
{
// Forward to the appropriate Dialog based on whether the endpoint hostname is present
if (string.IsNullOrEmpty(endpointHostName))
await context.Forward(new BasicQnAMakerPreviewDialog(), AfterAnswerAsync, message, CancellationToken.None);
else
await context.Forward(new BasicQnAMakerDialog(), AfterAnswerAsync, message, CancellationToken.None);
}
else
{
await context.PostAsync("Please set QnAKnowledgebaseId, QnAAuthKey and QnAEndpointHostName (if applicable) in App Settings. Learn how to get them at https://aka.ms/qnaabssetup.");
}
}
In the children Dialogs that get called by your root (BasicQnAMakerDialog for example), be sure to also replace anything that calls for a QnA key with ConfigurationManager.AppSettings["KeyName"].
For example in BasicQnAMakerDialog:
[Serializable]
public class BasicQnAMakerDialog : QnAMakerDialog
{
static readonly string qnaAuthKey = ConfigurationManager.AppSettings["QnAAuthKey"];
static readonly string qnaKBId = ConfigurationManager.AppSettings["QnAKnowledgebaseId"];
static readonly string endpointHostName = ConfigurationManager.AppSettings["QnAEndpointHostName"];
public BasicQnAMakerDialog() : base(new QnAMakerService(
new QnAMakerAttribute
(
qnaAuthKey,
qnaKBId,
"No good match in FAQ.",
0.5,
1,
endpointHostName
)))
{
}
}
One of the possible solution you can do is:
Right click your project in Solution Explorer in Visual Studio.
Click Properties.
Go to Debug Tab. Scroll down a little bit you will see Web Server Settings.. Check the URL and set it to new port. For example if it is: http://localhost:3798 change it to http://localhost:3979 or vice-versa. Changing the port number might solve your issue. Press Ctrl + S to save it.
Build the solution and re-run the project without Debugging (Ctrl +F5).
Open your Bot Framework Emulator and enter the URL that you mentioned in step 3 above.
Note: This doesn't mean it will 100% work, but this is one of the solution that I come across. I had the same problem and I solved it same way.
Hope this helps.

.cscfg file gives error when i specify <Setting name="serilog:write-to:AzureDocumentDB.endpointUrl" />

When I publish my API project to Azure here is the error what i'm getting.
Is there any way to resolve this issue and here is the code and the problem comes when this symbol ":" is init.
Here are some more details.
It's a web API Project
Version 4.6
Locally it's running without any issues but when it comes to release automation I should be able to change the value of endpointurl , key, and TTL manually so that I need to modify .csfg and .csdef file environment to environment. When i do that .csdef does not support that colon mark ":" so the build fails.
Expecting : Build is successful so that the log works as expected.
Actual : Build fails and log is not working as expected.
As you mentioned that ServiceDefinition:NamedElementNameString doesn't allow ':' (colon) in name. But we could add it with Azure friendly name. Then we could get it with following code. I also do a demo on my side, it works as expected.
var endpoint = RoleEnvironment.GetConfigurationSettingValue("endpointUrl");
var authorizationKey = RoleEnvironment.GetConfigurationSettingValue("authorizationKey");
var logger = new LoggerConfiguration()
.WriteTo.Console() //if no writeto.console there is no document in documentdb
.WriteTo.AzureDocumentDB(endpoint, authorizationKey)
.CreateLogger();
logger.Information("Tom Test");//log demo
About .csdef configuration please refer to the screenshot.
We could get more information from Configure Azure cloud service roles with Visual Studio
Check from Azure Portal:
Related serilog sdk
It seems this is not supported.
ServiceDefinition:NamedElementNameString doesn't allow ':' (colon) in name
So that I implemented custom configuration values and extract the values at run-time when the logger is initialized.
Here is the implementation.
.cscfg, .csdef, and web.config contains
<add key="LogEndpointUrl" value="xxxxxx/" />
<add key="LogAuthorizationKey" value="xxxxxxxxxxxxxxx=" />
<add key="LogTTL" value="1" />
When initializing got the values as follows from web.config
var endpoint = Common.Configuration.GetSetting(Constants.AppSettings.LogEndpointUrl);
var authorizationKey = Common.Configuration.GetSetting(Constants.AppSettings.LogAuthorizationKey);
int ttl = (int)Convert.ToInt64((Common.Configuration.GetSetting(Constants.AppSettings.LogTTL)));
And then
Log.Logger = new LoggerConfiguration().ReadFrom.AppSettings().WriteTo.AzureDocumentDB(endpoint, authorizationKey,timeToLive: ttl).CreateLogger();
// Used to debug serilog itself and confirm it is writing entries to document db
Serilog.Debugging.SelfLog.Enable(Console.Out);
var errorOrInformation = new Dictionary<string, string>();
errorOrInformation.Add(Constants.LoggingProperties.PartitionKey, logMetadata.PartitionKey);
errorOrInformation.Add(Constants.LoggingProperties.RowKey, logMetadata.RowKey);
//Add as many items as you want
Log.Verbose("Log Information Message {Information}", errorOrInformation);
// Also good idea to force flush of log entries before the app ends
Log.CloseAndFlush();

Using App Settings in Azure Cloud for ASP.net site Web.config

I was following some instructions on how to develop ASP.NET MVC applications, and I am adding the ability to log in using Facebook or Google.
I have heard that storing sensitive information in source code is bad practice. I modified my Web.config file like so:
<appSettings>
<add key="GoogClientID" value="X" />
<add key="GoogClientSecret" value="X" />
<add key="FbAppID" value="X" />
<add key="FbAppSecret" value="X" />
...etc...
this prevents me from storing sensitive information in the source code, and I was intending to override them by configuring the app in the Azure portal.
I access these settings from Startup.Auth.cs, whcih from my understanding is typical for this type of application.
//load from Web.config, to get app secrets.
System.Configuration.Configuration WebConfigForSecrets = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~/");
if (WebConfigForSecrets.AppSettings.Settings.Count > 0)
{
//google
System.Configuration.KeyValueConfigurationElement GoogClientID = WebConfigForSecrets.AppSettings.Settings["GoogClientID"];
if (GoogClientID != null)
{
System.Configuration.KeyValueConfigurationElement GoogClientSecret = WebConfigForSecrets.AppSettings.Settings["GoogClientSecret"];
if (GoogClientSecret != null)
{
//never reached when hosted on azure
app.UseGoogleAuthentication(
clientId: GoogClientID.Value.ToString(), //added .Value 3/9/16
clientSecret: GoogClientSecret.Value.ToString()); //added .Value 3/9/16
}
}
//facebook
System.Configuration.KeyValueConfigurationElement FbAppID = WebConfigForSecrets.AppSettings.Settings["FbAppID"];
if (FbAppID != null)
{
System.Configuration.KeyValueConfigurationElement FbAppSecret = WebConfigForSecrets.AppSettings.Settings["FbAppSecret"];
if (FbAppSecret != null)
{
//never reached when hosted on azure
app.UseFacebookAuthentication(
appId: FbAppID.Value.ToString(), //added .Value 3/9/16
appSecret: FbAppSecret.Value.ToString()); //added .Value 3/9/16
}
}
}
I tried to add the same key/value pairs to the "Application Settings" > "App settings" menu in the azure portal for my web application. It still acts as if it is not finding them.
I also tried going to Project -> Publish Web.config from visual studio. It said it was successful, but it didn't solve the issue.
I have also tried using an absolute path for the AppSettingsSecrets.config file, but I have not tried adding it to the project, since that seems like it may defeat the purpose suggested in the article.
Does anyone know how to do this / what the best practice is when it comes to this?

Programmatically retrieve the site URL from inside an Azure website

Azure websites have a default "site URL" provided by Azure, something like mysite.azurewebsites.net. Is it possible to get this URL from inside the website itself (i.e. from the ASP.NET application)?
There are several properties in the Environment and HttpRuntime class that contain the name of the website (e.g. "mysite") so that is easily accessible. Things are getting complicated when not the default but e.g. the staging slot of the site is accessed (that has the site URL like mysite-staging.azurewebsites.net).
So I was just wondering whether there is a straightforward way of getting this site URL directly. If not, then using one of the mentioned classes to get the site name and then somehow detecting the site slot (that could e.g. be set through a configuration value from the Azure portal) would be the solution
Edit (2/4/16): You can get the URL from the appSetting/EnvironmentVariable websiteUrl. This will also give you the custom hostname if you have one setup.
There are few of ways you can do that.
1. From the HOSTNAME header
This is valid only if the request is hitting the site using <SiteName>.azurewebsites.net. Then you can just look at the HOSTNAME header for the <SiteName>.azurewebsites.net
var hostName = Request.Headers["HOSTNAME"].ToString()
2. From the WEBSITE_SITE_NAME Environment Variable
This just gives you the <SiteName> part so you will have to append the .azurewebsites.net part
var hostName = string.Format("http://{0}.azurewebsites.net", Environment.ExpandEnvironmentVariables("%WEBSITE_SITE_NAME%"));
3. From bindingInformation in applicationHost.config using MWA
you can use the code here to read the IIS config file applicationHost.config
and then read the bindingInformation property on your site. Your function might look a bit different, something like this
private static string GetBindings()
{
// Get the Site name
string siteName = System.Web.Hosting.HostingEnvironment.SiteName;
// Get the sites section from the AppPool.config
Microsoft.Web.Administration.ConfigurationSection sitesSection =
Microsoft.Web.Administration.WebConfigurationManager.GetSection(null, null,
"system.applicationHost/sites");
foreach (Microsoft.Web.Administration.ConfigurationElement site in sitesSection.GetCollection())
{
// Find the right Site
if (String.Equals((string) site["name"], siteName, StringComparison.OrdinalIgnoreCase))
{
// For each binding see if they are http based and return the port and protocol
foreach (Microsoft.Web.Administration.ConfigurationElement binding in site.GetCollection("bindings")
)
{
var bindingInfo = (string) binding["bindingInformation"];
if (bindingInfo.IndexOf(".azurewebsites.net", StringComparison.InvariantCultureIgnoreCase) > -1)
{
return bindingInfo.Split(':')[2];
}
}
}
}
return null;
}
Personally, I would use number 2
Also, you can use Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME").
This will return full URL ("http://{your-site-name}.azurewebsites.net"), no string manipulations required.
To see a full list of properties available in environment variables just type Get-ChildItem Env: in SCM PowerShell debug console.

Accessing web.config from Sharepoint web part

I have a VS 2008 web parts project - in this project is a web.config file:
something like this:
<?xml version="1.0"?>
<configuration>
<connectionStrings/>
<system.web>
<appSettings>
<add key="MFOwner" value="Blah" />
</appSettings>
…….
In my web part I am trying to access values in the appSetting section:
I've tried all of the code below and each returns null:
string Owner = ConfigurationManager.AppSettings.Get("MFOwner");
string stuff1 = ConfigurationManager.AppSettings["MFOwner"];
string stuff3 = WebConfigurationManager.AppSettings["MFOwner"];
string stuff4 = WebConfigurationManager.AppSettings.Get("MFOwner");
string stuff2 = ConfigurationManager.AppSettings["MFowner".ToString()];
I've tried this code I found:
NameValueCollection sAll;
sAll = ConfigurationManager.AppSettings;
string a;
string b;
foreach (string s in sAll.AllKeys)
{
a = s;
b = sAll.Get(s);
}
and stepped through it in debug mode - that is getting things like :
FeedCacheTimer
FeedPageURL
FeedXsl1
ReportViewerMessages
which is NOT coming from anything in my web.config file....maybe a config file in sharepoint itself? How do I access a web.config (or any other kind of config file!) local to my web part???
thanks,
Phil J
Those values look like default SharePoint web.config properties. When you create a SharePoint web part, it gets deployed to the IIS virtual directory that hosts the SharePoint site which has it's own (very large) web.config file and NOT your application web.config file.
Depending on your production environment, you can update the SharePoint web.config file with the values you want and your code should work. If you cannot update the web.config file you can look into using a stand alone config file in the IIS directory that you deploy to (custom code then to read it) or possibly the property bag associated with each SP site collection for which you would need to add your values via code.

Categories