How can I use TimeZoneInfo.GetAdjustmentRules() in .net core? - c#

I'm writing some codes to deal with time zones, so naturally, I'm already having a bad day. In particular, I've really be struggling to get the DST adjustment rules for time zones. According the source code for TimeZoneInfo, there should be an instance method called .GetAdjustmentRules(). It was written early in 2015.
However, this console app doesn't compile targeting .net standard 1.6.
using System;
public class Program {
public static void Main() {
var tz = TimeZoneInfo.Local;
Console.WriteLine(tz.GetAdjustmentRules());
}
}
CS1061 'TimeZoneInfo' does not contain a definition for 'GetAdjustmentRules' and no extension method 'GetAdjustmentRules' accepting a first argument of type 'TimeZoneInfo' could be found (are you missing a using directive or an assembly reference?) timezonecore..NETCoreApp,Version=v1.0
In fact, when I retrieve a list of methods by reflection, I find that are none called .GetAdjustmentRules(). Interestingly, there is a private member called m_adjustmentRules, that I might be able to use instead.
But where is .GetAdjustmentRules()? If there's no way to use it, is there another way to get this information?

The "trick" is you don't directly reference mscorlib, you reference System.Runtime that is pulled in by dependencies of Microsoft.NETCore.App. The purpose of System.Runtime is to abstract away what actually implements all the System.* objects. The CoreCLR has implementations in mscorlib and the calls are forwarded to it. the CoreRT runtime for example has implementations in System.Private.CoreLib (some of which are pure C# implementations that are C++/native method calls in CoreCLR).
So to determine what you can actually call on TimeZoneInfo, this is the place to look for 1.0:
https://github.com/dotnet/corefx/blob/release/1.0.0/src/System.Runtime/ref/System.Runtime.cs
Both the branches release/1.0.0 and release/1.1.0 don't contain a definition of GetAdjustmentRules() but the master branch does so you can expect that method to be available in .NET Core 1.2.
The method will also be available when just targeting netstandard2.0 for libraries using NETStandard.Library. The reference for what will be available in netstandard2.0 is currently here:
https://github.com/dotnet/standard/tree/master/netstandard/ref

Related

Why can't I use the TextInfo.ToTitleCase method in the Humanizer library?

I downloaded the code for the Humanizer library from their GitHub page and was testing some changes in the code when I noticed a yellow "status warning" icon in the Intellisense window when looking at some methods on the TextInfo class:
I have never seen this icon in Intellisense before and am wanting to know what it means. I can do this method call without any errors in a normal app.
I am also unsure what Humanizer(netstandard1.0) - Not Available and Humanizer(netstandard2.0) - Available mean in this context.
Here is the code that I am using:
public string Transform(string input)
{
TextInfo textInfo = CultureInfo.InvariantCulture.TextInfo;
return textInfo.ToTitleCase(input);
}
which gives this error:
'TextInfo' does not contain a definition for 'ToTitleCase' and no accessible extension method 'ToTitleCase' accepting a first argument of type 'TextInfo' could be found (are you missing a using directive or an assembly reference?)
Why can't I use the TextInfo.ToTitleCase(...) method in the Humanizer library?
They are doing something called "multi-targeting" where their code produces two different versions of the library, one compatible with the netstandard1.0 API, and another compatible with the netstandard2.0 API:
<TargetFrameworks>netstandard1.0;netstandard2.0</TargetFrameworks>
TextInfo.ToTitleCase() was not added to the .Net Core until version 2.0, so it is not available to use if you are targeting any of the netstandard frameworks prior to version 2.0. See .NET Standard for a listing of which runtimes/platforms support which .Net Standard versions.
You have to limit your code to the API that is supported by the lowest API, unless you are using "conditional compilation" compiler directives. These are essentially where you provide alternate implementations of higher level API functions to the lower level target. See How to Multitarget in the Microsoft .Net Core docs for an example of this.
The reason to do this is to provide a smaller and usually less complex (code wise) version of the library that can be consumed in projects that can use the higher level API, but also a version where you can't use the higher level API.
am also unsure what Humanizer(netstandard1.0) - Not Available and Humanizer(netstandard2.0) - Available mean in this context.
The ToTitleCase method is not supported in .NET Core 1.0 (.netstandart = .NET Core) but it's supported in the 2.0 version. You might be using the one that's not supported hence you get the error.
You can look at the documentation of ToTitleCase to see in which versions it is supported.

