NewtonSoftJson DateTimeZoneHandling.Local net 3.1 vs net 5 - asp.net

I've migrated my application from net 3.1.8 to net 5.0.5
I'm using this configuration for pascal case and to automatically convert dates to local time:
services.AddControllersWithViews()
.AddNewtonSoftJson(opts => {
opts.SerializerSettings.ContractResolver = new DefaultContractResolver();
opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
opts.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local
}
My post action looks like this:
[HttpPost]
public async Task<IActionResult> ExportData([FromForm] DateTime reportDate)
{
}
Date format is 2021-04-15T21:00:00.000Z. In 3.1.8, reportDate was equal to 2021-04-16, but now in 5.0.5 it converts to 2021-04-15 and 9PM.
Any suggestions for the fix?

After some testing, apparently the DateTime model binder is different in 5.0.5.
Writing a custom model binder solved the problem for the [FromForm] parameter.
Looks like complex type parameters are handled by the Json.Net serializer global settings (opts.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local); so no problems there.

Related

XSLT3 Options for .NET Core in 2019

Has anyone got XSLT3 transforms working in .NET Core 2.x+ in 2019?
Seems that the request to MS for XSLT2/3 support hasn't moved forwards, and the Saxon people have other priorities, especially given the IKVM closedown.
Are there any other alternatives for in-process XSLT transformation? At the moment, it seems my only choice is to wrap something up via an external service or some undesirable (for us) COM-style approach that would involve lots of marshalling of data, hurting performance.
Unfortunately IKVM has never supported .NET Core, so the .NET version of Saxon cannot be made to work in that environment. In Saxonica we've been exploring alternative avenues for .NET support, but we haven't found anything remotely promising. (Anyone fancy doing a Kotlin implementation for .NET?)
I don't know what's possible using XMLPrime or Exselt, both of which target .NET.
2021 Update
Saxonica now ships SaxonCS on .NET 5, this product is built by converting the Java code of SaxonJ to C# source code using a custom transpiler.
There is one way how to use Saxon on .NET Core: via Transform.exe running as a process.
You can use code similar to this:
/// <summary>Transform XML inputFile using xsltFile and parameters. Save the result to the outputFile.</summary>
public void Transform(string inputFile, string outputFile, string xsltFile, NameValueCollection parameters)
{
//Search for the instalation path on the system
string path = GetInstalPath(#"Software\Saxonica\SaxonHE-N\Settings", "InstallPath");
string exePath = Path.Combine(path, "bin", "Transform.exe");
string parametersCmd = null;
//Set indicidual parameters
foreach (string parameter in parameters)
{
parametersCmd += String.Format("{0}={1} ", parameter, parameters[parameter]);
}
//set arguments for Transform.exe
string arguments = string.Format("-s:\"{1}\" -xsl:\"{0}\" -o:\"{3}\" {2}", xsltFile, inputFile, parametersCmd, outputFile);
//https://stackoverflow.com/questions/5377423/hide-console-window-from-process-start-c-sharp
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = exePath;
startInfo.Arguments = arguments;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
int waitingTime = 5 * 60 * 1000; //5 minutes; time in milliseconds
Process processTemp = new Process();
processTemp.StartInfo = startInfo;
processTemp.EnableRaisingEvents = true;
try
{
processTemp.Start();
processTemp.WaitForExit(waitingTime);
}
catch (Exception e)
{
throw;
}
}
static string GetInstalPath(string comName, string key)
{
RegistryKey comKey = Registry.CurrentUser.OpenSubKey(comName);
if (comKey == null)
return null;
string clsid = (string)comKey.GetValue(key);
return clsid;
}
SaxonCS EE has been released and works with .NET 5 and .NET 6 (RC/preview) and that way allows using XSLT 3, XPath 3.1 and XQuery 3.1 with .NET Core. It is only available under a commercial license however, but you can test it with a trial license, download from Saxonica is at https://www.saxonica.com/download/dotnet.xml, also on NuGet as https://www.nuget.org/packages/SaxonCS/.
In the meantime IKVM has been updated (https://www.nuget.org/packages/IKVM.Maven.Sdk) and is capable of producing .NET 3.1, .NET 5 and .NET 6 (aka .NET core) compatible cross-compilations. Using that I have managed to cross-compile Saxon HE 11.4 Java to .NET 6 and have published two command line apps/dotnet tools on NuGet to run XSLT 3.0 or XQuery 3.1:
XSLT 3.0: https://www.nuget.org/packages/SaxonHE11NetXslt/
XQuery 3.0: https://www.nuget.org/packages/SaxonHE11NetXQuery/
I have furthermore created an extension library to ease the use of the Java s9api from .NET code, it is on NuGet at https://www.nuget.org/packages/SaxonHE11s9apiExtensions/, the GitHub repository is at https://github.com/martin-honnen/SaxonHE11s9apiExtensions.
A simple example to run some XSLT 3.0 code with .NET 6, using the IKVM cross compiled Saxon HE 11, would be:
using net.sf.saxon.s9api;
using net.liberty_development.SaxonHE11s9apiExtensions;
//using System.Reflection;
// force loading of updated xmlresolver (no longer necessary with Saxon HE 11.5)
//ikvm.runtime.Startup.addBootClassPathAssembly(Assembly.Load("org.xmlresolver.xmlresolver"));
//ikvm.runtime.Startup.addBootClassPathAssembly(Assembly.Load("org.xmlresolver.xmlresolver_data"));
var processor = new Processor(false);
Console.WriteLine($"{processor.getSaxonEdition()} {processor.getSaxonProductVersion()}");
var xslt30Transformer = processor.newXsltCompiler().Compile(new Uri("https://github.com/martin-honnen/martin-honnen.github.io/raw/master/xslt/processorTestHTML5Xslt3InitialTempl.xsl")).load30();
xslt30Transformer.callTemplate(null, processor.NewSerializer(Console.Out));
A samples project showing various examples of XPath 3.1, XQuery 3.1 and XSLT 3.0 usage is at https://github.com/martin-honnen/SaxonHE11IKVMNet6SaxonCSSamplesAdapted.

Configure that Newtonsoft serializer serializes a date respecting the local time

I send javascript dates to the server via:
let start = new Date().toISOString()
This date string is sent as UTC date string.
On server side I have convert this utc date everywhere to the local date (CH/DE/AT) via myDate.ToLocalTime() which I want to save in the database.
I do not want this manual conversion at many places.
How can I fix this with newtonsoft json serializer ?
That I get always the current local time I do:
var cultureInfo = new CultureInfo("de-DE");
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
in my Startup.cs
You can add the below in your ConfigureServices method:
services.AddMvc()
.AddJsonOptions(o =>
{
o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
}
See DateTimeZoneHandling setting.
The DateTimeZoneHandling Enumeration defines DateTimeZoneHandling.Local as follows:
Local - Treat as local time. If the DateTime object represents a
Coordinated Universal Time (UTC), it is converted to the local time.

How do I make sure that a decimal value is passed with a "." separator when I consume an API in asp.net Core?

I made an API that looks like this
[HttpPost]
[Route("/products")]
public async Task<IActionResult> Add([FromBody]ProductDTO productDTO) {
Product newProduct = await _productsService.Add(productDTO);
return Ok(Mapper.Map<ProductDTO>(newProduct));
}
The ProductDTO has a Price property and has a decimal type.
I want to make sure that if a move my application to another server with a different locale, it will still accept a "." as separator. For instance, if I move it to a server that has a locale in Italy, I want to make sure that the separator won't change to ",".
Does setting the culture info in the Startup.Configure solve my problem?
var cultureInfo = new CultureInfo("en-US");
cultureInfo.NumberFormat.CurrencyDecimalSeparator = ".";
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
I tried to change the locale of the computer that I'm currently using for development, and it works. But I want to know if someone had problems when he/she deployed the application to another server.

DbFunctions.TruncateTime LINQ equivalent in EF CORE

I have the following functioning LINQ in my .net app
public ActionResult Index()
{
Dictionary<DateTime?, List<Event>> result;
result = (from events in db.Events.Include("Activity")
where events.IsActive
group events by DbFunctions.TruncateTime(events.DateTimeFrom) into dateGroup
select new { EventDate = dateGroup.Key, Events = dateGroup.ToList() }).ToDictionary(x => x.EventDate, x => x.Events);
return View(result);
}
When I use this in EF Core, I can't use DbFunctions. How can I rewrite this to make it work in Microsoft.EntityFrameworkCore ? I am using SQLite if that makes a difference.
In EF6 DbFunctions.TruncateTime is used instead of DateTime.Date property because for some reason the later is not supported.
In EF Core the former is not needed simply because DateTime.Date now is recognized and translated correctly.
group events by events.DateTimeFrom.Date into dateGroup
Unfortunately there is no documentation (yet) of what is supported, so as a general rule of thumb, always try the corresponding CLR method/property (if any) and check if it translates to SQL and how.
To use DbFunctions in ASP.NET CORE You must create an object.
var DbF = Microsoft.EntityFrameworkCore.EF.Functions;
Now you can easily use it.
var now = DateTime.Now;
int count = db.Tbl.Count(w => DbF.DateDiffDay(now, w.RegisterDate) <= 3);
more detail on github
DbFunctions are not supported yet for EF Core. However you can use "Raw Sql Queries".
You can find documentation of "Raw Sql Queries" here
And also you can track here for DbFunctions for EF Core
EF Core 3.0
I finally found an answer that works. The issue is that I was wanting to Group by Date on a DateTime column in the database.
The key for me was to use the EF.Property function. This allows the class to have the DateTime property which is used for adding that level of data, but below allowed me to then redefine it as a Date. However.. I suspect if I decalared the property on the class, it would have already allowed me to use the .Date function which it was not allowing me todo.
So the solution may rather be to define the property on the model, or use the below to define it in your query.
EF.Property(s, "dt").Date
Full code
var myData = _context.ActivityItems
.GroupBy(a => new { nlid = a.lid, nsd = EF.Property<DateTime>(a, "dt").Date })
.Select(g => new
{
g.Key.nlid,
g.Key.nsd,
cnt = g.Count()
});
I managed to rewrite this in Lambda as well and make it async. Seems to be working the same.
var temp = await _context.Events.Where(x => x.IsActive)
.Include(a => a.Activity)
.GroupBy(x => x.DateTimeFrom.Date)
.Select(g => new { EventDate = g.Key, Events = g.ToList() }).ToDictionaryAsync(x => x.EventDate, x => x.Events);
EF Core 2.0 now supports mapping database functions to static methods on your context.
Check out the section 'Database scalar function mapping' here - https://learn.microsoft.com/en-us/ef/core/what-is-new/
In my case, it is working in this way instead of DbFunctions.TruncateTime or EntityFunctions.TruncateTime-
result = _context.ActivityItems.Where(a => a.DateTimeFrom.Value.Date == paramFilter.DateTimeFrom.Value.Date);
It converts date first in the server side in where condition and then compare with parameter date value-
WHERE CONVERT(date, [a].[DateTimeFrom]) = #__paramFilter_DateTimeFrom_Value_Date_0

Json.Net with [WebMethod]s

I'm trying to swap out the Json formatter to Json.Net, so I can get ISO dates instead of "/Date(1379112467317)/"
I'm also letting .Net (WebForms) auto-magically handle Json serialization/deserialization through [WebMethod]s. Which don't seem to be using the Json.Net formatter.
In my global.asax, I can see the old MS Json formatter getting removed, and the new Json.net formatter added with the IsoDateTimeConverter.
But, my [Webmethod]s still come back with the old /Date()/ json strings instead of Iso dates. Do I have to do anything special in my global.asax for [Webmethod]s auto-magic to use the new formatter?
Here's the code in global:
As seen in: http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
var formatter = config.Formatters.Where(f => { return f.SupportedMediaTypes.Any(v => v.MediaType.Equals("application/json", StringComparison.CurrentCultureIgnoreCase)); }).FirstOrDefault();
if (formatter != null)
{
config.Formatters.Remove(formatter);
}
JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new IsoDateTimeConverter());
config.Formatters.Add(new JsonNetFormatter(serializerSettings));
I think the way that you setup the formatter looks fine for me. but how do you make sure it is being use in the webform returns, it doesn't happen automatically.
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public Something GetSomething()
{
}
how about you try that.

Resources