Skip to content
This repository has been archived by the owner on Oct 11, 2019. It is now read-only.

#123 Support module nested references loading in Platform modular system. #124

Merged
merged 6 commits into from
Feb 6, 2019

Conversation

yecli
Copy link
Contributor

@yecli yecli commented Jan 11, 2019

  • Added test for nested module dependencies loading;
  • Reworked LoadContextAssemblyResolver assembly loading: every module assembly with all level dependencies is loaded into AssemblyLoadContext.Default to allow shared types to be used;
  • Set all module Web projects option to true to have the possibility to copy all dependencies from its bin folder (using this approach it should work on prod env where we cannot just restore packages). Futher investigation and revision could be done based on:
    dotnet/cli#10502;
  • Set win10-x64 option to VirtoCommerce.ImageToolsModule.Web project to address problem with NuGet package Microsoft.Windows.Compatibility, which do not copy its dependency "System.Private.ServiceModel.dll" on publishing. Explicit reference to System.ServiceModel.Http 4.5.3 package did not help, setting RID helped, though it makes module platform dependent. Info:
    .NET Core 2.0 self-contained app: Could not load file or assembly 'System.Private.ServiceModel, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' dotnet/wcf#2349, https://stackoverflow.com/questions/50746592/system-private-servicemodel-missing-in-azure-app-service-at-runtime;
  • Removed redundant LoadContextAssemblyResolver singleton registration;
  • Changed Windows specific "" path separator to platform independent Path.DirectorySeparatorChar in LocalStorageModuleCatalog probing folder;
  • Excluded copying assembly related dlls that are in TPA;
  • Splitted assembly file extensions (dll, exe) and assembly service file extensions (pdb, xml, .deps.json) for using in filtering;
  • Fixed couple of Sonar warnings;

