Deserialization of ulong failing for .NET Core - json.net

I'm doing a very simple deserialization of ulong:
static void Main(string[] args)
{
try
{
var data = ulong.MaxValue;
var serialized = JsonConvert.SerializeObject(data);
var res = JsonConvert.DeserializeObject<ulong>(serialized);
Console.WriteLine(res);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadKey();
}
In a normal console application, this works fine. But when doing this in a .NET Core Console Application, it fails with the following error:
JSON integer 18446744073709551615 is too large or small for an Int64. Path '', line 1, position 20.
It seems to me that this is trying to convert to a long instead of a ulong. What is the matter here? Is this a bug in JSON .NET or .NET Core?

It was a bug in Newtonsoft.Json that will be fixed started from 9.0.2 (related issue).
I have checked your code in my project and after adding direct referenced to "Newtonsoft.Json": "9.0.2-beta001" in project.json the problem is gone.

Related

dotnet core TopShelf Windows Service fails to start

I have a dotnet core console application build to connect to a Sql Service Broker instance to monitor table changes.
The app monitors one table that is updated from an ERP system and then publishes messages to our bus.
It runs fine when running as a console application, or debugging in my IDE.
I am having an issue when using TopShelf to configure it as a windows service.
Here is the entry point:
private static void Main(string[] args)
{
RegisterComponents();
var serviceHost = HostFactory.Run(sc =>
{
sc.Service<ISalesOrderMonitorService>(s =>
{
var sqlListener = _container.ResolveNamed<SqlDependencyEx>(ListenerKey.SalesOrder);
var changeHandler = _container.Resolve<ISalesOrderChangeHandler>();
var listenerConfig = _container.ResolveNamed<ListenerConfiguration>(ListenerKey.SalesOrder);
var logger = _container.Resolve<ILogger<SalesOrder>>();
s.ConstructUsing(f =>
new SalesOrderMonitorService(sqlListener, changeHandler, listenerConfig, logger));
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
});
var exitCode = (int) Convert.ChangeType(serviceHost, serviceHost.GetType());
Environment.ExitCode = exitCode;
}
The "worker" class:
public abstract class ServiceBase<T, TZ> : IService<T>
where T : IChangeHandler
{
protected readonly IChangeHandler ChangeHandler;
protected readonly SqlDependencyEx Listener;
protected readonly ListenerConfiguration ListenerConfiguration;
protected readonly ILogger<TZ> Logger;
protected ServiceBase(SqlDependencyEx listener, IChangeHandler changeHandler,
ListenerConfiguration listenerConfiguration, ILogger<TZ> logger)
{
Logger = logger;
ListenerConfiguration = listenerConfiguration;
Listener = listener;
ChangeHandler = changeHandler;
}
public virtual void Start()
{
try
{
Listener.TableChanged += (o, e) => ChangeHandler.Process(e);
Listener.Start();
Logger.LogDebug(
$"Listening to changes on the {ListenerConfiguration.Table} table in the {ListenerConfiguration.Database} database");
}
catch (Exception e)
{
Logger.LogError(e, e.Message);
throw;
}
}
public virtual void Stop()
{
Listener.Stop();
}
Install through TopShelf is no problem:
c:>{ServiceName}.exe install -username "serviceAccount" -password "superSecret" -servicename "ServiceName" -servicedescription "Description" -displayname "Service DisplayName" --autostart
When I go to start the service - I get this:
This is misleading because the event viewer shows this:
This is happening way faster than 30 seconds. This is definitely related to how I am configuring TopShelf.
As stated - the application works just fine when run "debug" or even as just an exe console.
I got it figured out. Actually both comments from #DotNetPadawan and #Lex Li indirectly got me there.
For starters - enabling the remote debugger clued me in that my appsetting.json was not being read into my IConfiguration. That was really confusing because everything works fine running locally with a debugger or even just starting the exe.
The link Lex Li points out did not provide the answer - however that article had this reference:
Host and Deploy aspnetcore as a Windows Service
It was here that I found this little nugget:
The current working directory returned by calling GetCurrentDirectory for a Windows Service is the C:\WINDOWS\system32 folder. The system32 folder isn't a suitable location to store a service's files (for example, settings files). Use one of the following approaches to maintain and access a service's assets and settings files.
The link explains how to conditionally set the current directory if the app is running as a service.
var isConsole = args.Contains("-mode:console");
if (!isConsole)
{
var pathToExe = Process.GetCurrentProcess().MainModule?.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
Putting this out there for anyone else that runs into this problem.
Admittedly - netcore 3.0 is likely the better way to go - but I don't have the bandwidth to upgrade everything is this repo (lots of shared stuff) to 3.0. I needed to get this working.

What's the correct way to set up NLog for logging in a .NET Core app?

What is the correct way to set up NLog logging for a .NET Core console application?
I see there is mention in Wiring and injected NLog into a .Net Core console application of using Microsoft.Extensions.Logging.ILogger somehow but no example and I'm having the same issue of getting a null. I tried the other examples in the posting but same thing, null object.
As an update, I got the code below to work by adding using statements for NLog.Config and then using the LogManager.GetCurrentClassLogger. I saw this in another post but I'm not sure why it works or if it's the correct way. So, I am still hoping someone can enlighten me on the correct way to get NLog setup in .NET Core or if this is the correct way, please let me know.
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Config;
using NLog.Extensions.Logging;
static void Main(string[] args)
{
var logger = LogManager.GetCurrentClassLogger();
//service collection are were we register our services
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
serviceCollection.AddLogging();
//service provider is where we get our services
serviceProvider = serviceCollection.BuildServiceProvider();
//logging (ILoggerFactory requires Microsoft.Extensions.Logging dependency and adding a using statement)
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
//NLog.LogManager.LoadConfiguration("NLog.config");
//AddNLog required adding dependency NLog.Extensions.Logging and then adding a using statement
loggerFactory.AddNLog().ConfigureNLog("NLog.config");
logger.Debug("I successfully logged a debug via NLog!");
}
The best and most recent way to add and use NLog with support for ASP.Net Core 5 is described on github
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using NLog.Web;
namespace ASP.NET_Core_5_NLog_Example
{
public class Program
{
public static void Main(string[] args)
{
var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateHostBuilder(args).Build().Run();
}
catch (Exception exception)
{
//NLog: catch setup errors
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(LogLevel.Trace);
})
.UseNLog(); // NLog: Setup NLog for Dependency injection
}
}

Capturing serialization error in Nancy

Edit: Issue on GitHub here.
Playing around with Nancy for the first time and wrote this simple endpoint to test content negotiation based on Accept header:
public HomeModule()
{
Get["/"] = _ => new { Foo = "Bar" };
}
Using Postman, I set Accept: application/json and the result is as expected, while Accept: text/xml yields the text:
There was an error generating XML document
After some trial and error I found that this is caused by the anonymous type, and this is a separate issue concerning the XmlSerializer. However, I can't figure out how to capture this serialization error anywhere. It's like it's "swallowed" somewhere in Nancy or ASP.NET. The above message is returned as text to the requester with status code 200 OK.
Despite having setup Visual Studio to break on all exceptions as well as hooking up to pipelines.OnError and Application_OnError, I get no indication that an error occurred. I'm uncertain whether this is a problem with the serializer in general, ASP.NET or Nancy (or if I'm missing something obvious).
// in bootstrapper's ApplicationStartup method:
pipelines.OnError += (ctx, ex) => {
System.Diagnostics.Trace.WriteLine(ex?.ToString()); // doesn't fire
return null;
};
// in Global.asax:
protected void Application_Error(object sender, EventArgs e)
{
var err = Server.GetLastError();
System.Diagnostics.Trace.WriteLine(err?.Message);
}
Why is this error not thrown/capturable?
Nancy uses .Net XmlSerializer to XML serializations. And .Net XmlSerliazer cannot serialize anonymous types:
https://github.com/NancyFx/Nancy/wiki/Extending-Serialization-with-Converters
Nancy uses the .NET Framework's own built-in XmlSerializer
infrastructure to handle clients sending and receiving data using XML
as the transport format.
Can I serialize Anonymous Types as xml?
Sorry, you cannot. The XML Serializer pretty much just serializes public read-write types.
You will need to either return a POCO (Plain-Old-CSharp-Object) like this
public class HomeModule:NancyModule
{
public class FooModel
{
public string Foo { get; set; }
}
public HomeApi()
{
Get("/", p =>
{
var r = new F { Foo = "Bar" };
return r;
});
}
}
or implement another XmlSerialiser
https://github.com/NancyFx/Nancy/wiki/Extending-Serialization-with-Converters
The design of XmlSerializer is quite
extensible, and the way in which it is extensible is different from
the JavaScriptConverter and JavaScriptPrimitiveConverter types that
JSON serialization employs. XmlSerializer is unaware of JSON
converters, and JavaScriptSerializer ignores XML-specific attributes.
Thus, extensions to XML serialization and to JSON serialization can
coexist in the same project without interfering with one another.
Based on Nancy's internals. The error message from Serialiser is written to the output i.e There was an error generating the XML document.. But details of the exception are not written anywhere.
https://github.com/NancyFx/Nancy/blob/master/src/Nancy/Responses/DefaultXmlSerializer.cs
catch (Exception exception)
{
if (this.traceConfiguration.DisplayErrorTraces)
{
var bytes = Encoding.UTF8.GetBytes(exception.Message);
outputStream.Write(bytes, 0, exception.Message.Length);
}
}
In order to see the error, you need to workaround it by turning off Just-My-Code in Visual Studio. You can do this by the following steps:
Debug->Options and then Uncheck Just-My-Code
And then go to Debug->Windows-Exception Settings (Ctrl+Alt+E). Check Common Language Runtime Exceptions

Log event datetime with.Net Core Console logger

I'm using logging to Console output, that built-in to .Net Core framework.
Here initialization of the logger:
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton(new LoggerFactory()
.AddConsole());
Also for logging I'm using Microsoft.Extensions.Logging.LoggerExtensions class with methods Log...
Here an example of logging in my App:
_logger.LogInformation(eventId, "Action is started.");
Where _logger is instance of ILogger<T> class and initialized in the class constructor with built-in dependency injection.
As result of calling of the above method Console output shows following string:
info: NameSpaceName.ClassName[eventId] Action is started.
I would like to display date-time in the Console output, that points to time, when the Log method is executed, but it seems that Log.. methods don't contain any methods that allow to display date time.
Does it exist some method or additioanl classes-formatters that allow to display the action datetime in console output without passing it to the method as part of the message?
The feature was added into version 3 of the Microsoft.Extensions.Logging.Console(here is the pr). You can activate this with setting the TimestampFormat:
new ServiceCollection()
.AddLogging(opt =>
{
opt.AddConsole(c =>
{
c.TimestampFormat = "[HH:mm:ss] ";
});
})
Example in .NET 5 (ASP.NET Core):
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(options =>
{
options.AddSimpleConsole(c =>
{
c.TimestampFormat = "[yyyy-MM-dd HH:mm:ss] ";
// c.UseUtcTimestamp = true; // something to consider
});
});
// ...
}
Output example:
[2020-12-13 12:55:44] info: Microsoft.Hosting.Lifetime[0] Application is shutting down...
For ASP.NET Core, you might prefer to use configuration file appsettings.json over wiring it directly into the code.
{
"Logging": {
"Console": {
"TimestampFormat": "[yyyy-MM-dd HH:mm:ss] "
}
}
}
This works out of the box, provided that Host.CreateDefaultBuilder() is invoked in Program.cs
Built-in .NET Core console logger doesn't log date-time. Track this issue to get more details. The easiest workaround is:
logger.Log(LogLevel.Information, 1, someObj, null, (s, e) => DateTime.Now + " " + s.ToString());
I wrote a custom console logger to automatically log the timestamp and do other useful tricks:
[2017.06.15 23:46:44] info: WebHost[1] Request starting HTTP/1.1 GET http://localhost:6002/hc

exePath exception when using SAP .net connector 3.0 in ASP.net

Have a WPF app that I am converting to ASP.net and I am having issues with SAP.
When I run this line I get an exception.
RfcDestinationManager.RegisterDestinationConfiguration(Backend);
Exception Message {"exePath must be specified when not running inside a stand alone exe."}
Stack Trace
at System.Configuration.ConfigurationManager.OpenExeConfigurationImpl(ConfigurationFileMap fileMap, Boolean isMachine, ConfigurationUserLevel userLevel, String exePath, Boolean preLoad)
at System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel userLevel)
at SAP.Middleware.Connector.RfcConfigParameters..cctor()
googling around I saw as similar issue here exePath must be specified when not running inside a stand alone exe
The issue seems to be using ConfigurationManager.OpenExeConfiguration rather that System.Web.Configuration.WebConfigurationManagerwhich is what I need to use. Problem is I can't change that as its part of the SAP.Net Connector.
Is there anything I can do?
Edit: My BackendConfig code
public class BackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if ("P38".Equals(destinationName))
{
var parms = new RfcConfigParameters
{
{RfcConfigParameters.AppServerHost, "SAPSERVER"},
{RfcConfigParameters.SystemNumber, "86"},
{RfcConfigParameters.SncMode, "1"},
{RfcConfigParameters.SncPartnerName, "p:SAP#SERVER"},
{RfcConfigParameters.Client, "010"},
{RfcConfigParameters.Language, "EN"},
{RfcConfigParameters.PoolSize, "5"}
};
return parms;
}
else return null;
}
// The following two are not used in this example:
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}

Resources