Portable class library - Reference to type 'MarshalByRefObject' claims it is defined in 'mscorlib', but it could not be found

I have a normal Class Library with a function that converts a Byte Array to an Image.
Now I've deleted that Class Library and created a Portable Class Library with the same name and now the code does not seem to work anymore and gives me an error on the "FromStream"-function:
Reference to type 'MarshalByRefObject' claims it is defined in 'mscorlib', but it could not be found
using System;
using System.Drawing;
using System.IO;
namespace App.Converters
{
public static class Converter
{
public static Image ToImage(this byte[] byteArray)
{
try
{
return Image.FromStream(new MemoryStream(byteArray));
}
catch
{
throw new FormatException("Data is not an image");
}
}
}
}
My project is targeting:
.NET Framework 4.5
ASP.NET Core 1.0
Windows 8
Windows Phone 8.1
Xamarin.Android
Xamarin.iOS
Xamarin.iOS (Classic)
Is this because "something" is not supported in one of the frameworks I'm targeting? Then why does VS let me use and show it in auto-complete?
using System.Drawing;
That's probably a bit more relevant to what you did to get this error message. You used a sledgehammer to get that using directive recognized. We have to guess at it, but one way you might have done that is with Project > Add Reference > Browse button > pick System.Drawing.dll from the c:\windows\microsoft.net subdirectory. Seems to work just fine.
And presumably you used a similar kind of sledgehammer on mscorlib.dll to get MarshalByRefObject recognized. Although that's much harder to do since the IDE can tell that is not valid, mscorlib.dll is already included in the reference set. Maybe you edited the project file by hand, hard to guess.
Don't use sledgehammers.
A PCL project already has a reference to all of the framework assemblies you can possibly use. They are not listed individually in the References node of your project, they are collapsed in the single ".NET" node.
This isn't done to make your life miserable, it ensures that you cannot accidentally use a class that is not available on one of the targets you selected. Saves you from finding out at the worst possible time, after you've spent weeks writing the code, testing it to perfection on your dev machine and now try to run it on a phone. Kaboom, can't work. Up a creek, no paddle, weeks lost.
System.Drawing is only available on desktop machines. And likewise, MBRO is only available on the full version of the CLR, not the .NETCore version. Can't work, MBRO requires remoting, a feature that was cut from .NETCore to make it "core". You'll have to find another way to accomplish what you want to do. No guidance, the question isn't detailed enough.

'Catel.IoC.IServiceLocator' does not contain a definition for 'Instance'

I'm quite new to .NET development and Catel but is working on a project to learn it. At the moment the original developer is not present so I'll try my luck here instead.
To implement Undo/Redo the Memento Pattern support of Catel seems great so giving it a try but I have problems getting an instance of the MementoService. When I do in my ViewModel:
var mementoService = ServiceLocator.Instance.ResolveType<IMementoService>();
I get:
'Catel.IoC.IServiceLocator' does not contain a definition for 'Instance' and no extension method 'Instance' accepting a first argument of type 'Catel.IoC.IServiceLocator' could be found (are you missing a using directive or an assembly reference?)
I have noticed that when I do the same in my Model though I do not get this problem. Seems strange that I would need different ways of getting an instance of the service depending on where I try to get it.
I do not understand what I'm doing wrong. The NuGet Package Manager Console tells me I have Catel.Extensions.Memento 3.7 installed as well as Catel.Core, Catel.MVVM and Catel.Extensions.Controls 3.7. I'm using Visual Studio 2013 with .NET 4.0.
The interface does not have an Instance property, and it is obsolete.
You should use this:
1) In a static context:
Catel.IoC.ServiceLocator.Default
2) In a non-static context (takes care of scopes automatically):
var serviceLocator = this.GetServiceLocator();

TypeLoadException when using PCL in .NET application if called class contains [OnDeserialized] method

