SteveDunn / Vogen

A semi-opinionated library which is a source generator and a code analyser. It Source generates Value Objects
Apache License 2.0
782 stars 46 forks source link

Exception thrown when comparing two values. #642

Closed Romanx closed 2 months ago

Romanx commented 2 months ago

Describe the bug

An exception is thrown when performing a comparision between two well constructed objects. This only seems to happen if you use ExcludeAssets="runtime" in your PackageReference.

System.IO.FileNotFoundException: Could not load file or assembly 'Vogen.SharedTypes, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
  Stack Trace:
      at System.ModuleHandle.ResolveType(QCallModule module, Int32 typeToken, IntPtr* typeInstArgs, Int32 typeInstCount, IntPtr* methodInstArgs, Int32 methodInstCount, ObjectHandleOnStack type)
   at System.ModuleHandle.ResolveTypeHandle(Int32 typeToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext)
   at System.Reflection.RuntimeModule.ResolveType(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
   at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(MetadataToken caCtorToken, MetadataImport& scope, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1& derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctorWithParameters, Boolean& isVarArg)
   at System.Reflection.CustomAttribute.IsCustomAttributeDefined(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Int32 attributeCtorToken, Boolean mustBeInheritable)
   at System.Reflection.CustomAttribute.IsDefined(RuntimeType type, RuntimeType caType, Boolean inherit)
   at System.Diagnostics.StackTrace.ToString(TraceFormat traceFormat, StringBuilder sb)
   at System.Diagnostics.StackTrace.ToString(TraceFormat traceFormat)
   at System.Exception.get_StackTrace()

Steps to reproduce

Created a slimmed down repo project here: https://github.com/Romanx/VogenIssue

Expected behaviour

The runtime asset can be excluded and no exception is thrown when comparing the two objects.

SteveDunn commented 2 months ago

Hi - thanks for the bug report. By specifying PrivateAssets="all" ExcludeAssets="runtime", you're saying that Vogen is a development-only dependency, which isn't strictly true. Vogen.dll is, but Vogen.SharedTypes.dll isn't.

The types specified in VogenSharedTypes need to be there at runtime because the generated code references them. For your scenario, the path through the generated code hits EnsureInitialized, which expects ValueObjectValidationException to exist:

        private readonly void EnsureInitialized()
        {
            if (!IsInitialized())
            {
#if DEBUG
                global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? "";
#else
                global::System.String message = "Use of uninitialized Value Object.";
#endif

                throw new global::Vogen.ValueObjectValidationException(message);  // <=== THROWN HERE
            }
        }

... but that exception isn't present at runtime.

You could stop this from happening by specifying your own exception, e.g. in global config:

[assembly: VogenDefaults(throws: typeof(MyException))]

... but it is not recommended: doing so fixes your particular problem, but there are other shared types in Vogen.SharedTypes, such as ValueObjectOrError which is used if TryFrom is generated.

Including it adds just 18KB and one DLL to the published app:

image

Looking at it in something like dotPeek, you can see the types that it exports:

image

I've updated the wiki with this information.

Thanks again for your feedback.

Romanx commented 1 month ago

@SteveDunn thanks very much, we happily took the runtime dependency (which as you said is very small) when we found the error. Just got confused by the situation when it occurred. Thanks for updating the docs!

SteveDunn commented 1 month ago

Very welcome!