ricosuter/namotion.reflection

Storage | Messaging | Reflection

Validate nullability (C# 8)

Storage | Messaging | Reflection

Namotion.Reflection

Storage | Messaging | Reflection

.NET library with advanced reflection APIs like XML documentation reading, Nullable Reference Types (C# 8) reflection and string based type checks.

This library is mainly used in NJsonSchema and NSwag.

Contextual and cached types

Inheritance hierarchy:

  • CachedType: A cached Type object which does not have a context
    • ContextualType: A cached Type with contextual attributes (e.g. property attributes)
      • ContextualParameterInfo
      • ContextualMemberInfo
        • ContextualPropertyInfo
        • ContextualFieldInfo

Behavior:

  • Each CachedType instance is cached per Type, ParameterInfo or MemberInfo.
  • Contextual and type attributes are evaluated only once and then cached for higher performance.
  • If the original Type is Nullable<T> then T is unwrapped and stored in the Type property - the original type can be accessed with the OriginalType property.

Nullability reflection (C# 8)

With the ContextualType class you can reflect on the nullability of properties, fields, method parameters and return types which will be available when compiling with the C# 8 compiler with the Nullable Reference Types feature enabled.

Given the following test class with some C# 8 nullability annotations (?):

#nullable enable

public class MyClass
{
    public void MyMethod(Dictionary<string, string?> dictionary)
    {
    }
}

To reflect on the first parameter's nullability, we can load a ContextualType instance and display the nullability of the parameter's types:

using Namotion.Reflection;

var method = typeof(MyClass).GetMethod(nameof(MyClass.MyMethod));
var parameter = method.GetParameters().First();
var contextualParameter = parameter.ToContextualParameter();

Console.WriteLine("Dictionary: " + contextualParameter.Nullability);
Console.WriteLine("Key: " + contextualParameter.GenericArguments[0].Nullability);
Console.WriteLine("Value: " + contextualParameter.GenericArguments[1].Nullability);

The output is:

Dictionary: NotNullable
Key: NotNullable
Value: Nullable

For more details, see https://blog.rsuter.com/the-output-of-nullable-reference-types-and-how-to-reflect-it/

Methods:

  • CachedType.ClearCache()

It is important to understand that Nullable Reference Types is a compiler feature only and the .NET runtime does not do any checks when your app is running. Consider the following class:

public class Person
{
    public string FirstName { get; set; }

    public string? MiddleName { get; set; }

    public string LastName { get; set; }
}

Inside your application you'll get warnings when you forget to set the FirstName. However when data is coming from outside (e.g. via reflection, serialization, etc.) you could end up with invalid objects. This JSON.NET call throws no exception but will create an invalid object:

var person = JsonConvert.DeserializeObject<Person>("{}");

Call the EnsureValidNullability() extension method which throws an InvalidOperationException when the object is in an invalid state:

person.EnsureValidNullability();

Methods:

  • HasValidNullability();
  • EnsureValidNullability();
  • ValidateNullability();

Read XML Documentation

Methods:

  • Type|MemberInfo.GetXmlDocsSummaryAsync():

  • Type|MemberInfo.GetXmlDocsRemarksAsync():

  • ParameterInfo.GetXmlDocsAsync(): Gets the parameter's description

  • ParameterInfo.GetXmlDocsElementAsync(): Gets the XElement of the given type

  • ... and more

  • XmlDocs.ClearCache()

This functionality can also be used with Cecil types with the Namotion.Reflection.Cecil package.

Extension methods

Methods:

IEnumerable extensions

  • GetAssignableToTypeName(): Gets all objects which are assignable to the given type name as string.
  • FirstAssignableToTypeNameOrDefault(): Tries to get the first object which is assignable to the given type name as string.
  • GetCommonBaseType(): Finds the first common base type of the given types.

Object extensions

  • HasProperty(): Determines whether the specified property name exists.
  • TryGetPropertyValue(): Returns the value of the given property or null if the property does not exist.

Type extensions

  • IsAssignableToTypeName()
  • InheritsFromTypeName()
  • GetEnumerableItemType()
  • GetDisplayName(): Gets a human readable identifier for the type (eg. "DictionaryOfStringAndInt32").
Issues

Quick list of the latest Issues we found

ryanwilliams83

ryanwilliams83

Icon For Comments0

I'm trying to use JsonSchema.FromType<System.DirectoryServices.AccountManagement.UserPrincipal>() but it's throwing an InvalidOperationException.

I suspect the reason is that ArrayList.GetEnumerator has an overload which is not working with https://github.com/RicoSuter/Namotion.Reflection/blob/master/src/Namotion.Reflection/Context/ContextualType.cs#L146

I don't fully understand your code but I'd like to suggest the following possible solution var getEnumeratorMethod = Methods.SingleOrDefault(m => m.Name == "GetEnumerator" && m.Parameters.Length == 0);

bessgeor

bessgeor

Icon For Comments5

What am I trying to do?

Generate NRT-compatible classes using Reflection.Emit

What do I expect?

An emitted class having the same attributes as a coded one provides the same Nullability values for every property

What happens

Despite of having the very same attributes on emitted class, every emitted property is having the Unknown nullability while the coded ones are having the Nullable nullability.

I don't think this is a Namotion.Reflection bug, just trying to find someone to help me finding out what am I missing =)

Repro: https://github.com/bessgeor/namotion-reflection-emit-vs-roslyn (asserts the observed behavior).

PhilipAlexanderWallin

PhilipAlexanderWallin

Icon For Comments0

I noticed "Unknown" nullability was given for a nested class when the parent class contains an async method. The use case I have is I have an async unit test which use a nested test class for the unit tests of a factory method. The following is a minimal example of the problem, given the method "Test" in run in a nullable context, note that it works as expected if the async "Test" method is removed:

InspiringCode

InspiringCode

Icon For Comments4

Whenever I try to call Add-Type -Path "D:\Test\Namotion.Reflection.dll" in a PowerShell session it fails with Could not load file or assembly 'Namotion.Reflection, Version=1.0.12.0, Culture=neutral, PublicKeyToken=c2f9c3bdfae56102'.

The loading of a big .NET Core Project even with Entity Framework Core succeeds. Just the Namation.Reflection assembly cannot be loaded. Do you have any ideas what the issue could be? Any special dependencies or such? Some ideas that I could try?

ProductiveRage

ProductiveRage

Icon For Comments1

It seems that the contents of list types are not correctly checked for null whether or not non-nullable-reference-type behaviour is enabled - for example, the below code will throw a System.NullReferenceException if reference types are allowed to be null or not:

The expected behaviour for the above code is that an InvalidOperationException should be throw if the non-nullable-reference-types behaviour is enabled and nothing should happen if it's not.

I've created PR #40 to pull in some unit tests to help you diagnose.

I tried poking around with the code to see if I could create a PR with a possible solution but I'm afraid that I got lost! The first problem is that in "ValidateNullability" there is a call to obj.GetType() that doesn't check whether "obj" is null (which is why there is NullReferenceException regardless of whether the value should be allowed to be null or not) but I think that the "IEnumerable" handling has to take into account the type of items in the list and whether they're allowed to be null - I got stuck trying to do that; I could dig around and get the types of "T" that any given enumerable type might implement IEnumerable for but I failed to correctly determine whether those "T" values should be Unknown, Nullable or NotNullable. I then had a quick look at the documentation here and got completely lost trying to track how to work out what generic type parameters should be marked as nullable or not!

Please let me know if I can include any additional information, example cases or other help.

danielmeza

danielmeza

Icon For Comments2

There are severals issues asociated to multiple documentation files or missed documentation files.

NSwag: 2161 Namotion.Reflection: 33

We need to provide a way to include documentation files sources or just add it to the Cache.

jeremyVignelles

jeremyVignelles

bug
Icon For Comments19

I have some models exposed in my APIs that come from NuGetPackages.

While those types are properly documented (I can see the doc in intellisense), they have no documentation in the generated OpenApi spec.

This does not happen on netcoreapp2.1, but does happen on netcoreapp3.1.

To highlight this issue, please see this repro: 3.1 branch : https://github.com/jeremyVignelles/TestNSwagNetCoreApp/blob/repro/doc-external-models/TestNSwagNetCoreApp/Hello.cs#L42

2.1 branch: https://github.com/jeremyVignelles/TestNSwagNetCoreApp/blob/repro/doc-external-models-netcoreapp2.1/TestNSwagNetCoreApp/Hello.cs#L42

I used a type from the NSwag NuGet package to highlight the issue.

LockTar

LockTar

enhancement
Icon For Comments13

Hi,

I'm trying to help with the following issue that generates documentation via NSwag in Azure Functions v2. I know what the problem is but I need to determine where NSwag get's the xml documentation from.

I think it's coming from this package.

In order to solve the issue, I need to be able to set a custom xml file path. In Azure Functions on the server, this is different then the path when you locally develop. So I tracked the code through NSwag to this package and ended up with the method GetXmlDocsPath.

So the ultimate questions are:

  1. Is this the correct line that NSwag is using to obtain the XML file for the documentation?
  2. If so, How can I set a custom path to the XML file in NSwag and then of course here in this package?

Library Stats (Sep 12, 2022)

Subscribers: 3
Stars: 129
Forks: 32
Issues: 18

Azure IoT Edge Connector for Kubernetes

Azure IoT Edge Connector leverages the IoT Edge Deployment and submits it to the backing IoT hub

Azure IoT Edge Connector for Kubernetes

Yamlizr - Azure DevOps Designer-to-YAML Pipeline Conversion Tool

Export to YAML feature which allows you to export a Build pipeline to YAML with a single click

Yamlizr - Azure DevOps Designer-to-YAML Pipeline Conversion Tool

Azure API Management DevOps Resource Kit

They have become the de facto standard for connecting apps, data, and services

Azure API Management DevOps Resource Kit

Azure Image Optimizer

An Azure App Services WebJob that compresses all images being uploaded or deployed to your website

Azure Image Optimizer

# Azure Artifacts Credential Provider

The Azure Artifacts Credential Provider automates the acquisition of credentials needed to restore NuGet packages as part of your

# Azure Artifacts Credential Provider

Azure DevOps CI

Technical support, chat and collaboration at Discord: Parity or to browse and execute all the different samples on how to use Nethereum directly in your...

Azure DevOps CI

Microsoft Azure IoT Protocol Gateway

Azure IoT protocol gateway is a framework for protocol adaptation that enables bi-directional communication with Azure IoT Hub

Microsoft Azure IoT Protocol Gateway

Kiota Azure Identity authentication provider library for dotnet

The is the authentication provider implementation with Kiota generated project will need a reference to a authentication provider library to authenticate HTTP requests to an...

Kiota Azure Identity authentication provider library for dotnet

Microsoft Azure Storage SDK for

If you would like to access our latest

Microsoft Azure Storage SDK for
Azure Active Directory IdentityModel Extensions for

Azure Functions OpenWeather Sample

:loudspeaker: Notice: Samples have been updated to reflect that they work on AVEVA Data Hub

Azure Functions OpenWeather Sample