My goal is to inject values from the appsettings.json into nlog.config for an ASP.NET Core application. I am using NLog.Web.AspNetCore 4.8.3, NLog 4.6.5, NLog.config 4.6.5, and Microsoft.Extensions.Logging.Abstractions 2.0.0.
I wasn't able to get this working. I was under the impression that ${configsetting:name=ConnectionStrings.ApplicationDatabase} would be replaced with the ConnectionStrings.ApplicationDatabase value inside of my appsettings.json file but this does not work. The nlog.config variable value is unchanged and throws an error when I run my application because that is an invalid connection string.
Snippet of nlog.config
<!-- Using logDirectory variable to set path to src/logs folder in allfile and ownFile-web targets below -->
<variable name="logDirectory" value="${basedir}/../../../logs/${shortdate}/internal-nlog.log" />
<variable name="logDatabase" value="${configsetting:name=ConnectionStrings.ApplicationDatabase}"/>
<variable name="logDatabaseUser" value="${configsetting:name=DatabaseCredentials.User}"/>
<variable name="logDatabasePassword" value="${configsetting:name=DatabaseCredentials.Password}"/>-->
<variable name="logConnectionString" value="mongodb://${logDatabaseUser}:${logDatabasePassword}#${logDatabase}/myApplicationDB?authSource=admin"/>
<!-- Load the ASP.NET Core plugin -->
<extensions>
<add assembly="NLog.Web.AspNetCore" />
<add assembly="NLog.Mongo" />
</extensions>
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="allfile" fileName="${logDirectory}"
layout="${longdate}|${event-properties:item=EventId.Id}|${logger}|${uppercase:${level}}|${message} ${exception}" />
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
<target xsi:type="File" name="ownFile-web" fileName="${logDirectory}"
layout="${longdate}|${event-properties:item=EventId.Id}|${logger}|${uppercase:${level}}| ${message} ${exception}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
<target xsi:type="Mongo" name="error-mongo"
connectionString="${logConnectionString}"
collectionName="errorLogs">
<field name="date" layout="${date}" bsonType="DateTime" />
<field name="level" layout="${level}" />
<field name="message" layout="${message}" />
<field name="logger" layout="${logger}" />
<field name="exception" layout="${exception:format=tostring}" />
<field name="threadID" layout="${threadid}" bsonType="Int32" />
<field name="threadName" layout="${threadname}" />
<field name="processID" layout="${processid}" bsonType="Int32" />
<field name="processName" layout="${processname:fullName=true}" />
<field name="userName" layout="${windows-identity}" />
</target>
<target xsi:type="Mongo" name="event-mongo"
connectionString="${logConnectionString}"
collectionName="eventLogs">
<field name="date" layout="${date}" bsonType="DateTime" />
<field name="level" layout="${level}" />
<field name="event" layout="${event-properties:item=EventId.Id}" />
<field name="message" layout="${message}" />
<field name="logger" layout="${logger}" />
</target>
</targets>
Snippet of appsetting.json
"ConnectionStrings": {
"ApplicationDatabase": "App-db-server-1.com:27017,App-db-server-2.com:27017,App-db-server-3.com:27017/AccessManagement?ssl=true&replicaSet=myReplicaSet&authSource=admin"
},
"DatabaseCredentials": {
"User": "",
"Password": ""
}
}
Snippet of startup.cs
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
ConfigureNLog(app, loggerFactory);
/*These settings need to be changed*/
app.UseCors(
options => options
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
app.UseAuthentication();
//Swagger Set Up
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Authentication API V1");
});
app.UseMvc();
}
private static void ConfigureNLog(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
IConfigurationRoot config = new ConfigurationBuilder()
.AddJsonFile(path: "appSettings.json").Build();
NLog.Extensions.Logging.ConfigSettingLayoutRenderer.DefaultConfiguration = config;
}
**Snippet of Program.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.UseNLog()
.Build();
}
}
From https://github.com/NLog/NLog/wiki/ConfigSetting-Layout-Renderer
When calling UseNLog() from NLog.Web.AspNetCore or NLog.Extensions.Hosting then it will automatically register hosting environment configuration with ConfigSettingLayoutRenderer.
To manual register the Microsoft Extension IConfiguration with ${configsetting}:
IConfigurationRoot config = new ConfigurationBuilder()
.AddJsonFile(path: "AppSettings.json").Build();
NLog.Extensions.Logging.ConfigSettingLayoutRenderer.DefaultConfiguration = config;
Update, demo case here https://github.com/304NotModified/NLog-Demo-cases/tree/master/AspNetCore2WithConfigSetting
Related
I need to send NLog log messages to Kibana. Now I'm using NLog.Targets.ElasticSearch with structuredlogging.json but in this case elastic treats message as string and not as json. In general I need Kibana to treat message as object by fields of which I can make future analytics.
So how to force nlog or NLog.Targets.ElasticSearch to send data to elasticsearch to be treated as json?
Here is what i have now:
{
"_index": "logstash-2017.10.12",
"_type": "logevent",
"_id": "AV8QvCAHXFqCIKUdDl_1",
"_score": 1,
"_source": {
"#timestamp": "2017-10-12T13:18:05.0609218Z",
"level": "Error",
"message": """{"TimeStamp":"2017-10-12T13:18:05.060Z","Level":"Error","LoggerName":"testApp.Program","Message":"error","CallSite":"testApp.Program.Main","error":"error0"}"""
}
}
And it needs to be something like this:
{
"_index": "logstash-2017.10.12",
"_type": "logevent",
"_id": "AV8QvCAHXFqCIKUdDl_1",
"_score": 1,
"_source": {
"#timestamp": "2017-10-12T13:18:05.0609218Z",
"level": "Error",
"message": {
"TimeStamp":"2017-10-12T13:18:05.060Z",
"Level":"Error",
"LoggerName":"testApp.Program",
"Message":"error",
"CallSite":"testApp.Program.Main",
"error":"error0"
}
}
}
Current NLog.config looks so:
<target name="elastic" xsi:type="BufferingWrapper" flushTimeout="5000" >
<target xsi:type="ElasticSearch" layout="${structuredlogging.json}">
</target>
</target>
Maybe this will work:
<target name="elastic" xsi:type="BufferingWrapper" flushTimeout="500" >
<target xsi:type="ElasticSearch">
<field name="msg" layout="${structuredlogging.json}" layoutType="System.Object" />
</target>
</target>
Alternative you can do this (Without NLog.StructuredLogging.Json):
<target name="elastic" xsi:type="BufferingWrapper" flushTimeout="500" >
<target xsi:type="ElasticSearch" includeAllProperties="true">
<field name="TimeStamp" layout="${date:format=o}" />
<field name="Level" layout="${level}" />
<field name="LoggerName" layout="${logger}" />
<field name="Message" layout="${message}" />
<field name="CallSite" layout="${callsite}" />
<field name="error" layout="${exception:format=tostring}" />
</target>
</target>
Alternative you can do this (Using EcsLayout):
<extensions>
<add assembly="NLog.Targets.ElasticSearch"/>
<add assembly="Elastic.Apm.NLog"/>
<add assembly="Elastic.CommonSchema.NLog"/>
</extensions>
<targets>
<target xsi:type="ElasticSearch" enableJsonLayout="true">
<layout xsi:type="EcsLayout" />
</target>
</targets>
You can do it with:
<field name="MessageObject" layout="${message}" layoutType="System.Object" />
then make logging with:
_logger.LogInformation("{#ExampleObject}", exampleLoggingObject);
The output is:
"Message": {
"ExecutionTime": 12,
"Level": "Information",
"Type": "ABC",
....
}
I'm using angularJS and C#,
Due to my superior demand, i have to use HTML5 mode at all cost, mean no '#' sign.
And Again do to his request, i Used rewrite to provide ability to access the angular page even on page refresh.
Recently we implement phantomJS, the first page did worked well, till i notice other page doesn't, after digin-in in codes, i found out that, NO, it wont capture the whole URL, for example, for: http://localhost:xyzw/flights?_escaped_fragment_= it only capture the http://localhost:1350/?_escaped_fragment_=, which doesn't contain the 'flights' part. i also made some change in my rewrite config, which the last one was adding following rule to ignore these path for angular app, and process them directly: <add input="{REQUEST_URI}" matchType="Pattern" pattern="(.*)_escaped_fragment_=(.*)" ignoreCase="false" />
I separate my code for those who come and say hey shorten your issue, as whole of it can be,...
first part, my configs and small codes
second the tutorial i read about PhantomeJS
other files which may be required
Here are my main setup:
WebConfig Rewrite:
<rewrite>
<rules>
<!--<rule name="Seo rewrite rule" stopProcessing="true">
<conditions>
<add input="{QUERY_STRING}" pattern="(.*)_escaped_fragment_=(.*)" ignoreCase="false" />
</conditions>
<action type="Rewrite" url="http://service.prerender.io/http://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
</rule>-->
<rule name="Index Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Token" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/bundles/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Content/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Scripts/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/SiteMap/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/CallBackBank/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Error/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="(.*)_escaped_fragment_=(.*)" ignoreCase="false" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/HtmlSnapshot[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Flight[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Hotel[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Tour[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/TravelAgency[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Users[^s]?/" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
Phantom JS required Filter:
public class AjaxCrawlableAttribute : System.Web.Mvc.ActionFilterAttribute
{
private const string Fragment = "_escaped_fragment_";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.RequestContext.HttpContext.Request;
var url = request.Url.ToString();
if (request.QueryString[Fragment] != null && !url.Contains("HtmlSnapshot/returnHTML"))
{
url = url.Replace("?_escaped_fragment_=", string.Empty).Replace(request.Url.Scheme + "://", string.Empty);
url = url.Split(':')[1];
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "controller", "HtmlSnapshot" }, { "action", "returnHTML" }, { "url", url } });
}
return;
}
}
Route Config:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
//PhantomJS
routes.MapRoute(
name: "HtmlSnapshot",
url: "HtmlSnapshot/returnHTML/{*url}",
defaults: new {controller = "HtmlSnapshot", action = "returnHTML", url = UrlParameter.Optional});
////PhantomJS
//routes.MapRoute(
// name: "SPA",
// url: "{*catchall}",
// defaults: new {controller = "Home", action = "Index"});
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional});
}
PhantomJS for C# The tutorial that i read:
OK, since the tutorial i read about phantomjs is in non-english i post the thing i wrote for my later usage:
1.Install Package
Install-Package PhantomJS.exe -version 1.9.2.1
2.Create Helper
public class AjaxCrawlableAttribute : System.Web.Mvc.ActionFilterAttribute
{
private const string Fragment = "_escaped_fragment_";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.RequestContext.HttpContext.Request;
var url = request.Url.ToString();
if (request.QueryString[Fragment] != null && !url.Contains("HtmlSnapshot/returnHTML"))
{
url = url.Replace("?_escaped_fragment_=", string.Empty).Replace(request.Url.Scheme + "://", string.Empty);
url = url.Split(':')[1];
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "controller", "HtmlSnapshot" }, { "action", "returnHTML" }, { "url", url } });
}
return;
}
}
3.Replace Default Routes With:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "HtmlSnapshot",
url: "HtmlSnapshot/returnHTML/{*url}",
defaults: new { controller = "HtmlSnapshot", action = "returnHTML", url = UrlParameter.Optional });
//If doesn't work, use default route instead...:
routes.MapRoute(
name: "SPA",
url: "{*catchall}",
defaults: new { controller = "Home", action = "Index" });
}
4.Add AjaxCrawlableAttribute As A Filter
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AjaxCrawlableAttribute());
}
}
5.Create HtmlSnapshot Controller
public ActionResult returnHTML(string url)
{
var prefix = HttpContext.Request.Url.Scheme + "://" + HttpContext.Request.Url.Host + ":";
url = prefix + url;
string appRoot = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
var startInfo = new ProcessStartInfo
{
Arguments = string.Format("{0} {1}", "\"" + Path.Combine(appRoot, "Scripts\\seo.js") + "\"", url),
FileName = "\"" + Path.Combine(appRoot, "bin\\phantomjs.exe") + "\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
StandardOutputEncoding = System.Text.Encoding.UTF8
};
var p = new Process();
p.StartInfo = startInfo;
p.Start();
string output1 = p.StandardOutput.ReadToEnd();
p.WaitForExit();
var removeNgUiView = output1.Replace("<!-- ngView: -->", "").Replace("ng-view=\"\"", "");
removeNgUiView = Regex.Replace(removeNgUiView, "<!--\\suiView:\\s\\w*\\s-->", "");
removeNgUiView = Regex.Replace(removeNgUiView, "(<\\w+[^>]*)(ui-view(=\"\\w*\")?)([^<]*>)", "$1$4");
removeNgUiView = Regex.Replace(removeNgUiView, "(<\\w+[^>]*)(ng-app(=\"\\w*\")?)([^<]*>)", "$1$4");
ViewData["result"] = removeNgUiView;
return View();
}
6.Create Views of Controller
#{
Layout = null;
}
#Html.Raw(ViewData["result"])
7.Create seo.js in Script (!Important) Folder
var page = require('webpage').create();
var system = require('system');
var lastReceived = new Date().getTime();
var requestCount = 0;
var responseCount = 0;
var requestIds = [];
var startTime = new Date().getTime();;
page.onResourceReceived = function (response) {
if (requestIds.indexOf(response.id) !== -1) {
lastReceived = new Date().getTime();
responseCount++;
requestIds[requestIds.indexOf(response.id)] = null;
}
};
page.onResourceRequested = function (request) {
if (requestIds.indexOf(request.id) === -1) {
requestIds.push(request.id);
requestCount++;
}
};
function checkLoaded() {
return page.evaluate(function () {
return document.all["compositionComplete"];
}) != null;
}
// Open the page
page.open(system.args[1], function () {
});
var checkComplete = function () {
// We don't allow it to take longer than 5 seconds but
// don't return until all requests are finished
if ((new Date().getTime() - lastReceived > 300 && requestCount === responseCount) || new Date().getTime() - startTime > 10000 || checkLoaded()) {
clearInterval(checkCompleteInterval);
console.log(page.content);
phantom.exit();
}
}
// Let us check to see if the page is finished rendering
var checkCompleteInterval = setInterval(checkComplete, 300);
8.Layout.cshtml Based On:
<!DOCTYPE html>
<html ng-app="appOne">
<head>
<meta name="fragment" content="!">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
<base href="/">
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
<script src="~/Scripts/angular/angular.js"></script>
<script src="~/Scripts/angular/angular-route.js"></script>
<script src="~/Scripts/angular/angular-animate.js"></script>
<script>
angular.module('appOne', ['ngRoute'], function ($routeProvider, $locationProvider) {
$routeProvider.when('/one', {
template: "<div>one</div>", controller: function ($scope) {
}
})
.when('/two', {
template: "<div>two</div>", controller: function ($scope) {
}
}).when('/', {
template: "<div>home</div>", controller: function ($scope) {
}
});
$locationProvider.html5Mode({
enabled: true
});
});
</script>
</head>
<body>
<div id="body">
<section ng-view></section>
#RenderBody()
</div>
<div id="footer">
<ul class='xoxo blogroll'>
<li>one</li>
<li>two</li>
</ul>
</div>
</body>
</html>
NOTE: PhantomJS cannot process Persian Links(UTF-8)
Third part, other things you may required to know...
I don't see any other thing that may be involved, if you saw one ask me i'll edit my question.
I wrote a small article on it, just some simple settings, it is tested and working see this
Add in your routing:
$locationProvider.html5Mode(true);
Index Page:
<base href="/">
And web.config:
<system.webServer>
...........
</system.webServer>
You are done.
Here's what i did:
for configuration:
<rule name="Crawler" stopProcessing="false">
<match url=".*"/> <!-- rule back-reference is captured here -->
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_URI}" matchType="Pattern" pattern="(.*)_escaped_fragment_=(.*)" ignoreCase="true" negate="true" /><!-- condition back-reference is captured here -->
</conditions>
<action type="Rewrite" url="{R:0}" /><!-- rewrite action uses back-references to condition and to rule when rewriting the url -->
</rule>
<rule name="Index Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Token" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/bundles/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Content/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Scripts/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/SiteMap/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/CallBackBank/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Error/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/HtmlSnapshot[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Flight[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Hotel[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Tour[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/TravelAgency[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Users[^s]?/" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
And here's for Filter:
var request = filterContext.RequestContext.HttpContext.Request;
if (request.Url == null)
return;
var url = request.Url.Scheme + "://" + request.Url.Authority + request.RawUrl; //THIS LINE (`RowUrl` contains rest of the path)
I am creating an update to my companies system that will be running on several clients and I have two config files, the old config file and the newer version.
Is there any way that I can compare the both files and check the differences to add to the older file what I have new in the first one?
Keep in mind that the files may have different info and the only thing that I need to add/change is the keys. For example, if a key is different change the older to that new "version", if a key doesn’t exist in the older files add it.
they keys will have the exact same name but may have different values. Plus there could be a new key that doesnt exist in the older file and I need to add it
I will leave an example of the files for you to see,
Any help would be appreciated.
<configuration>
<appSettings>
<add key="ORCASRV1" value="ORCA30|tcp://127.0.0.1:9001" />
<add key="ORCASRV2" value="REORCA30|tcp://127.0.0.1:9001" />
<add key="ServidorEmail" value="xxx" />
<add key="SqlTrans" value="1" />
<add key="RemoteType" value="0" />
<add key="LocalPort" value="9002" />
<add key="LocalMsgStore" value="1" />
<add key="sqlCHAR_TO_DATA" value="CONVERT(datetime, '#MM#/#DD#/#YYYY#')" />
<add key="sqlDATA_TO_CHAR" value="CONVERT(char(30), #CAMPO#)" />
<add key="sqlDATAPARTE" value="LTRIM(STR(DATEPART(#PARTE, #CAMPO#)))" />
<add key="sqlNUM_TO_CHAR" value="LTRIM(STR(#VALOR#))" />
<add key="sqlSYSDATE" value=" GetDate() " />
<add key="sqlALIAS" value=" As " />
<add key="sqlCONCATENAR" value="+" /> <add key="sqlNULL" value="IsNull(#CAMPO#,#VALOR#)" />
<add key="sqlROUND" value="ROUND(#CAMPO#,#PARTE#)" />
<add key="sqlLPAD" value="RIGTH(REPLICATE('#CHAR#',#VEZES#)+#CAMPO#,#VEZES#)" />
<add key="oraCHAR_TO_DATA" value="TO_DATE('#MM#/#DD#/#YYYY#','MM/DD/YYYY')" />
<add key="oraDATA_TO_CHAR" value="TO_CHAR(#CAMPO#, 'DD/MM/YYYY')" />
<add key="oraDATAPARTE" value="TO_CHAR(#PARTE#, #CAMPO#)" />
<add key="oraNUM_TO_CHAR" value="TO_CHAR(#VALOR#)" />
<add key="oraSYSDATE" value=" SYSDATE " />
<add key="oraALIAS" value=" " />
<add key="oraCONCATENAR" value="||" />
<add key="oraNULL" value="NVL(#CAMPO#,#VALOR#)" />
<add key="oraROUND" value="ROUND(#CAMPO#,#PARTE#)" />
<add key="oraLPAD" value="LPAD(#CAMPO#,#VEZES#,#CHAR#)" />
<add key="EmailCDP" value="antonio.santos#cdp-si.pt" />
<add key="EmailCliente" value="xxx" />
<add key="RPT_PATH1" value="C:\PROD\ORCAREPORT\" />
<add key="StartPage_Height" value="90" />
<add key="StartPage_Margem" value="220" />
<add key="StartPage_Espaco" value="5" />
<add key="StartPage_Intervalo" value="2" />
<add key="StartPage_Mais" value="35" />
<add key="HelpExec" value="WINHLP32.EXE" />
<add key="HelpFile" value="ORCA.HLP" />
<add key="LogLevel" value="0" />
<add key="LogSqlClient" value="0" />
<add key="LogFile" value="C:\cdpsi\logs" />
</appSettings>
<system.runtime.remoting>
<application>
<channels>
<channel ref="tcp" port="9002">
<clientProviders>
<formatter ref="binary" /> <provider type="CdpCompress.CompressionClientSinkProvider, CdpCompress" />
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Code went like this and it works like a charm:
public void UpdateService(string FilePathOld, string FilePathNew, string LatestVersion)
{
Dictionary<string, string> Old = new Dictionary<string, string>();
Dictionary<string, string> New = new Dictionary<string, string>();
if (ExisteFicheiro(FilePathNew) == true && ExisteFicheiro(FilePathOld) == true)
{
ExeConfigurationFileMap configOld = new ExeConfigurationFileMap();
configOld.ExeConfigFilename = FilePathOld;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configOld, ConfigurationUserLevel.None);
ExeConfigurationFileMap configNew = new ExeConfigurationFileMap();
configNew.ExeConfigFilename = FilePathNew;
Configuration config2 = ConfigurationManager.OpenMappedExeConfiguration(configNew, ConfigurationUserLevel.None);
KeyValueConfigurationCollection settings = config.AppSettings.Settings;
Old = settings.AllKeys.ToDictionary(key => key, key => settings[key].Value);
KeyValueConfigurationCollection settings2 = config2.AppSettings.Settings;
New = settings2.AllKeys.ToDictionary(key => key, key => settings2[key].Value);
foreach (var NewKey in New)
{
string value;
if (Old.TryGetValue(NewKey.Key, out value))
{
if (value != NewKey.Value)
{
//if (ExistsKey(NewKey.Key, false) == true)
Old[NewKey.Key] = NewKey.Value;
}
}
else
{
Old.Add(NewKey.Key, NewKey.Value);
}
}
foreach (var NewKey in Old)
{
string key = NewKey.Key;
string value = NewKey.Value;
if (config.AppSettings.Settings[key] != null)
{
config.AppSettings.Settings[key].Value = value;
if (key == "Version")
config.AppSettings.Settings[key].Value = LatestVersion;
}
else
{
config.AppSettings.Settings.Add(key, value);
}
if (config.AppSettings.Settings["Version"] == null)
{
config.AppSettings.Settings.Add("Version", LatestVersion);
}
}
config.Save();
}
else
{
Erro NovoErro = new Erro();
Global.Erro = "O ficheiro \"OrcaService.exe.config\" ou o ficheiro \"Orca.exe.config\" não existem nos caminhos especificados!";
}
}
I have around 15,000 XML files in a folder, an example of a XML filename is; 000010000.img.xml
Each XML file contains specific information I need in a single text file.
Each XML file has the exact same structure aside from the information presented.
Here is what I want to focus in on (within the XML file)
<imgdir name="000010000.img">
<imgdir name="info">
<int name="version" value="10" />
<int name="cloud" value="0" />
<int name="town" value="0" />
<float name="mobRate" value="1.0" />
<string name="bgm" value="Bgm34/MapleLeaf" />
<int name="returnMap" value="10000" />
<string name="mapDesc" value="" />
<int name="hideMinimap" value="0" />
<int name="forcedReturn" value="999999999" />
<int name="moveLimit" value="0" />
<string name="mapMark" value="MushroomVillage" />
<int name="swim" value="0" />
<int name="fieldLimit" value="8260" />
<int name="VRTop" value="-892" />
<int name="VRLeft" value="-1064" />
<int name="VRBottom" value="915" />
<int name="VRRight" value="1334" />
<int name="fly" value="0" />
<int name="noMapCmd" value="0" />
<string name="onFirstUserEnter" value="" />
<string name="onUserEnter" value="go10000" />
<int name="standAlone" value="0" />
<int name="partyStandAlone" value="0" />
<string name="fieldScript" value="" />
</imgdir>
</imgdir>
<imgdir name="portal">
<imgdir name="0">
<string name="pn" value="sp" />
<int name="pt" value="0" />
<int name="x" value="-389" />
<int name="y" value="183" />
<int name="tm" value="999999999" />
<string name="tn" value="" />
</imgdir>
<imgdir name="1">
<string name="pn" value="sp" />
<int name="pt" value="0" />
<int name="x" value="-416" />
<int name="y" value="185" />
<int name="tm" value="999999999" />
<string name="tn" value="" />
</imgdir>
<imgdir name="2">
<string name="pn" value="sp" />
<int name="pt" value="0" />
<int name="x" value="-450" />
<int name="y" value="183" />
<int name="tm" value="999999999" />
<string name="tn" value="" />
</imgdir>
<imgdir name="3">
<string name="pn" value="out00" />
<int name="pt" value="2" />
<int name="x" value="1080" />
<int name="y" value="541" />
<int name="tm" value="20000" />
<string name="tn" value="in00" />
<string name="script" value="" />
<int name="hideTooltip" value="0" />
<int name="onlyOnce" value="0" />
<int name="delay" value="0" />
</imgdir>
</imgdir>
A batch file does not work; as you can see in my other thread: Batch script not working?
I need a C# application to open each XML file, grab specific information (that I will specify below), write that information into a single text file, and rinse and repeat till every XML file has been read.
Using the XML file snippet/actual XML file information posted above, here is how I need the text file text structure to be;
[10000]
total=4
sp 0 -389 183 999999999
sp 0 -416 185 999999999
sp 0 -450 183 999999999
out00 2 1080 541 20000
I just cannot wrap my head around on how to do this in a c# console application.
I am asking for help, anything is appreciated!
string[] files = Directory.GetFiles(#"SomeWhere");
List<string> result = new List<string>();
foreach (string file in files)
{
string[] lines = File.ReadAllLines(file);
// Grab information from the lines and store it in result
// by using result.Add(...) or result.AddRange(...)
}
File.WriteAllLines(#"AlsoSomewhere", result);
Here is a quick and dirty program I wrote up. I had to add a root to your XML file for it to play nice-nice with the program. I hope this helps!
Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace ConsoleApp5 {
public class Program {
public static void Main(string[] args) {
string test = getValuesOneFile("xmltest.xml");
Console.WriteLine(test);
Console.ReadLine();
}
public static string getValuesOneFile(string fileName) {
string finalString = "";
XElement xmlDocument = XElement.Load((fileName));
XElement infoImgDir = GetImgDir(xmlDocument, "info");
finalString += "[" + GetInt(infoImgDir, "returnMap") + "]\n";
finalString += "total=" + GetChildrenCount(GetImgDir(xmlDocument,"portal")) + "\n";
IEnumerable<XElement> portals = GetImgDir(xmlDocument, "portal").Elements();
foreach (XElement currentPortal in portals) {
finalString += GetString(currentPortal, "pn") + " ";
finalString += GetInt(currentPortal, "pt") + " ";
finalString += GetInt(currentPortal, "x") + " ";
finalString += GetInt(currentPortal, "y") + " ";
finalString += GetInt(currentPortal, "tm") + "\n";
}
return finalString;
}
public static XElement GetImgDir(XElement file, string imgDirName) {
return file.Descendants("imgdir").FirstOrDefault(x => (string)x.Attribute("name") == imgDirName);
}
public static int GetInt(XElement data, string attribName) {
var element = data.Descendants("int").FirstOrDefault(x => (string)x.Attribute("name") == attribName);
if (element == null)
return 0;
var value = (int?)element.Attribute("value");
if (value == null)
return 0;
return value.Value;
}
public static string GetString(XElement data, string attribName) {
var element = data.Descendants("string").FirstOrDefault(x => (string)x.Attribute("name") == attribName);
if (element == null)
return "";
var value = (string)element.Attribute("value");
if (value == null)
return "";
return value;
}
public static int GetChildrenCount(XElement data) {
return data.Elements().Count();
}
}
}
I have profile provider in my web.config
<profile defaultProvider="MyProvider">
<providers>
.......
<properties>
<add name="CustomField1" type="string" />
<add name="CustomField2" type="string" />
<add name="CustomField3" type="string" />
<add name="CustomField4" type="string" />
</properties>
</profile>
How can I get string[] array containing all avaliable properties (CustomField1, CustomField2....)
Edit:
Found working solution but not sure if it's the best and easiest one.
var allCustomProperties =
profile.GetType().GetProperties().Where(l => l.PropertyType.Name == "String" && l.CanWrite == true).Select(
l => l.Name).ToArray();
I'd go with that:
string[] props = ProfileBase.Properties.Cast<SettingsProperty>()
.Select( p => p.Name ).ToArray();
You have to import both System.Web.Profile and System.Configuration namespaces.