I am adapting an existing .NET class library to a Portable Class Library. I am using profile 78 (.NET 4.5, Windows Store 8, Windows Phone 8) in favor of profile 158 (which also targets Silverlight 5) because I want to be able to compile the unsafe code of the original library.
The .NET library contains quite a lot of classes marked [Serializable], so I have implemented a support PCL library containing a dummy SerializableAttribute implementation:
public class SerializableAttribute : Attribute { }
which is referenced from the main PCL library.
To sufficiently use the main PCL library in a .NET application while avoiding type name clashes I have also prepared a .NET support library (with the same strong name as the PCL support library), containing a type forwarding declaration:
[assembly: TypeForwardedTo(SerializableAttribute)]
and in my .NET application explicitly reference the .NET support library instead of the PCL one.
After having prepared all this and being able to successfully compile the PCL adapted library, I am re-using the unit tests from the original .NET library, now referencing the PCL main library and the .NET support library.
This generally works very well, but for unit tests that include a [Serializable] class with an [OnDeserialized] decorated method:
[Serializable]
public class Foo
{
[OnDeserialized]
private void DoSomething(StreamingContext context) { }
}
I get the following TypeLoadException:
Type 'Foo' in assembly 'MyPclAssembly' has method 'DoSomething' with an incorrect signature for the serialization attribute that it is decorated with.
(It can be noted that OnDeserializedAttribute is included in the portable subset, presumably because it is also recognized in [DataContract] serialization.)
I do not obtain the exception when running the unit tests on the original .NET library. I have carefully analyzed the method signature in the Foo class, and it is completely in line with the signature these (de-) serialization helper methods should have, see e.g. here. I have also tried changing the visibility of the [OnDeserialized] method to internal and public, to no avail.
What is the cause of this exception when using the PCL library, and what can I do to avoid it?
EDIT I have examined the IL code of the PCL library and the .NET library for the [OnDeserialized] method, and I can't see any relevant difference:
PCL
.method private hidebysig instance void DoSomething(valuetype [System.Runtime.Serialization.Primitives]System.Runtime.Serialization.StreamingContext context) cil managed
.NET
.method private hidebysig instance void DoSomething(valuetype [mscorlib]System.Runtime.Serialization.StreamingContext context) cil managed
The assembly references for StreamingContext are different, but I assume that the PCL System.Runtime.Serialization.Primitives assembly is simply a type forwarding assembly to mscorlib types?
For now, I have decided to exclude the [OnDeserialized] methods from my PCL project, since I do not plan to make use of serialization anyway. An answer to why I am experiencing the TypeLoadException is still welcome, though.
Yeah, this is a game you cannot win. Up front, the [Serializable] attribute is only ever relevant to the BinaryFormatter class, the one that implements binary serialization. That class is not available in the .NET Framework version that's resident on a Phone or a slate so there's no point in trying to make it work.
You are battling the notion of type identity in .NET. Which states that a type is not just identified by the namespace and type name but also the assembly from which it came. It is a very strong anti-DLL Hell countermeasure, the kind you are skirting with here by using types that won't be available on the target architecture.
And the cold hard fact is that in a 4.5 PCL library, the StreamingContext type lives in the System.Runtime.Serialization.dll assembly. An app that targets the desktop will use the one from mscorlib.dll. It is not a forwarded type, it is duplicated. The System.Runtime.Serialization.dll assembly is a small shim assembly with the express intent to isolate these dependencies and prevent DLL Hell.
Kaboom at runtime, it sees a method that has an argument with the wrong type identity.

Resolving extension methods/LINQ ambiguity

