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

Commit

Permalink
#123 Support module nested references loading in Platform modular sys…
Browse files Browse the repository at this point in the history
…tem. (#124)

* - Added tests for module assembly loading;
- Used PluginLoader for this;

* #123 Support module nested references loading in Platform modular system:
- 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.

* #123 Support module nested references loading in Platform modular system:
- 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;

* #123 Support module nested references loading in Platform modular system:
- Implemented "api/platform/modules/restart" action using IApplicationLifetime.StopApplication();

* #123 Support module nested references loading in Platform modular system:
- 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;

* #123 Support module nested references loading in Platform modular system:
- Removed copying NuGet assemblies to output in Module1;
  • Loading branch information
yecli authored and tatarincev committed Feb 6, 2019
1 parent 8b1138f commit dc56b97
Show file tree
Hide file tree
Showing 39 changed files with 828 additions and 129 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,4 @@ __pycache__/
/VirtoCommerce.Platform.Web/wwwroot/App_Data
/VirtoCommerce.Platform.Web/wwwroot/maps
/VirtoCommerce.Platform.Web/wwwroot/assets
/VirtoCommerce.Platform.Web/wwwroot/dist
4 changes: 0 additions & 4 deletions Modules/Module1/Module1.Abstractions/IMyService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Module1.Abstractions
{
public interface IMyService
Expand Down
7 changes: 7 additions & 0 deletions Modules/Module1/Module1.Abstractions/IThirdPartyService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Module1.Abstractions
{
public interface IThirdPartyService
{
string CallThirdPartyMethodFromAnotherProject(string message);
}
}
8 changes: 7 additions & 1 deletion Modules/Module1/Module1.Data/Module1.Data.csproj
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath></OutputPath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.4" />
<PackageReference Include="SharpZipLib" Version="1.1.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\VirtoCommerce.Platform.Data\VirtoCommerce.Platform.Data.csproj" />
<ProjectReference Include="..\Module1.Abstractions\Module1.Abstractions.csproj" />
</ItemGroup>

</Project>
45 changes: 45 additions & 0 deletions Modules/Module1/Module1.Data/Services/MyThirdPartyService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.IO;
using System.Text;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Module1.Abstractions;

namespace Module1.Data.Services
{
public class ThirdPartyServiceImpl : IThirdPartyService
{
public string CallThirdPartyMethodFromAnotherProject(string message)
{
var bytes = Encoding.UTF8.GetBytes(message);
using (var inStream = new MemoryStream(bytes))
using (var outStream = CreateToMemoryStream(inStream, "TestZipEntry"))
{
return $"Zipped message UTF-8 decoded string is:{Encoding.UTF8.GetString(outStream.ToArray())}";
}
}

// From https://github.com/icsharpcode/SharpZipLib/wiki/Zip-Samples#create-a-zip-fromto-a-memory-stream-or-byte-array
private MemoryStream CreateToMemoryStream(MemoryStream memStreamIn, string zipEntryName)
{
var outputMemStream = new MemoryStream();
var zipStream = new ZipOutputStream(outputMemStream);

zipStream.SetLevel(3); //0-9, 9 being the highest level of compression

var newEntry = new ZipEntry(zipEntryName);
newEntry.DateTime = DateTime.Now;

zipStream.PutNextEntry(newEntry);

StreamUtils.Copy(memStreamIn, zipStream, new byte[4096]);
zipStream.CloseEntry();

zipStream.IsStreamOwner = false; // False stops the Close also Closing the underlying stream.
zipStream.Close(); // Must finish the ZipOutputStream before using outputMemStream.

outputMemStream.Position = 0;
return outputMemStream;
}
}
}
52 changes: 52 additions & 0 deletions Modules/Module1/Module1.Test/AssemblyLoadTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Reflection;
using Microsoft.Extensions.Logging;
using Moq;
using VirtoCommerce.Platform.Modules;
using Xunit;

namespace Module1.Test
{
[Trait("Category", "CI")]
public class AssemblyLoadTest
{
[Theory]
[InlineData(true)]
[InlineData(false)]
public void LoadAssemblyFrom_ThirdPartyAssemblyInDependentProject_IsLoadedCorrectly(bool isDevelopmentEnvironment)
{
// Arrange
var loggerStub = Mock.Of<ILogger<LoadContextAssemblyResolver>>();
var resolver = new LoadContextAssemblyResolver(loggerStub, isDevelopmentEnvironment);

// Act
var webRuntimeAssembly = LoadAssemblyInRuntime(resolver);
// To ensure assembly is loaded correctly with all recursive dependencies, need to instantiate controller and call the method that call third party dll functions
var stringResult = CallControllerMethod(webRuntimeAssembly);

// Assert
Assert.NotNull(stringResult);
}

private static Assembly LoadAssemblyInRuntime(LoadContextAssemblyResolver resolver)
{
// We need to load assembly that is not referenced by our project, thats why physical path is hardcoded
var Module1WebModulePath = $"{AppContext.BaseDirectory}..\\..\\..\\Module1.Web\\bin\\netcoreapp2.1\\Module1.Web.dll";
var webRuntimeAssembly = resolver.LoadAssemblyFrom(Module1WebModulePath);
return webRuntimeAssembly;
}

private static string CallControllerMethod(Assembly webRuntimeAssembly)
{
// We need to use type and method string names instead of nameof() here as we should not have the reference to these assemblies for proper load testing
const string ControllerTypeFullName = "Module1.Web.Controllers.ThirdPartyController";
const string ControllerMethodName = "Execute3rdPartyCodeFromDifferentProject";
const string ControllerMethodSampleArgument = "Sample message.";

var controllerType = webRuntimeAssembly.GetType(ControllerTypeFullName);
var controllerInstance = Activator.CreateInstance(controllerType);
var method = controllerType.GetMethod(ControllerMethodName);
return (string)method.Invoke(controllerInstance, new object[] { ControllerMethodSampleArgument });
}
}
}
28 changes: 28 additions & 0 deletions Modules/Module1/Module1.Test/Module1.Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>bin\</OutputPath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="Moq" Version="4.10.1" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\VirtoCommerce.Platform.Modules\VirtoCommerce.Platform.Modules.csproj" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.Extensions.Logging.Abstractions"/>
</ItemGroup>

</Project>
20 changes: 20 additions & 0 deletions Modules/Module1/Module1.Web/Controllers/ThirdPartyController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc;
using Module1.Data.Services;

namespace Module1.Web.Controllers
{
[Route("api/[controller]")]
public class ThirdPartyController
{
public ThirdPartyController()
{
}

// GET api/thirdparty
[HttpGet]
public string Execute3rdPartyCodeFromDifferentProject(string message)
{
return new ThirdPartyServiceImpl().CallThirdPartyMethodFromAnotherProject(message);
}
}
}
6 changes: 2 additions & 4 deletions Modules/Module1/Module1.Web/Module.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Module1.Abstractions;
using Module1.Data;
using Module1.Data.Services;
using Module1.Services;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Platform.Core.DynamicProperties;
using VirtoCommerce.Platform.Core.Modularity;
using VirtoCommerce.Platform.Core.Settings;
using VirtoCommerce.Platform.Data.Model;
using VirtoCommerce.Platform.Data.Repositories;

namespace Module1.Web
Expand All @@ -24,6 +21,7 @@ public void Initialize(IServiceCollection serviceCollection)
var configuration = serviceCollection.BuildServiceProvider().GetRequiredService<IConfiguration>();
//var mode = FluentValidation.CascadeMode.Continue;
serviceCollection.AddSingleton<IMyService, MyServiceImpl>();
serviceCollection.AddSingleton<IThirdPartyService, ThirdPartyServiceImpl>();
serviceCollection.AddDbContext<PlatformDbContext2>(builder =>
{
builder.UseSqlServer(configuration.GetConnectionString("VirtoCommerce"));
Expand Down
5 changes: 5 additions & 0 deletions Modules/Module1/Module1.Web/Module1.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>bin\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="8.0.101" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--<RuntimeIdentifier>win10-x64</RuntimeIdentifier>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<!--This line is necessary to copy all dependencies in the bin folder-->
<!--<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>-->
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public class LocalStorageModuleCatalogOptions
{
public string DiscoveryPath { get; set; }
public string ProbingPath { get; set; }
public string[] AssemblyFileExtensions { get; set; } = new[] { ".dll", ".pdb", ".exe", ".xml", ".deps.json" };
public string[] AssemblyFileExtensions { get; set; } = new[] { ".dll", ".exe", };
public string[] AssemblyServiceFileExtensions { get; set; } = new[] { ".pdb", ".xml", ".deps.json", ".runtimeconfig.json", ".runtimeconfig.dev.json", };
}
}
Loading

0 comments on commit dc56b97

Please sign in to comment.