// Explicit loading of "ICSharpCode.SharpZipLib.dll" (dependency of referenced assembly Module1.Data) to default ALC would make test passing even without proper dependency loading:
// System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(Environment.ExpandEnvironmentVariables(@"%userprofile%\.nuget\packages\sharpziplib\1.1.0\lib\netstandard2.0\ICSharpCode.SharpZipLib.dll"));
var controllerType = webRuntimeAssembly.GetType("Module1.Web.Controllers.ThirdPartyController");
Assert.NotNull(controllerType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public class AssemblyLoadTest
{
[Fact]
public void TestAssemblyLoadThroughReflection()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


<ItemGroup>
<Reference Include="Microsoft.Extensions.Logging.Abstractions">
<HintPath>..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.extensions.logging.abstractions\2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In implented dependency load system with copying all dependencies to output it is required. Will try to avoid dependency assembly copying by including NuGet and .NET caches to additional probing paths.
Explanation:
Set win10-x64 option to VirtoCommerce.ImageToolsModule.Web project to address problem with NuGet package Microsoft.Windows.Compatibility, which do not copy its dependency "System.Private.ServiceModel.dll" on publishing. Explicit reference to System.ServiceModel.Http 4.5.3 package did not help, setting RID helped, though it makes module platform dependent. Info:
dotnet/wcf#2349, https://stackoverflow.com/questions/50746592/system-private-servicemodel-missing-in-azure-app-service-at-runtime;

var bytes = Encoding.UTF8.GetBytes(message);
using (var inStream = new MemoryStream(bytes))
{
var outStream = CreateToMemoryStream(inStream, "TestZipEntry");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using or close

namespace VirtoCommerce.Platform.Modules.AssemblyLoading
{
// Based on https://github.com/natemcmaster/DotNetCorePlugins/blob/ce1b5cc94e8e9ce018f9304368cc69897cc70cca/src/Plugins/Loader/DependencyContextExtensions.cs
public static class DependencyReader
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not extension. bad idea use static, better to use with DI, read https://stackoverflow.com/questions/241339/when-to-use-static-classes-in-c-sharp

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is ok, just need to change methods signatures to be more like to extensions class semantic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read the SO post and think that it is still a suitable choice as it is true utility class with strictly defined function.
Could change it back to an extension.

// Fill _basePath and _additionalProbingPaths based on assemblyPath
// In fact we could add some _additionalProbingPaths to support assembly storages like NuGet package cache (), if we would need
var assemblyDirectory = Path.GetDirectoryName(assemblyUri.LocalPath);
_basePath = assemblyDirectory;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not really good to change object field inside read only functions, could we avoid this?

private bool SearchForLibrary(ManagedLibrary library, out string path)
{
// 1. Check for in _basePath + app local path
var localFile = Path.Combine(_basePath, library.AppLocalPath);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code cognitive complexity can be simplified by generation array of search paths and do single linq call FirstOrDefault

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Candidates pregeneration will add significant overhead. The common scenario would be just finding dependency inside _basePath, which generally speaking means one string creation, comparison and return. Sometimes dependency could be found inside _additionalProbingPaths, it could add couple of string creations. If we will pregenerate all the strings, method would become about 5 times slower.
And i think algorithm is readable and self-described, probably algorithm readability would not change much if we switch to pregeneration array.
Is generalization here costs 5-times performance loss?

private static IEnumerable<ManagedLibrary> ResolveRuntimeAssemblies(DependencyContext depContext, RuntimeFallbacks runtimeGraph)
{
var rids = GetRids(runtimeGraph);
return from library in depContext.RuntimeLibraries
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you replace this linq expression to extensions method syntax?

{
bool assemblyPredicate(RuntimeLibrary runtime)
var loadedAssembly = LoadAssemblyCached(dependency) ?? throw GenerateAssemblyLoadException(dependency.Name.Name, assemblyPath);
if (mainAssemblyName.Equals(loadedAssembly.GetName().Name, StringComparison.OrdinalIgnoreCase))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be replaced with .EqualsInvariant

- Used PluginLoader for this;
…tem:

- Reworked LoadContextAssemblyResolver assembly loading: every module assembly with all level dependencies is loaded into AssemblyLoadContext.Default to allow shared types to be used;
- Set all module Web projects <CopyLocalLockFileAssemblies> option to true to have the possibility to copy all dependencies from its bin folder (using this approach it should work on prod env where we cannot just restore packages). Futher investigation and revision could be done based on:
https://github.com/dotnet/cli/issues/10502;
- Set <RuntimeIdentifier>win10-x64</RuntimeIdentifier> option to VirtoCommerce.ImageToolsModule.Web project to address problem with NuGet package Microsoft.Windows.Compatibility, which do not copy  its dependency "System.Private.ServiceModel.dll" on publishing. Explicit reference to System.ServiceModel.Http 4.5.3 package did not help, setting RID helped, though it makes module platform dependent. Info:
dotnet/wcf#2349, https://stackoverflow.com/questions/50746592/system-private-servicemodel-missing-in-azure-app-service-at-runtime;
- Removed redundant LoadContextAssemblyResolver singleton registration;
- Changed Windows specific "\" path separator to platform independent Path.DirectorySeparatorChar in LocalStorageModuleCatalog probing folder;
- Fixed Sonar warning in ModuleInitializer.
…tem:

- Excluded copying assembly related dlls that are in TPA;
- Splitted assembly file extensions (dll, exe) and assembly service file extensions (pdb, xml, .deps.json) for using in filtering;
- Removed unused McMaster.NETCore.Plugins package reference from VirtoCommerce.Platform.Modules.csproj;
- Fixed couple of Sonar warnings;
…tem:

- Implemented "api/platform/modules/restart" action using IApplicationLifetime.StopApplication();
…tem:

- Added AdditionalProbingPath filling based on runtimeconfig.json and runtimeconfig.dev.json for Development env + NuGet and DotNet package caches for Production env;
- Removed all NuGet assembly copying to Modules probing folder. So it works on DEV env, for PROD we will need to prepare packages with required dependencies;
- Rebased on the latest master;
Review:
- Reworked test according to https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices;
- Arranged DependencyReader as an extension;
- Other fixes;
@yecli yecli force-pushed the feature/assembly-references-deep-loading branch from 44a179c to 3007f80 Compare January 25, 2019 21:16
…tem:

- Removed copying NuGet assemblies to output in Module1;
@tatarincev tatarincev merged commit dc56b97 into master Feb 6, 2019
@tatarincev tatarincev deleted the feature/assembly-references-deep-loading branch February 6, 2019 13:22
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants