Overcoming EF Run-time Exceptions when Deploying .Net Core 3.1+ to Service Fabric

February 29, 2020    ServiceFabric .Net

Overcoming Entity Framework Run-time Exceptions when Deploying .Net Core 3.1+ to Service Fabric

I recently had a several day fight with a run-time exception. I lost way too much time on this. It came on a deadline week too, of course. I hope this helps you save a lot of time.

I have a lot of notes and info. You’re probably looking for the exception details. The solution is at the bottom.

I’ve created an issue on the Service Fabric Github page .

I was happily putting off creating the Azure DevOps pipeline and right-clicking from Visual Studio to publish. This was working great. A co-worker got freed up to create the release of the pipeline for me (I am able to do it, but was just too busy). After the first deploy, I got a new run-time exception (see below). I learned that I shouldn’t put off the automation (build and release) till later. I should do it right away as Jeremy Likness suggests on the Azure DevOps Podcast .

It is important to note that the publishing in ADO .Net with the Visual Studio Build task (msbuild /t:packge) works with .Net Core 2.2. I haven’t checked the differences in output between msbuild and dotnet publish for the Microsoft.Data.SqlClient.dll

My tech stack:

  • Service Fabric OnPremise - version 7.0.457.9590

  • .Net Core 3.1.2 (netcoreapp31)

  • Entity Framework Core 3.1.2

  • Azure DevOps 2019 OnPremise

  • 2 Asp .Net Core 3.1.2 projects for a publisher/subscriber setup

The Problem

Visual Studio, right-click publish on the .sfproj worked great. The Azure DevOps pipeline release has this run-time exception.

Note: I’ve changed namespaces to K. to remove company specific details.

System.PlatformNotSupportedException: Microsoft.Data.SqlClient is not supported on this platform. at Microsoft.Data.SqlClient.SqlConnection..ctor(String connectionString) at Microsoft.

The exception stack trace.

