├── .gitignore
├── README.md
└── src
├── addin-loadassemblybypath
├── app
│ ├── ComponentHost.cs
│ ├── Program.cs
│ └── app.csproj
├── lib
│ ├── Class1.cs
│ └── lib.csproj
└── lib2
│ ├── Class2.cs
│ └── lib2.csproj
├── build.ps1
├── customloadcontext
├── ComponentHost.cs
├── Program.cs
└── customloadcontext.csproj
├── gutenapp
├── dialogue
│ ├── Class1.cs
│ └── dialogue.csproj
├── gutenapp
│ ├── ComponentContext.cs
│ ├── ComponentResolution.cs
│ ├── ComponentResolver.cs
│ ├── ComponentResolverBinDirectoryStrategy.cs
│ ├── ComponentResolverProductionStrategy.cs
│ ├── DirectoryInfoExtensions.cs
│ ├── IComponentResolver.cs
│ ├── Program.cs
│ └── gutenapp.csproj
├── interfaces
│ ├── IProvider.cs
│ └── interfaces.csproj
├── mostcommonwords
│ ├── MostCommonWords.cs
│ └── mostcommonwords.csproj
└── wordcount
│ ├── WordCount.cs
│ └── wordcount.csproj
├── lib
├── Class1.cs
├── lib.csproj
└── out
│ ├── lib.deps.json
│ └── lib.dll
├── loadassemblybypath
├── Program.cs
└── loadassemblybypath.csproj
├── test-prod.ps1
└── test.ps1
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 | .vscode/
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015/2017 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # Ignore the published app from our test scritps
34 | out/
35 | testdir/
36 |
37 | # Visual Studio 2017 auto generated files
38 | Generated\ Files/
39 |
40 | # MSTest test Results
41 | [Tt]est[Rr]esult*/
42 | [Bb]uild[Ll]og.*
43 |
44 | # NUNIT
45 | *.VisualState.xml
46 | TestResult.xml
47 |
48 | # Build Results of an ATL Project
49 | [Dd]ebugPS/
50 | [Rr]eleasePS/
51 | dlldata.c
52 |
53 | # Benchmark Results
54 | BenchmarkDotNet.Artifacts/
55 |
56 | # .NET Core
57 | project.lock.json
58 | project.fragment.lock.json
59 | artifacts/
60 |
61 | # StyleCop
62 | StyleCopReport.xml
63 |
64 | # Files built by Visual Studio
65 | *_i.c
66 | *_p.c
67 | *_i.h
68 | *.ilk
69 | *.meta
70 | *.obj
71 | *.iobj
72 | *.pch
73 | *.pdb
74 | *.ipdb
75 | *.pgc
76 | *.pgd
77 | *.rsp
78 | *.sbr
79 | *.tlb
80 | *.tli
81 | *.tlh
82 | *.tmp
83 | *.tmp_proj
84 | *.log
85 | *.vspscc
86 | *.vssscc
87 | .builds
88 | *.pidb
89 | *.svclog
90 | *.scc
91 |
92 | # Chutzpah Test files
93 | _Chutzpah*
94 |
95 | # Visual C++ cache files
96 | ipch/
97 | *.aps
98 | *.ncb
99 | *.opendb
100 | *.opensdf
101 | *.sdf
102 | *.cachefile
103 | *.VC.db
104 | *.VC.VC.opendb
105 |
106 | # Visual Studio profiler
107 | *.psess
108 | *.vsp
109 | *.vspx
110 | *.sap
111 |
112 | # Visual Studio Trace Files
113 | *.e2e
114 |
115 | # TFS 2012 Local Workspace
116 | $tf/
117 |
118 | # Guidance Automation Toolkit
119 | *.gpState
120 |
121 | # ReSharper is a .NET coding add-in
122 | _ReSharper*/
123 | *.[Rr]e[Ss]harper
124 | *.DotSettings.user
125 |
126 | # JustCode is a .NET coding add-in
127 | .JustCode
128 |
129 | # TeamCity is a build add-in
130 | _TeamCity*
131 |
132 | # DotCover is a Code Coverage Tool
133 | *.dotCover
134 |
135 | # AxoCover is a Code Coverage Tool
136 | .axoCover/*
137 | !.axoCover/settings.json
138 |
139 | # Visual Studio code coverage results
140 | *.coverage
141 | *.coveragexml
142 |
143 | # NCrunch
144 | _NCrunch_*
145 | .*crunch*.local.xml
146 | nCrunchTemp_*
147 |
148 | # MightyMoose
149 | *.mm.*
150 | AutoTest.Net/
151 |
152 | # Web workbench (sass)
153 | .sass-cache/
154 |
155 | # Installshield output folder
156 | [Ee]xpress/
157 |
158 | # DocProject is a documentation generator add-in
159 | DocProject/buildhelp/
160 | DocProject/Help/*.HxT
161 | DocProject/Help/*.HxC
162 | DocProject/Help/*.hhc
163 | DocProject/Help/*.hhk
164 | DocProject/Help/*.hhp
165 | DocProject/Help/Html2
166 | DocProject/Help/html
167 |
168 | # Click-Once directory
169 | publish/
170 |
171 | # Publish Web Output
172 | *.[Pp]ublish.xml
173 | *.azurePubxml
174 | # Note: Comment the next line if you want to checkin your web deploy settings,
175 | # but database connection strings (with potential passwords) will be unencrypted
176 | *.pubxml
177 | *.publishproj
178 |
179 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
180 | # checkin your Azure Web App publish settings, but sensitive information contained
181 | # in these scripts will be unencrypted
182 | PublishScripts/
183 |
184 | # NuGet Packages
185 | *.nupkg
186 | # The packages folder can be ignored because of Package Restore
187 | **/[Pp]ackages/*
188 | # except build/, which is used as an MSBuild target.
189 | !**/[Pp]ackages/build/
190 | # Uncomment if necessary however generally it will be regenerated when needed
191 | #!**/[Pp]ackages/repositories.config
192 | # NuGet v3's project.json files produces more ignorable files
193 | *.nuget.props
194 | *.nuget.targets
195 |
196 | # Microsoft Azure Build Output
197 | csx/
198 | *.build.csdef
199 |
200 | # Microsoft Azure Emulator
201 | ecf/
202 | rcf/
203 |
204 | # Windows Store app package directories and files
205 | AppPackages/
206 | BundleArtifacts/
207 | Package.StoreAssociation.xml
208 | _pkginfo.txt
209 | *.appx
210 |
211 | # Visual Studio cache files
212 | # files ending in .cache can be ignored
213 | *.[Cc]ache
214 | # but keep track of directories ending in .cache
215 | !*.[Cc]ache/
216 |
217 | # Others
218 | ClientBin/
219 | ~$*
220 | *~
221 | *.dbmdl
222 | *.dbproj.schemaview
223 | *.jfm
224 | *.pfx
225 | *.publishsettings
226 | orleans.codegen.cs
227 |
228 | # Including strong name files can present a security risk
229 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
230 | #*.snk
231 |
232 | # Since there are multiple workflows, uncomment next line to ignore bower_components
233 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
234 | #bower_components/
235 |
236 | # RIA/Silverlight projects
237 | Generated_Code/
238 |
239 | # Backup & report files from converting an old project file
240 | # to a newer Visual Studio version. Backup files are not needed,
241 | # because we have git ;-)
242 | _UpgradeReport_Files/
243 | Backup*/
244 | UpgradeLog*.XML
245 | UpgradeLog*.htm
246 | ServiceFabricBackup/
247 | *.rptproj.bak
248 |
249 | # SQL Server files
250 | *.mdf
251 | *.ldf
252 | *.ndf
253 |
254 | # Business Intelligence projects
255 | *.rdl.data
256 | *.bim.layout
257 | *.bim_*.settings
258 | *.rptproj.rsuser
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush
299 | .cr/
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Assembly Loading with .NET Core
2 |
3 | Loading assemblies is an important part of many programs. In most cases, your static dependencies will be loaded automatically, but dynamic dependencies require active use of various assembly loader APIs. For any program that exposes an add-in model, assembly loading is a critical part of the application architecture.
4 |
5 | Many .NET developers are familar with the .NET Framework assembly loading model, using [Assembly](https://docs.microsoft.com/dotnet/api/system.reflection.assembly?view=netframework-4.7.2) and [AppDomain](https://docs.microsoft.com/dotnet/api/system.appdomain?view=netframework-4.7.2) class APIs. .NET Core exposes a similar model, with differences. The biggest difference is that .NET Core exposes [AssemblyLoadContext](https://docs.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext), which has some overlap with AppDomain, but is a much lighter-weight subsystem.
6 |
7 | Resources:
8 |
9 | * [AssemblyLoadContext design document](https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/assemblyloadcontext.md)
10 | * [AssemblyLoadContext source](https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs)
11 |
12 | This document describes a set of scenarios with guidance and samples for implementing those scenarios.
13 |
14 | ## AssemblyLoaderContext assembly loading model
15 |
16 | You need to understand the basics of the new [AssemblyLoadContext](https://docs.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext) (ALC) type to correctly control assembly loading.
17 |
18 | The basic function and value proposition of ALC is enabling assembly loading isolation within a process. This is similar to what the AppDomain type provides with .NET Framework. Within a given ALC, you can load an assembly with a given simple name, like foo.dll, just once. You can load assemblies with that same name multiple times across multiple ALCs. The version and publisher of the assembly does not need to match across ALCs. That statics for an assembly are unique (and don't leak) across ALCs.
19 |
20 | Every assembly is loaded into an ALC. By default, assemblies are loaded into the default ALC. Assemblies that are loaded into the default ALC are visible to all other ALCs, including the values of static fields.
21 |
22 | The [AssemblyLoadContext](https://docs.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext) class exposes a set of assembly loading APIs. These APIs, by virtue of being instance APIs, load assemblies into the current ALC. They can load by name, file, or stream.
23 |
24 | The default ALC is available via the static `AssemblyLoadContext.Default` property. Non-default ALCs are available by using APIs that expose ALCs, which are discussed later. ALCs do not currently have names. You can only differentiate ALCs via equality comparisons, like comparing the default ALC to another ALC reference.
25 |
26 | The [Assembly](https://docs.microsoft.com/dotnet/api/system.reflection.assembly?view=netcore-2.1) class exposes a set of assembly loading APIs, the same ones that are available in the .NET Framework. These APIs have the following behavior:
27 |
28 | * Assembly.Load - loads assembly and its dependencies into the current ALC. One can consider this API neutral in nature, leaving all loading policy up to the host application.
29 | * Assembly.LoadFrom - loads assembly and its dependencies into the default ALC. Only a application or host should use this API.
30 | * Assembly.LoadFile and Assembly.Load(Byte[]) - loads assembly and its depenencies into a new ALC.
31 |
32 | ## Loading an assembly by path
33 |
34 | The simplest scenario is loading an assembly by path. The following code loads an assembly by path into the default ALC.
35 |
36 | ```csharp
37 | var assemblyLocation = "/fully/qualified-path/to.dll"
38 | var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyLocation);
39 | ```
40 |
41 | Alternatively, you can `Assembly` class APIs. `Assembly.LoadFrom` will load assemblies into the default ALC. `Assembly.LoadFile` will load assemblies into a new ALC with each invocation.
42 |
43 | ## Loading an assembly by path as an add-in
44 |
45 | There is a simple pattern that can used to acquire a reference to the current context, using the `AssemblyLoadContext.GetLoadContext` method, as follows (assuming the calling type is `Class1`).
46 |
47 | ```csharp
48 | var context = AssemblyLoadContext.GetLoadContext(typeof(Class1).Assembly);
49 | ```
50 |
51 | Once this reference is acquired, you can call `AssemblyLoadContext` instance methods to load assemblies from an addin.
52 |
53 | Some developers have asked for an [AssemblyLoadContext.Current](https://github.com/dotnet/coreclr/issues/10233) API to load dependencies. This API doesn't seem necessary.
54 |
--------------------------------------------------------------------------------
/src/addin-loadassemblybypath/app/ComponentHost.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.Loader;
4 |
5 | public class ComponentHost : AssemblyLoadContext
6 | {
7 | protected override Assembly Load(AssemblyName assemblyName)
8 | {
9 | return Assembly.Load(assemblyName);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/addin-loadassemblybypath/app/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.Loader;
4 | using static System.Console;
5 |
6 | namespace loaderexample
7 | {
8 | class Program
9 | {
10 | static void Main(string[] args)
11 | {
12 |
13 | var assemblyLocation = "/Users/rlander/git/dotnet-core-assembly-loading/src/addin-loadassemblybypath/lib/bin/Debug/netcoreapp2.1/lib.dll";
14 | WriteLine("Load lib.dll");
15 | var host = new ComponentHost();
16 | var assembly = host.LoadFromAssemblyPath(assemblyLocation);
17 | var type = assembly.GetType("loaderexample.Class1");
18 | WriteLine("Call GetString method:");
19 | var method = type.GetMethod("GetString");
20 | var returnValue = method.Invoke(null, null);
21 | WriteLine(returnValue);
22 | WriteLine("Print loaded assemblies in 'host' AssemblyLoadContext:");
23 | WriteLine("Call GetOtherString method:");
24 | var method2 = type.GetMethod("GetOtherString");
25 | var returnValue2 = method2.Invoke(null, null);
26 | WriteLine(returnValue2);
27 | WriteLine("Print loaded assemblies in 'host' AssemblyLoadContext:");
28 |
29 | foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
30 | {
31 | // only display assemblies not loaded in the Default AssemblyLoadContext
32 | if (AssemblyLoadContext.GetLoadContext(asm) == host)
33 | {
34 | WriteLine(asm.FullName);
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/addin-loadassemblybypath/app/app.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/addin-loadassemblybypath/lib/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.Loader;
4 |
5 | namespace loaderexample
6 | {
7 | public class Class1
8 | {
9 | public static string GetString()
10 | {
11 | return "Bow ties are cool.";
12 | }
13 |
14 | public static string GetOtherString()
15 | {
16 | var assemblyLocation = "/Users/rlander/git/dotnet-core-assembly-loading/src/addin-loadassemblybypath/lib2/bin/Debug/netstandard2.0/lib2.dll";
17 | var context = AssemblyLoadContext.GetLoadContext(typeof(Class1).Assembly);
18 | var assembly = context.LoadFromAssemblyPath(assemblyLocation);
19 | var type = assembly.GetType("Class2");
20 | var method = type.GetMethod("GetOtherString");
21 | var returnValue = (string)method.Invoke(null, null);
22 | return returnValue;
23 | }
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/addin-loadassemblybypath/lib/lib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/addin-loadassemblybypath/lib2/Class2.cs:
--------------------------------------------------------------------------------
1 | public class Class2
2 | {
3 | public static string GetOtherString()
4 | {
5 | return "Before I go, I just want to tell you: you were fantastic. Absolutely fantastic. And you know what? So was I.";
6 | }
7 | }
--------------------------------------------------------------------------------
/src/addin-loadassemblybypath/lib2/lib2.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/build.ps1:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env pwsh
2 |
3 | $BasePath = (Get-Item -Path "./").FullName
4 | $BaseGutenapp = Join-Path $BasePath "gutenapp"
5 | $GutenappDir = Join-Path $BaseGutenapp "gutenapp"
6 | $Gutenapp = Join-Path $GutenappDir "gutenapp.csproj"
7 | $WordcountDir = Join-Path $BaseGutenapp "wordcount"
8 | $Wordcount = Join-Path $WordcountDir "wordcount.csproj"
9 | $MostcommonwordsDir = Join-Path $BaseGutenapp "mostcommonwords"
10 | $Mostcommonwords = Join-Path $MostcommonwordsDir "mostcommonwords.csproj"
11 |
12 | dotnet build $Gutenapp
13 | dotnet build $Wordcount
14 | dotnet build $Mostcommonwords
15 |
--------------------------------------------------------------------------------
/src/customloadcontext/ComponentHost.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.Loader;
4 |
5 | public class ComponentHost : AssemblyLoadContext
6 | {
7 | protected override Assembly Load(AssemblyName assemblyName)
8 | {
9 | return Assembly.Load(assemblyName);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/customloadcontext/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.Loader;
4 | using static System.Console;
5 |
6 | namespace loaderexample
7 | {
8 | class Program
9 | {
10 | static void Main(string[] args)
11 | {
12 | var assemblyLocation = "/Users/rlander/git/dotnet-core-assembly-loading/src/lib/out/lib.dll";
13 | WriteLine("Load lib.dll");
14 | var host = new ComponentHost();
15 | var assembly = host.LoadFromAssemblyPath(assemblyLocation);
16 | var type = assembly.GetType("lib.Class1");
17 | WriteLine("Call GetString method:");
18 | var method = type.GetMethod("GetString");
19 | var returnValue = method.Invoke(null, null);
20 | WriteLine(returnValue);
21 |
22 | foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
23 | {
24 | // only display assemblies not loaded in the Default AssemblyLoadContext
25 | if (AssemblyLoadContext.GetLoadContext(asm) == AssemblyLoadContext.Default)
26 | {
27 | continue;
28 | }
29 |
30 | WriteLine(asm.FullName);
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/customloadcontext/customloadcontext.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/gutenapp/dialogue/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace dialogue
4 | {
5 | public class Class1
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/gutenapp/dialogue/dialogue.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/ComponentContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.Loader;
4 |
5 | namespace ComponentHost
6 | {
7 | public class ComponentContext : AssemblyLoadContext
8 | {
9 |
10 | private ComponentResolver[] _resolvers;
11 |
12 | public ComponentContext(string component, params ComponentResolver[] resolvers)
13 | {
14 | _resolvers = resolvers;
15 | Component = component;
16 | }
17 |
18 | private ComponentContext()
19 | {
20 | }
21 |
22 | public string Component {get; private set;}
23 |
24 | protected override Assembly Load(AssemblyName assemblyName)
25 | {
26 | return Assembly.Load(assemblyName);
27 | }
28 |
29 | public Assembly LoadAssemblyWithResolver(string assemblyFile)
30 | {
31 | if (_resolvers == null)
32 | {
33 | return null;
34 | }
35 |
36 | foreach (var resolver in _resolvers)
37 | {
38 | var componentResolution = resolver.SetComponent(Component);
39 | if (!componentResolution)
40 | {
41 | continue;
42 | }
43 | var libraryResolution = resolver.FindLibrary(assemblyFile);
44 | if (libraryResolution.ResolvedComponent)
45 | {
46 | return LoadFromAssemblyPath(libraryResolution.ResolvedPath);
47 | }
48 | }
49 |
50 | return null;
51 | }
52 |
53 | public static (ComponentContext, Assembly) CreateContext(string assemblyPath)
54 | {
55 | var host = new ComponentContext();
56 | var assembly = host.LoadFromAssemblyPath(assemblyPath);
57 | return (host, assembly);
58 | }
59 |
60 | public static (ComponentContext, Assembly, T) CreateContext(string assemblyPath, string typeName)
61 | {
62 | var (ComponentContext, assembly) = CreateContext(assemblyPath);
63 | T obj = (T)assembly.CreateInstance(typeName);
64 | return (ComponentContext, assembly, obj);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/ComponentResolution.cs:
--------------------------------------------------------------------------------
1 | namespace ComponentHost
2 | {
3 | public struct ComponentResolution
4 | {
5 | public string RequestedComponent;
6 | public bool ResolvedComponent;
7 | public string ResolvedPath;
8 | public string[] Candidates;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/ComponentResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.IO;
4 | using System.Collections.Generic;
5 |
6 | namespace ComponentHost
7 | {
8 | public class ComponentResolver : IComponentResolver
9 | {
10 | public ComponentResolver()
11 | {
12 | BaseDirectory = new DirectoryInfo(AppContext.BaseDirectory);
13 | }
14 |
15 | public DirectoryInfo BaseDirectory { get; set; }
16 |
17 | public string Component { get; set; }
18 |
19 | public virtual bool SetComponent(string component)
20 | {
21 | Component = component;
22 | return true;
23 | }
24 |
25 | public virtual ComponentResolution FindLibrary(string library)
26 | {
27 | if (string.IsNullOrWhiteSpace(library))
28 | {
29 | throw new ArgumentException("libraryName not set");
30 | }
31 |
32 | return ProbeDirectoryForLibrary(BaseDirectory,library);
33 | }
34 |
35 | protected ComponentResolution ProbeDirectoryForLibrary(DirectoryInfo probingDir, string library)
36 | {
37 | var fileList = probingDir.GetFiles(library);
38 | if (fileList.Length != 1)
39 | {
40 | return GetFalseResolution(library);
41 | }
42 | var resolution = new ComponentResolution();
43 | resolution.RequestedComponent = library;
44 | resolution.ResolvedComponent = true;
45 | resolution.ResolvedPath = fileList[0].FullName;
46 | return resolution;
47 | }
48 |
49 | protected ComponentResolution GetFalseResolution(string component)
50 | {
51 | var resolution = new ComponentResolution();
52 | resolution.RequestedComponent = component;
53 | resolution.ResolvedComponent = false;
54 | return resolution;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/ComponentResolverBinDirectoryStrategy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using ComponentHost;
5 |
6 | namespace ComponentResolverStrategies
7 | {
8 | public class ComponentResolverBinDirectoryStrategy : ComponentResolver
9 | {
10 |
11 | // The following directory structure demonstrates an expected
12 | // directory layout during development.
13 | // Looking up from app.exe:
14 | // app.exe (appbase is here)
15 | // netcoreapp2.1
16 | // debug | release
17 | // bin
18 | // app
19 | // Looking down from app dir:
20 | // app
21 | // component
22 | // bin
23 | // debug | release
24 | // netcoreapp2.1
25 | // component.dll (component that we want is here)
26 |
27 | // In the case that release or debug *app* directories are
28 | // discovered that build kind is preferred for probing
29 |
30 | private const string DEBUG = "debug";
31 | private const string RELEASE = "release";
32 | private const string BIN = "bin";
33 | private const string DLL = ".dll";
34 | private readonly string[] TARGETS = new string[] { "netcore", "netstandard" };
35 | private string _appTFM = string.Empty;
36 | private string _buildKind = string.Empty;
37 |
38 | public override ComponentResolution FindLibrary(string library)
39 | {
40 | if (string.IsNullOrWhiteSpace(library))
41 | {
42 | throw new ArgumentException("libraryName not set");
43 | }
44 | return FindLibraryInComponentBinDirectory(library);
45 | }
46 |
47 | public override bool SetComponent(string component)
48 | {
49 | var (componentDirectoryFound, componentDirectory) = TryFindComponentDirectory(component);
50 | if (!componentDirectoryFound)
51 | {
52 | return false;
53 | }
54 | Component = component;
55 | BaseDirectory = componentDirectory;
56 | return true;
57 | }
58 |
59 | private (bool componentDirectoryFound, DirectoryInfo componentDirectory) TryFindComponentDirectory(string component)
60 | {
61 | // Expected location, per probingDir: app/bin/[buildKind]/tfm
62 | var probingDir = BaseDirectory;
63 |
64 | var count = 0;
65 | var dirName = string.Empty;
66 | while (count++ < 4)
67 | {
68 | probingDir = probingDir?.Parent;
69 |
70 | if (probingDir == null)
71 | {
72 | return False();
73 | }
74 |
75 | dirName = probingDir.Name.ToLowerInvariant();
76 |
77 | if (dirName == DEBUG ||
78 | dirName == RELEASE)
79 | {
80 | _buildKind = dirName;
81 | }
82 | else if (dirName == BIN)
83 | {
84 | // Assumption: components are in peer directories
85 | // one level above bin
86 | probingDir = probingDir.Parent?.Parent;
87 | if (probingDir == null)
88 | {
89 | return False();
90 | }
91 | break;
92 | }
93 | else
94 | {
95 | break;
96 | }
97 | }
98 |
99 | // Expected location, per probingDir: app
100 | var (componentDirFound, componentDir) = probingDir.TryNavigateDirectoryDown(component);
101 |
102 | if (!componentDirFound)
103 | {
104 | return False();
105 | }
106 |
107 | return (true, componentDir);
108 |
109 | (bool, DirectoryInfo) False()
110 | {
111 | return (false, BaseDirectory);
112 | }
113 | }
114 |
115 | private ComponentResolution FindLibraryInComponentBinDirectory(string library)
116 | {
117 | var candidateLibraries = new List();
118 |
119 | // Expected location, per probingDir: app/component
120 | var probingDir = BaseDirectory;
121 |
122 | var (binFound, binDir) = probingDir.TryNavigateDirectoryDown(BIN);
123 |
124 | if (!binFound)
125 | {
126 | return False();
127 | }
128 |
129 | // Expected location, per probingDir: app/component/bin
130 | probingDir = binDir;
131 |
132 | DirectoryInfo releaseDir = null;
133 | DirectoryInfo debugDir = null;
134 |
135 | // these directories do not have uniform casing, hence foreach
136 | foreach (var dir in probingDir.GetDirectories())
137 | {
138 | var lowerName = dir.Name.ToLowerInvariant();
139 | if (lowerName == RELEASE)
140 | {
141 | releaseDir = dir;
142 | }
143 | else if (lowerName == DEBUG)
144 | {
145 | debugDir = dir;
146 | }
147 | }
148 |
149 | // Expected location, per probingDir: app/component/bin/[release | debug]
150 | if (releaseDir == null && debugDir == null)
151 | {
152 | return False();
153 | }
154 |
155 | // Probe release and debug directories
156 | // prefering release
157 | if (_buildKind == RELEASE && releaseDir != null)
158 | {
159 | probingDir = releaseDir;
160 | }
161 | else if (_buildKind == DEBUG && debugDir != null)
162 | {
163 | probingDir = debugDir;
164 | }
165 | else if (releaseDir != null)
166 | {
167 | probingDir = releaseDir;
168 | }
169 | else if (debugDir != null)
170 | {
171 | probingDir = debugDir;
172 | }
173 | else
174 | {
175 | return False();
176 | }
177 |
178 | return ProbeForLibrariesInTargetFrameworkDirectories(probingDir, library);
179 |
180 | ComponentResolution False()
181 | {
182 | var resolution = new ComponentResolution();
183 | resolution.ResolvedComponent = false;
184 | return resolution;
185 | }
186 | }
187 |
188 | private ComponentResolution ProbeForLibrariesInTargetFrameworkDirectories(DirectoryInfo probingDir, string library)
189 | {
190 | var resolution = new ComponentResolution();
191 | var candidateLibs = new List();
192 |
193 | foreach (var target in TARGETS)
194 | {
195 | var dirs = probingDir.GetDirectories($"{target}*");
196 | foreach (var dir in dirs)
197 | {
198 | var probeResult = ProbeDirectoryForLibrary(dir, library);
199 | if (probeResult.ResolvedComponent)
200 | {
201 | candidateLibs.Add(probeResult.ResolvedPath);
202 | }
203 | }
204 | }
205 |
206 | if (candidateLibs.Count == 0)
207 | {
208 | resolution.ResolvedComponent = false;
209 | }
210 | else
211 | {
212 | resolution.ResolvedComponent = true;
213 | resolution.Candidates = candidateLibs.ToArray();
214 | resolution.ResolvedPath = resolution.Candidates[0];
215 | }
216 |
217 | return resolution;
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/ComponentResolverProductionStrategy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using ComponentHost;
4 |
5 | // The following directory structure demonstrates an expected
6 | // directory layout for prod layouts.
7 | // Looking up from app.exe:
8 | // app.exe (appbase is here)
9 | // app
10 | // Looking down from app dir:
11 | // app
12 | // component
13 | // component.dll (component that we want is here)
14 |
15 | namespace ComponentResolverStrategies
16 | {
17 | public class ComponentResolverProductionStrategy : ComponentResolver
18 | {
19 |
20 | public override ComponentResolution FindLibrary(string library)
21 | {
22 | if (string.IsNullOrWhiteSpace(library))
23 | {
24 | throw new ArgumentException("libraryName not set");
25 | }
26 |
27 | var resolution = new ComponentResolution();
28 |
29 | var (componentDirFound, componentDir) = BaseDirectory.TryNavigateDirectoryDown(Component);
30 |
31 | if (!componentDirFound)
32 | {
33 | resolution.ResolvedComponent = false;
34 | return resolution;
35 | }
36 |
37 | return ProbeDirectoryForLibrary(componentDir, library);
38 | }
39 |
40 | public override bool SetComponent(string component)
41 | {
42 | var (componentDirectoryFound, componentDirectory) = BaseDirectory.TryNavigateDirectoryDown(component);
43 |
44 | if (!componentDirectoryFound)
45 | {
46 | return false;
47 | }
48 |
49 | Component = component;
50 | BaseDirectory = componentDirectory;
51 | return true;
52 |
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/DirectoryInfoExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | public static class DirectoryInfoExtensions
4 | {
5 | public static (bool, DirectoryInfo) TryNavigateDirectoryUp(this DirectoryInfo baseDir, string name)
6 | {
7 | if (baseDir.Parent?.Name == name)
8 | {
9 | return (true, baseDir.Parent);
10 | }
11 | return (false, baseDir);
12 | }
13 |
14 | public static (bool, DirectoryInfo) TryNavigateDirectoryDown(this DirectoryInfo baseDir, string name)
15 | {
16 | var list = baseDir.GetDirectories(name);
17 | if (list.Length == 1)
18 | {
19 | return (true, list[0]);
20 | }
21 | return (false, baseDir);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/IComponentResolver.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using ComponentHost;
3 |
4 | namespace ComponentHost
5 | {
6 | public interface IComponentResolver
7 | {
8 | ComponentResolution FindLibrary(string library);
9 | bool SetComponent(string component);
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net.Http;
6 | using System.Runtime.Loader;
7 | using System.Threading.Tasks;
8 | using interfaces;
9 | using ComponentHost;
10 | using ComponentResolverStrategies;
11 |
12 | namespace guttenapp
13 | {
14 | class Program
15 | {
16 | static async Task Main(string[] args)
17 | {
18 | var books = new Dictionary {
19 | {"Pride and Prejudice", "http://www.gutenberg.org/files/1342/1342-0.txt"},
20 | {"The War That Will End War", "http://www.gutenberg.org/files/57481/57481-0.txt"},
21 | {"Alice’s Adventures in Wonderland","http://www.gutenberg.org/files/11/11-0.txt"},
22 | {"Dracula","http://www.gutenberg.org/cache/epub/345/pg345.txt"},
23 | {"The Iliad of Homer","http://www.gutenberg.org/cache/epub/6130/pg6130.txt"},
24 | {"Dubliners","http://www.gutenberg.org/files/2814/2814-0.txt"},
25 | {"Gulliver's Travels","http://www.gutenberg.org/files/829/829-0.txt"}
26 | };
27 |
28 | var wordcountContext = new ComponentContext("wordcount",
29 | new ComponentResolverProductionStrategy(),
30 | new ComponentResolverBinDirectoryStrategy()
31 | );
32 |
33 | var mostcommonwordsContext = new ComponentContext("mostcommonwords",
34 | new ComponentResolverProductionStrategy(),
35 | new ComponentResolverBinDirectoryStrategy()
36 | );
37 |
38 | var wordCountAsm = wordcountContext.LoadAssemblyWithResolver("wordcount.dll");
39 | var mostcommonwordsAsm = mostcommonwordsContext.LoadAssemblyWithResolver("mostcommonwords.dll");
40 |
41 | var client = new HttpClient();
42 |
43 | foreach(var book in books)
44 | {
45 | var url = book.Value;
46 | using(var stream = await client.GetStreamAsync(url))
47 | using(var reader = new StreamReader(stream))
48 | {
49 | var wordCount = (IProvider)wordCountAsm.CreateInstance("Lit.WordCount");
50 | var mostcommonwords = (IProvider)mostcommonwordsAsm.CreateInstance("Lit.MostCommonWords");
51 | Task line;
52 | while (!reader.EndOfStream)
53 | {
54 | line = reader.ReadLineAsync();
55 | try
56 | {
57 | var wordCountTask = wordCount.ProcessTextAsync(line);
58 | var mostcommonwordsTask = mostcommonwords.ProcessTextAsync(line);
59 | await Task.WhenAll(wordCountTask, mostcommonwordsTask);
60 | }
61 | catch (Exception e)
62 | {
63 | Console.WriteLine(e.Message);
64 | throw e;
65 | }
66 | }
67 |
68 | var wordcountReport = wordCount.GetReport();
69 | Console.WriteLine($"Book: {book.Key}; Word Count: {wordcountReport["count"]}");
70 | Console.WriteLine("Most common words, with count:");
71 | var mostcommonwordsReport = mostcommonwords.GetReport();
72 | var orderedMostcommonwords = (IOrderedEnumerable>)mostcommonwordsReport["words"];
73 | var mostcommonwordsCount = (int)mostcommonwordsReport["count"];
74 |
75 | var index = 0;
76 | foreach (var word in orderedMostcommonwords)
77 | {
78 | if (index++ >= 10)
79 | {
80 | break;
81 | }
82 | Console.WriteLine($"{word.Key}; {word.Value}");
83 | }
84 | }
85 | }
86 |
87 | Console.WriteLine();
88 | foreach(var asm in AppDomain.CurrentDomain.GetAssemblies())
89 | {
90 | var context = AssemblyLoadContext.GetLoadContext(asm);
91 | var def = AssemblyLoadContext.Default;
92 | var isDefaultContext = context == def;
93 | var isWordcountContext = context == wordcountContext;
94 | var isMostcommonwordsContext = context == mostcommonwordsContext;
95 |
96 | if (asm.FullName.StartsWith("System") && isDefaultContext)
97 | {
98 | continue;
99 | }
100 |
101 | Console.WriteLine($"{asm.FullName} {asm.Location}");
102 | Console.WriteLine($"Default: {isDefaultContext}; WordCount: {isWordcountContext}; MostCommonWords: {isMostcommonwordsContext}");
103 | }
104 |
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/gutenapp/gutenapp/gutenapp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Exe
9 | netcoreapp2.1
10 | latest
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/gutenapp/interfaces/IProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace interfaces
6 | {
7 | public interface IProvider
8 | {
9 | Task ProcessTextAsync(Task text);
10 | Dictionary GetReport();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/gutenapp/interfaces/interfaces.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/gutenapp/mostcommonwords/MostCommonWords.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using System.Linq;
5 | using interfaces;
6 |
7 | namespace Lit
8 | {
9 | public class MostCommonWords : IProvider
10 | {
11 | private Dictionary _wordsCount = new Dictionary();
12 |
13 | public Dictionary GetReport()
14 | {
15 | var orderedList = _wordsCount.OrderByDescending( e=> { return e.Value;});
16 | var report = new Dictionary();
17 | report.Add("words",orderedList);
18 | report.Add("count",_wordsCount.Count);
19 | return report;
20 | }
21 |
22 | public async Task ProcessTextAsync(Task text)
23 | {
24 | var line = await text;
25 | return UpdateWordCounts(line);
26 | }
27 |
28 | private int UpdateWordCounts(string text)
29 | {
30 | var wasSpace = false;
31 | var lengthTest = text.Length -1;
32 | var start = 0;
33 | var index = 0;
34 | var newwords = 0;
35 |
36 | if (text == string.Empty)
37 | {
38 | return 0;
39 | }
40 |
41 | Span buffer = stackalloc char[100];
42 |
43 | while (true)
44 | {
45 |
46 | var c = text[index];
47 | var isSpace = c == ' ';
48 | string loweredWord = String.Empty;
49 |
50 | if (index >= lengthTest && index > 0)
51 | {
52 | var span = text.AsSpan(start);
53 |
54 | if (text.Length >100)
55 | {
56 | loweredWord = span.ToString().ToLowerInvariant();
57 | }
58 | else
59 | {
60 | span.ToLowerInvariant(buffer);
61 | loweredWord = buffer.Slice(0, text.Length).ToString();
62 | }
63 | newwords += AddWord(loweredWord);
64 | break;
65 | }
66 | else if (isSpace && !wasSpace)
67 | {
68 | var end = index - start;
69 | var span = text.AsSpan(start, end);
70 | if (text.Length > 100)
71 | {
72 | loweredWord = span.ToString().ToLowerInvariant();
73 | }
74 | else
75 | {
76 | span.ToLowerInvariant(buffer);
77 | loweredWord = buffer.Slice(0, end).ToString();
78 | }
79 | wasSpace = true;
80 | start = index + 1;
81 | newwords += AddWord(loweredWord);
82 | }
83 | else if (!isSpace && wasSpace)
84 | {
85 | wasSpace = false;
86 | }
87 | index++;
88 | }
89 | return newwords;
90 | }
91 |
92 | private int AddWord(string word)
93 | {
94 | if (word == string.Empty)
95 | {
96 | return 0;
97 | }
98 | else if (_wordsCount.ContainsKey(word))
99 | {
100 | _wordsCount[word]++;
101 | return 1;
102 | }
103 | else
104 | {
105 | _wordsCount.Add(word, 1);
106 | return 0;
107 | }
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/gutenapp/mostcommonwords/mostcommonwords.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | netcoreapp2.1
9 | latest
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/gutenapp/wordcount/WordCount.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using interfaces;
5 |
6 | namespace Lit
7 | {
8 | public class WordCount : IProvider
9 | {
10 | int _count = 0;
11 | int _lines = 0;
12 | public WordCount(){}
13 |
14 | public Task ProcessTextAsync(Task text)
15 | {
16 | _lines++;
17 | return CountAddAsync(text);
18 | }
19 |
20 | public Dictionary GetReport()
21 | {
22 | var d = new Dictionary();
23 | d.Add("count",_count);
24 | d.Add("lines", _lines);
25 | return d;
26 | }
27 |
28 | public int CountAdd(string text)
29 | {
30 | var wasSpace = false;
31 | var count = 0;
32 | foreach(var t in text)
33 | {
34 | var isSpace = Char.IsWhiteSpace(t);
35 | if (isSpace &&
36 | !wasSpace)
37 | {
38 | count++;
39 | wasSpace = true;
40 | }
41 | else if (!isSpace && wasSpace)
42 | {
43 | wasSpace = false;
44 | }
45 | }
46 |
47 | if (count > 0 && !wasSpace)
48 | {
49 | count++;
50 | }
51 | _count += count;
52 | return count;
53 | }
54 |
55 | public async Task CountAddAsync(Task text)
56 | {
57 | var t = await text;
58 | if (string.IsNullOrWhiteSpace(t))
59 | {
60 | return 0;
61 | }
62 | var count = CountAdd(t);
63 | return count;
64 | }
65 |
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/gutenapp/wordcount/wordcount.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | netstandard2.0
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/lib/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace lib
4 | {
5 | public class Class1
6 | {
7 | public static string GetString()
8 | {
9 | return "Bow ties are cool.";
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/lib/lib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/out/lib.deps.json:
--------------------------------------------------------------------------------
1 | {
2 | "runtimeTarget": {
3 | "name": ".NETStandard,Version=v2.0/release",
4 | "signature": "cfe1dc2a80602aef150a12815387068463a61a0d"
5 | },
6 | "compilationOptions": {},
7 | "targets": {
8 | ".NETStandard,Version=v2.0": {},
9 | ".NETStandard,Version=v2.0/release": {
10 | "lib/1.0.0": {
11 | "dependencies": {
12 | "NETStandard.Library": "2.0.3"
13 | },
14 | "runtime": {
15 | "lib.dll": {}
16 | }
17 | },
18 | "Microsoft.NETCore.Platforms/1.1.0": {},
19 | "NETStandard.Library/2.0.3": {
20 | "dependencies": {
21 | "Microsoft.NETCore.Platforms": "1.1.0"
22 | }
23 | }
24 | }
25 | },
26 | "libraries": {
27 | "lib/1.0.0": {
28 | "type": "project",
29 | "serviceable": false,
30 | "sha512": ""
31 | },
32 | "Microsoft.NETCore.Platforms/1.1.0": {
33 | "type": "package",
34 | "serviceable": true,
35 | "sha512": "sha512-RTmkgwugaI7tV0PjAwBX4ZKHbv/lMuIKUBeyIB0aosbGALFKIxiGvseJDF5ui5RBGbEJ5AvxZsGI0Rr6ZEfwaw==",
36 | "path": "microsoft.netcore.platforms/1.1.0",
37 | "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
38 | },
39 | "NETStandard.Library/2.0.3": {
40 | "type": "package",
41 | "serviceable": true,
42 | "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
43 | "path": "netstandard.library/2.0.3",
44 | "hashPath": "netstandard.library.2.0.3.nupkg.sha512"
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/lib/out/lib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richlander/dotnet-core-assembly-loading/5888536f099dc9db214383c767ab9eb1724c114a/src/lib/out/lib.dll
--------------------------------------------------------------------------------
/src/loadassemblybypath/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reflection;
4 | using System.Runtime.Loader;
5 | using static System.Console;
6 |
7 | namespace loadlibrary
8 | {
9 | class Program
10 | {
11 | static void Main(string[] args)
12 | {
13 | var assemblyLocation = "/Users/rlander/git/dotnet-core-assembly-loading/src/lib/out/lib.dll";
14 | WriteLine("Load lib.dll");
15 | var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyLocation);
16 | var type = assembly.GetType("lib.Class1");
17 | WriteLine("Call GetString method:");
18 | var method = type.GetMethod("GetString");
19 | var returnValue = method.Invoke(null,null);
20 | WriteLine(returnValue);
21 | WriteLine("Print loaded (non-platform) assemblies:");
22 | foreach(var asm in AppDomain.CurrentDomain.GetAssemblies())
23 | {
24 | if (asm.FullName.StartsWith("System") || asm.FullName.StartsWith("netstandard") )
25 | {
26 | continue;
27 | }
28 |
29 | WriteLine(asm.FullName);
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/loadassemblybypath/loadassemblybypath.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/test-prod.ps1:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env pwsh
2 |
3 | $BasePath = (Get-Item -Path "./").FullName
4 | $BaseGutenapp = Join-Path $BasePath "gutenapp"
5 | $GutenappDir = Join-Path $BaseGutenapp "gutenapp"
6 | $Gutenapp = Join-Path $GutenappDir "gutenapp.csproj"
7 | $WordcountDir = Join-Path $BaseGutenapp "wordcount"
8 | $Wordcount = Join-Path $WordcountDir "wordcount.csproj"
9 | $MostcommonwordsDir = Join-Path $BaseGutenapp "mostcommonwords"
10 | $Mostcommonwords = Join-Path $MostcommonwordsDir "mostcommonwords.csproj"
11 |
12 | dotnet publish $Gutenapp -c release -o out
13 | dotnet publish $Wordcount -c release -o out
14 | dotnet publish $Mostcommonwords -c release -o out
15 |
16 | Set-Location $BasePath
17 |
18 | $testdir = Join-Path . testdir
19 | New-Item -ItemType Directory -Path $testdir -Force
20 | Copy-Item ([System.IO.Path]::Combine($GutenappDir, "out", "*")) $testdir -Force
21 | New-Item -ItemType Directory -Path (Join-Path $testdir wordcount) -Force
22 | Copy-Item ([System.IO.Path]::Combine($WordcountDir, "out", "*")) (Join-Path $testdir wordcount) -Force
23 | New-Item -ItemType Directory -Path (Join-Path $testdir mostcommonwords) -Force
24 | Copy-Item ([System.IO.Path]::Combine($MostcommonwordsDir, "out", "*")) (Join-Path $testdir mostcommonwords) -Force
25 |
26 | dotnet (Join-Path $testdir gutenapp.dll)
--------------------------------------------------------------------------------
/src/test.ps1:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env pwsh
2 |
3 | $BasePath = (Get-Item -Path "./").FullName
4 | $BaseGutenapp = Join-Path $BasePath "gutenapp"
5 | $GutenappDir = Join-Path $BaseGutenapp "gutenapp"
6 | $Gutenapp = Join-Path $GutenappDir "gutenapp.csproj"
7 |
8 |
9 | .\build.ps1
10 |
11 | dotnet run --project $Gutenapp -c debug
12 | dotnet run --project $Gutenapp -c release
--------------------------------------------------------------------------------