Developer forum

Forum » Development » Locally installing NuGet package

Locally installing NuGet package

Justin Sjouw Dynamicweb Employee
Justin Sjouw
Reply

Hi, 

I'm having some trouble installing a (custom) nuget package.  I've attached both the zipped project and the package.

  • Files in the package in contentFiles\any\any\ do not seem to get installed into the files directory
  • The package installs correctly, but in the App Store selecting in from the "Installed Apps" list results in an error with a null reference on version

This is the first time I'm attempting this so a push in the right direction would be much appreciated :-)

Thanks,

Justin


Replies

 
Justin Sjouw Dynamicweb Employee
Justin Sjouw
Reply

I someone could try and install the package to see if the behaviour is the same, I would highly appreciate it :-)

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

From Chatgpt:

I had a close look at your project file and the behavior you describe, and a couple of things stand out that might explain both the missing files and the null reference on version.


1. contentFiles in NuGet is tricky

In .NET Core / .NET 8, contentFiles doesn’t behave the same way as the old content folder did in NuGet.

  • contentFiles is meant for source files or assets that get included into a project when it references your package.

  • But in your case, Dynamicweb’s app system expects physical files under /Files in the application root, not just entries in the consuming project’s build. NuGet doesn’t automatically copy them to your Dynamicweb instance.

That explains why you see the package install correctly but no files show up in Files.

Fix: You’ll probably want to package them as content (legacy style) or use a targets file inside the NuGet package to copy them at install-time to the Files folder. With contentFiles alone, nothing will land in your Dynamicweb /Files.


2. Null reference on version in “Installed Apps”

Dynamicweb’s App Store integration looks at the package metadata (<Version> and nuspec info).

  • You have <VersionPrefix> and some conditional <Version> properties, but note: NuGet will only use the evaluated <Version> at pack time. If that doesn’t resolve correctly, the .nuspec might end up with an empty <version/>.

  • If the version is missing, Dynamicweb will throw a null reference when trying to display it in Installed Apps.

Try forcing a simple static version to confirm:

<PropertyGroup>
  <Version>1.0.0</Version>
</PropertyGroup>

Build and repack. If the error disappears, your version condition logic was the culprit.


3. Small details in your .csproj

  • <PackageCopyToOutput> is not a valid MSBuild property in this context. The correct way is <CopyToOutputDirectory>Always</CopyToOutputDirectory> or PreserveNewest.

  • <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> is rarely needed in modern SDK-style projects. It can be dropped unless you’re debugging dependency resolution.

  • <FrameworkReference Include="Microsoft.AspNetCore.App" /> is redundant with net8.0. It doesn’t hurt, but you don’t need it.


Recommended adjustments

Try this in your .csproj:

<ItemGroup>
  <!-- Include all Files folder content in the NuGet package -->
  <Content Include="Files\**\*.*" Pack="true" PackagePath="content\Files\%(RecursiveDir)%(Filename)%(Extension)">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>

<PropertyGroup>
  <!-- Simple explicit version for testing -->
  <Version>1.0.0</Version>
</PropertyGroup>

Notice the difference:

  • PackagePath="content\Files\..." instead of contentFiles.

  • Explicit version.

Then repack and reinstall.


Why this matters for Dynamicweb apps

Dynamicweb’s “Installed Apps” list isn’t just NuGet—it’s reading from its own app system layered on top. It expects:

  • A proper version number in the NuGet metadata.

  • Files physically in /Files after installation.

NuGet’s modern contentFiles won’t do that for you, so you need either:

  • Old-school content\ packaging, or

  • A .targets file to copy things where you need them on install.


If you want, I can sketch a .targets file that copies your Files contents into wwwroot/Files (or Dynamicweb’s Files folder) automatically when the NuGet is installed. Would you like me to?

YES!

 

Perfect, let’s wire this up properly.
The trick is: a .targets file inside your NuGet package can hook into the build of the consuming project and copy your Files folder into Dynamicweb’s /Files folder.

Here’s how you can do it:


1. Add a .targets file to your project

Create a file called MarkdownToolkit.targets (name it after your package) in your project root:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="CopyMarkdownToolkitFiles" AfterTargets="Build">
    <!-- Source: content files inside the package -->
    <ItemGroup>
      <MarkdownToolkitFiles Include="$(MSBuildThisFileDirectory)..\content\Files\**\*.*" />
    </ItemGroup>

    <!-- Destination: Dynamicweb's Files folder -->
    <Copy 
      SourceFiles="@(MarkdownToolkitFiles)" 
      DestinationFolder="$(MSBuildProjectDirectory)\Files\%(RecursiveDir)" 
      SkipUnchangedFiles="true" />
  </Target>
</Project>

This says:

  • After build, take everything from the NuGet package’s content\Files directory.

  • Copy it into the consuming project’s /Files folder.


2. Include this .targets file in your NuGet package

Update your .csproj so it packs the .targets file correctly:

<ItemGroup>
  <None Include="MarkdownToolkit.targets" Pack="true" PackagePath="build\MarkdownToolkit.targets" />
</ItemGroup>

Now, when someone installs your NuGet, MSBuild will import your .targets automatically.


3. Keep your Files folder packaging clean

Instead of contentFiles, switch back to content:

<ItemGroup>
  <Content Include="Files\**\*.*" Pack="true" PackagePath="content\Files\%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

That way, NuGet places your files under content/Files in the .nupkg, and the .targets file will copy them into the Dynamicweb solution’s /Files.


4. Test the flow

  • Build your package (dotnet pack).

  • Install it into your Dynamicweb 10 project.

  • Rebuild the solution.

You should see your files appear in Files\ in the consuming Dynamicweb site, and the Installed Apps view should now display the correct version (after fixing the <Version> property as I mentioned earlier).