I'm writing an add-in for ReSharper 4. For this, I needed to reference several of ReSharper's assemblies. One of the assemblies (JetBrains.Platform.ReSharper.Util.dll) contains a System.Linq namespace, with a subset of extension methods already provided by System.Core.
When I edit the code, it creates an ambiguity between those extensions, so that I cannot use OrderBy, for instance. How could I solve this? I would like to use the core LINQ extensions, and not the ones from ReSharper.
I get the following error when trying to compile:
The call is ambiguous between the
following methods or properties:
'System.Linq.Enumerable.OrderBy<string,int>(System.Collections.Generic.IEnumerable<string>,
System.Func<string,int>)' and
'System.Linq.Enumerable.OrderBy<string,int>(System.Collections.Generic.IEnumerable<string>,
System.Func<string,int>)'
EDIT: I tried the suggestion below, unfortunately without luck. In the meanwhile, I "solved" the problem by removing references to System.Core. This way I could use the extensions provided by ReSharper DLL files.
I uploaded a sample program where I just imported the ReSharper DLL files I needed. I changed the alias of System.Core to SystemCore, added the extern alias directive, but it still didn't work. If I missed something, please let me know.
P.S. The references are to ReSharper v4.1 DLL files installed in the default directroy in "C:\Program Files\JetBrains\ReSharper\v4.1\...".
This is probably one of those rare cases where it makes sense to use an extern alias.
In the properties page for the reference to System.Core (i.e. under References, select System.Core, right-click and select "Properties"), change the "Aliases" value to "global,SystemCore" (or just "SystemCore" if it's blank to start with).
Then in your code, write:
extern alias SystemCore;
using SystemCore::System.Linq;
That will make all the relevant types etc in System.Core.dll's System.Linq namespace available. The name "SystemCore" here is arbitrary - you could call it "DotNet" or something else if that would make it clearer for you.
This isn't really an answer, but may provide an easier way for others to reproduce the issue (from the command-line - you could do it with two projects in Visual Studio if you want).
1) Create BadLinq.cs and build it as BadLinq.dll:
using System.Collections.Generic;
namespace System.Linq
{
public static class Enumerable
{
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
Func<T,bool> predicate)
{
return null;
}
}
}
2) Create Test.cs:
extern alias SystemCore;
using System;
using SystemCore::System.Linq;
static class Test
{
static void Main()
{
var names = new[] { "Larry", "Curly", "Moe" };
var result = names.Where(x => x.Length > 1);
}
}
3) Compile Test.cs specifying the extern alias:
csc Test.cs /r:BadLinq.dll /r:SystemCore=System.Core.dll
This fails with:
Test.cs(11,28): error CS1061:
'System.Array' does not contain a
definition for 'Where' and no
extension method 'Where' accepting a first argument of type
'System.Array' could be found
(are you missing a using directive or an assembly reference?)
If you change it to not try to use an extension method (i.e. Enumerable.Where) it works fine with the extern alias.
I think this may be a compiler bug. I've emailed a private mailing list which the C# team reads - I'll update this answer or add a new one when I hear back.
This is no longer an issue, since I am able to use the LINQ extensions, as provided by ReSharper DLL files, even while targeting .NET 3.0.
Mr. Skeet was right again! I am able to use full LINQ syntax, while targeting .NET 3.0 in the project's properties and not referencing System.Core!
In order for ReSharper to be as compatible as possible with the variety of solutions it is used with, it is built against .NET 2.0. LINQ, etc. came in in C# 3.0, so they are not available in that version of the Framework. So, JetBrains added in their own version.
The solution is to build your addin against .NET 2.0 as well.
I had the ambiguous reference problem using System.ComponentModel. Visual Studio was complaining that a DLL file exists in both v2 and v4. I was able to resolve it by removing the reference to the System DLL file and readding it.
One solution would be to move all your code out to a partial class that uses the ReSharper code. In there, you'd import only the ReSharper namespace and not System.Core.
In the rest of the partial class, you'd import all the other namespaces you need, including System.Core, but not the ReSharper namespace.
I had the same problem, even with extern alias, and I raised it as a compiler bug on Connect. The workaround for the time being is to forgo the extension method syntax.
The bug is fixed for Visual Studio 2010.
It is really a compiler error.
I had the same problem, and I solved it just by cleaning and rebuilding the project. After that the problem disappeared.
I had a similar situation. After two hours struggling I realized I had duplicate namespace names in my libraries. If you are using file Dynamic.cs published by Microsoft, the only thing you need to do is rename the current namespace to something else, and it will be fixed.
//Copyright (C) Microsoft Corporation. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace System.Linq.Dynamic <- for example to Linq.Dynamic
{
I found this same sort of ambiguity when use PagedList in MVC (.Net 4.5, MVC 5). I found that if I took the object for the argument that was ambiguous and cast it explicitly first, the problem was resolved. To if the ambiguity was between a method that takes System.Linq.Enumerable and one that takes System.Collections.Generic.IEnumerable as the parameter in question, and the source is of type System.Collections.Generic.IEnumerable, I don't use the extension method on it. I cast it.In this example, my repository method returns a List:
searchRequest.CaseSearchResults = csr.SelectMatchingCases(searchRequest);
var results = searchRequest.CaseSearchResults.AsEnumerable<CaseSearchResult>();
int pageNum = (int)(ViewBag.PageNum ?? 1);
var pageResults =results.ToPagedList<CaseSearchResult>(pageNum, 5);
Calling the extension method on searchRequest.CaseSearchResults caused the ambiguity error; explicitly casting to results and then calling the extension on that worked.

Categories