An exception occurred while iterating over the results of a query for context type 'K.Infrastructure.KDbContext'. System.PlatformNotSupportedException: Microsoft.Data.SqlClient is not supported on this platform. at Microsoft.Data.SqlClient.SqlConnection..ctor(String connectionString) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.CreateDbConnection() at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.get_DbConnection() at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.CreateCommand(RelationalCommandParameterObject parameterObject, Guid commandId, DbCommandMethod commandMethod) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`…

Thankfully that exception message is really helpful and pointed me to Microsoft.Data.SqlClient.dll.

I noticed that the Microsoft.Data.SqlClient (version 1.0.19269.1) was different from the VS publish to the ADO publish.

Microsoft.Data.SqlClient side by side

The left image is (publish from VS) right is from ADO. Interesting that the dll sizes are different…. Right click > package in VS, has a 1,199 KB Microsoft.Data.SqlClient.dll

.Net Core 2.2 doesn’t have this issue in our other SF Apis

Nuget on my machine: These are all version 1.0.19269.1 that I found on my laptop.

  • C:\Users\k.nuget\packages\microsoft.data.sqlclient\1.0.19269.1\runtimes\win\lib\netcoreapp2.1 is 1,* 199 KB
  • C:\Users\k.nuget\packages\microsoft.data.sqlclient\1.0.19269.1\runtimes\win\lib\netstandard2.0 is * 1,177 KB
  • C:\Users\k.nuget\packages\microsoft.data.sqlclient\1.0.19269.1\lib\netcoreapp2.1 is 322 KB
  • C:\Users\k.nuget\packages\microsoft.data.sqlclient\1.0.19269.1\ref\netcoreapp2.1 is 59 KB

I don’t understand what all those are, but I was thinking there’d be a netcoreapp3.1. Maybe it used the netstandard2.0 version? (let me know if you know)

Failed (learning) Attempts to Fix and information gathered

  • I ran dotnet publish on the Api projects. That’s where I saw that the Microsoft.Data.SqlClient.dll was different.
  • I talked to colleagues at Omnitech that have dealt with the same issue.
  • I tried the suggested solution of checking in the correct dll and reference it from the K.Infrastucture.csproj that had EF referenced and others reference this.
  • I bingled the issue. I found a lot of Azure Function references (see some links below).
  • I attempted the solutions in the Github issues. One was adding this to the SF project
<Target Name="PostPublish" BeforeTargets="Publish">
    <Exec Command="move $(PublishDir)\runtimes $(PublishDir)\bin" />
</Target>

Why and how is VS package different than ADO package command?

Both call MS build, so it must be repeatable from my machine… Right?

In the ADO Build, Build solution **\Prepaid.KServiceFabric.sfproj step, advanced > MS Build architecture to x64 Build logs: ##[command]“C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\msbuild.exe” “D:\agent_work\108\s\Repo\Source\ServiceFabric\K.ServiceFabric.sfproj” /nologo /nr:false /t:“Clean” … /p:SolutionDir=“D:\agent_work\108\s\Repo\Source” /t:Package /p:platform=“x64” /p:configuration=“Release” /p:VisualStudioVersion=“16.0”

I couldn’t see VS’s direct call when I right-click. I guess they call msbuild inside of a sandbox. I tried Procman from the suggestion on StackOverflow , but I still couldn’t see it. I also turned build logging output to detailed.

My Solution - what you are looking for

I finally decided to go my own way. I know that dotnet publish got the right dll. I also learned that I couldn’t use dotnet cli to create a SF package . I created a .bat file to have something to run locally quickly. Then I moved that .bat file into the Azure DevOps build and called that. In the future, I may split it back out into separate build tasks.

Here is my bat file. Notice the steps.

  1. use dotnet publish. I’m targeting win10-x64 since that’s the only runtime I need right now
  2. use msbuild /t:package to create the needed SF package files
  3. copy the dotnet publish into the Service Fabric/Code directories
  4. Copy other needed xml files for SF publishing.
REM Package K for SF
@echo off
set rootDir=%1
Echo Root Directory is %rootDir%

set publishDir=%rootDir%sfPublish
Echo Publish Directory is %publishDir%

set sourceDir=%rootDir%Source
Echo Source Directory is %sourceDir%

set deployDir=%publishDir%\deploy
Echo Deploy Directory is %deployDir%

Echo Delete %publishDir%
RD /S /Q  %publishDir%

Echo delete %sourceDir%\K\ServiceFabric\pkg\Release
RD /S /Q %sourceDir%\K\ServiceFabric\pkg\Release

Echo Dotnet core publish Provider
dotnet publish %sourceDir%\K\Provider\Api\K.Provider.Api.csproj --runtime win10-x64 -c Release --framework netcoreapp3.1 /p:SolutionDir=%sourceDir% --self-contained -o %publishDir%\Provider\

Echo Dotnet core publish Consumer
dotnet publish %sourceDir%\L\Consumer\ApiWorkerForSf\K.Consumer.ApiWorkerForSf.csproj --runtime win10-x64 -c Release --framework netcoreapp3.1 /p:SolutionDir=%sourceDir% --self-contained -o %publishDir%\Consumer\

Echo msbuild /t:Package .sfproj
REM full path to msbuild is needed for the build agent
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\msbuild.exe" %sourceDir%\K\ServiceFabric\K.ServiceFabric.sfproj /p:SolutionDir=%sourceDir% /t:Package /p:platform="x64" /p:configuration="Release"

Echo copy pkg from K\ServiceFabric\ to publish\SfPackage
ROBOCOPY /E %sourceDir%\K\ServiceFabric\pkg\Release %publishDir%\SfPackage

ROBOCOPY "%publishDir%\SfPackage" "%deployDir%\Release" ApplicationManifest.xml

Echo copy files from publish\Provider to deploy\SfPackage\
ROBOCOPY /E "%publishDir%\SfPackage\K.Provider.ServiceFabricPkg" "%deployDir%\Release\K.Provider.ServiceFabricPkg"
ROBOCOPY /E "%publishDir%\Provider" "%deployDir%\Release\K.Provider.ServiceFabricPkg\Code"

Echo copy files from publish\Consumer to deploy\SfPackage\
ROBOCOPY /E "%publishDir%\SfPackage\K.Consumer.ServiceFabricPkg" "%deployDir%\Release\K.Consumer.ServiceFabricPkg"
ROBOCOPY /E "%publishDir%\Provider" "%deployDir%\Release\K.Consumer.ServiceFabricPkg\Code"


Echo Copy SF files
ROBOCOPY /E "%sourceDir%\K\ServiceFabric\ApplicationPackageRoot" "%publishDir%\SfPackage\ApplicationPackageRoot
ROBOCOPY /E "%sourceDir%\K\ServiceFabric\ApplicationParameters" "%publishDir%\SfPackage\ApplicationParameters
ROBOCOPY /E "%sourceDir%\K\ServiceFabric\PublishProfiles" "%publishDir%\SfPackage\PublishProfiles

Echo Copy Deploy Script
ROBOCOPY "%sourceDir%\K\ServiceFabric\Scripts" "%publishDir%" Deploy-FabricApplication.ps1

REM result we want in deploy/ is a release folder in the root that has sf packages, with dotnet publish dlls
REM applicationManifest.xml at the root
REM 2 ServiceFabricPkg/Code and /Config with ServiceManifest.xml

Here’s what it looks like in the ADO build. We have an intermediate CI build that makes that package. That gets picked up by the release that uses the built in SF task steps.

Path: $(RepoPath)\Build\createSfPackage.bat Arguments: $(RepoPath)\

Zip: Script Path: $(Build.SourcesDirectory)\PowerShell\Zip.ps1 Arguments: -FolderToZip "$(RepoPath)\SfPublish\deploy\" -DestinationPath "$(SfPackageStagingPath)/PRS" -DestinationFileName "PRS-$(Version).zip"

SF Build steps with the bat

I wish I had Containers

The good news is that now I have a good story about how a container would have helped me. I’d also like to put my same code into Kubernetes, but the organization isn’t ready for that big of a change. I have a lot to learn about that approach as well.

Hopefully you won’t run into this or Microsoft will be able to fix the SF packaging so we won’t need this work around.

References



Watch the Story for Good News
I gladly accept BTC Lightning Network tips at [email protected]

Please consider using Brave and adding me to your BAT payment ledger. Then you won't have to see ads! (when I get to $100 in Google Ads for a payout, I pledge to turn off ads)

Use Brave

Also check out my Resources Page for referrals that would help me.


Swan logo
Use Swan Bitcoin to onramp with low fees and automatic daily cost averaging and get $10 in BTC when you sign up.