This approach is pretty standard for packages that need to drop “physical assets” into a web project (like JS, CSS, or, in your case, app files).

Do you want me to also show you a minimal nuspec override that ensures Dynamicweb sees the correct metadata (version, description, etc.), so you can avoid the null reference issue altogether?

 
Justin Sjouw Dynamicweb Employee
Justin Sjouw
Reply

Thanks NicolAI! Unfortunately ChatGPT was basically wrong about everything, but it did help me forward ;-)

Turns out the null reference actually came from referencing DW ring1 like this apparently: <PackageReference Include="Dynamicweb.Suite.Ring1" Version="*" />

Changed that to a specific version : <PackageReference Include="Dynamicweb.Suite" Version="10.18.10" /> Which fixed it.

I still cannot get my files in the right place. If I pack the files like this, I end up with the files inside the Files folder under assets.

<Content Include="Files\**\*.*" Pack="true" PackagePath="Files/%(RecursiveDir)%(Filename)%(Extension)">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

If I remove Files, the files are like below in the nupkg, but not added in the corresponding folders (System\Items and Templates\etc...):

<Content Include="Files\**\*.*" Pack="true" PackagePath="%(RecursiveDir)%(Filename)%(Extension)">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

 

 

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Did you try

<Content Include="Files\**\*.*" Pack="true" PackagePath="contentFiles/%(RecursiveDir)%(Filename)%(Extension)">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 </Content>

Also the resulting nuget package should not reference ring1. You custom solution should have 2 projects - a runner/program.cs that references the ring1 and then your actual package that only references what you need.

 
 
Justin Sjouw Dynamicweb Employee
Justin Sjouw
Reply

Yes I tried that, and a couple of others structures mentioned before. when I have Sytem and Templates as a subfolder of contentFiles in the package, they do not get copied.

Long story short I cannot get Items and Templates to install to the correct location. 

The other issues are solved.

 
Justin Sjouw Dynamicweb Employee
Justin Sjouw
Reply

Any thoughts? I do have one, which is to add some more logging to the commandlog when installing apps. Right now, I was hoping to at least see the nuget package that get installed, and maybe even the files that get copied as part of the process. But the log now does not show much:

 
Jesse Bakker
Reply

Justin, did you find a way to place the files in the correct folders? 

For us everything from /Files/ in the package ends up in the AddIn/Installed/ folder next the installed pacakage.

When checking LocalAddinProvider.InstallNuGetPackage it seems like we can't alter this location. 

While the documentation states the following: 
Any files included in the package under a Files/ folder are extracted to the website's /Files/ directory. (Getting Started | Dynamicweb 10 Developer Documentation)

 
Justin Sjouw Dynamicweb Employee
Justin Sjouw
Reply

Hi Jesse,

No, I did not. I think it would be good if this got some attention. I'm currently building an app that I would like to include Item types and Templates with, and just like you I cannot get them to install into the website's /Files/ folder, everything ends up next to the package in the AddIn/Installed/ folder instead, no matter how I structure the package (Files/, content/, contentFiles/  I tried them all earlier in this thread).

Your finding in LocalAddinProvider.InstallNuGetPackage matches what I was seeing: the extract location is hardcoded, so there's currently no way to get package content into System/Items, Templates/ etc. That also means the documentation is incorrect (or describes intended-but-unimplemented behaviour) where it says files under Files/ are extracted to the website's /Files/ directory.

@Nicolai could this be picked up? Making LocalAddinProvider respect the Files/ convention as documented, not sure if the problem is the same for installing an AppStore package?

Best, Justin

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

The behaviour you're seeing is expected. The App Store installer does not look at contentFiles for file deployment — that NuGet convention is for compile-time project inclusion, not runtime file installation, so nothing in contentFiles\any\any\ gets copied to disk.

To get files installed into the solution, the installer looks for a top-level Files/ folder inside the .nupkg. Every entry under that folder is extracted into the website's /Files/ directory, preserving the sub-path. So Files/Templates/MyApp/list.cshtml in the package lands at /Files/Templates/MyApp/list.cshtml on the solution.

In your .csproj, pack the content with an explicit PackagePath rooted at Files (use None rather than Content so MSBuild doesn't reroute it into contentFiles):

<ItemGroup>

  <None Include="Files\**\*.*"

        Pack="true"

        PackagePath="Files\%(RecursiveDir)%(Filename)%(Extension)" />

</ItemGroup>

After packing, open the .nupkg (it's a zip) and confirm you see a Files/... folder at the package root — not contentFiles/. That's the deciding factor.

A couple of things to be aware of:

  • The destination is always /Files — you choose the sub-path within /Files by mirroring that structure inside the package's Files/ folder. You can't target locations outside /Files (app root, bin, config, etc.) through the package.
  • Existing files are overwritten on install; there's no merge or skip-if-exists, so don't ship files a user is expected to have customised.

On the separate null-reference error when selecting the package from Installed Apps: that comes from the package having no resolvable version for the running framework. The installer only accepts assemblies built for a fixed set of target frameworks (netstandard2.0, net5.0–net8.0, plus net9.0/net10.0 when the solution itself runs on .NET 10) and reads the version off the loaded assembly. A package that contains only files under Files/ and no lib/<tfm>/ assembly has nothing to load, which is what produces the null version. Make sure the package contains a lib/<tfm>/ assembly targeting one of the supported frameworks.

 
Jesse Bakker
Reply

Hi Nicolai,

I've tried your exact format in a example project. See attached together with the nupkg as result. 

This contains the /Files/Templates in the root of the package, but after installing it is still placed in the /system/addins/installed/files folder.

I've tested this on 10.25.9. Am I missing something?

 

 

 

 

You must be logged in to post in the forum