<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>2022-10 on Funky Si's Blog (Dev)</title><link>https://blog-dev.funkysi1701.com/2022/10/</link><description>Recent content in 2022-10 on Funky Si's Blog (Dev)</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><managingEditor>funkysi1701@gmail.com (Simon Foster)</managingEditor><webMaster>funkysi1701@gmail.com (Simon Foster)</webMaster><lastBuildDate>Tue, 18 Oct 2022 18:00:45 +0000</lastBuildDate><atom:link href="https://blog-dev.funkysi1701.com/2022/10/index.xml" rel="self" type="application/rss+xml"/><item><title>Expiring Azure AD Client Secrets</title><link>https://blog-dev.funkysi1701.com/posts/2022/expiring-azuread-client-secrets/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Tue, 18 Oct 2022 18:00:45 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2022/expiring-azuread-client-secrets/</guid><category term="DotNet">DotNet</category><category term="Azure">Azure</category><category term="ActiveDirectory">ActiveDirectory</category><category term="Secret">Secret</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/AzureADSecret.png"/><description>&lt;p&gt;This morning the client complains that the website is not working. I do some checking and I confirm that there is indeed an error.&lt;/p&gt;
&lt;p&gt;The error they are seeing is:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;An unhandled exception occurred while processing the request.
MsalServiceException: A configuration issue is preventing authentication
- check the error message from the server for details.
You can modify the configuration in the application registration portal.
See https://aka.ms/msal-net-invalid-client for details.
Original exception: AADSTS7000222: The provided client secret keys for
app &amp;#39;00000000-0000-0000-0000-000000000000&amp;#39; are expired.
Visit the Azure portal to create new keys for your app: https://aka.ms/NewClientSecret,
or consider using certificate credentials for added security: https://aka.ms/certCreds.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The fix for this is relatively simple, generate a new client secret in Azure AD and update the website config to make use of it.&lt;/p&gt;
&lt;p&gt;The problem is that this problem will reoccur in 3, 6, 12, 18 or 24 months, depending on what expiry date you set on the secret.&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="Add a client secret" src="https://blog-dev.funkysi1701.com/images/AzureADSecret.png" loading="lazy"
width="572" height="350"
/&gt;
&lt;/p&gt;
&lt;p&gt;A suggestion from twitter is that Logic Apps and Event Grid might help solve this problem. Lets see how this might work.&lt;/p&gt;
&lt;p&gt;Logic Apps have a consumption plan, so this kind of solution shouldn&amp;rsquo;t break the bank as will only cost per run eg daily/weekly etc&lt;/p&gt;
&lt;p&gt;But first here is a powershell script that lists all the secrets that are due to expire in the next 30 days.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#secret expiration date filter (for example 30 days)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$LimitExpirationDays = &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#Retrieving the list of secrets that expires in the above range of days&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$SecretsToExpire = Get-AzureADApplication -All:$true | ForEach-Object {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $app = $_
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Get-AzureADApplicationPasswordCredential -ObjectId $_.ObjectId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Get-AzureADApplicationKeyCredential -ObjectId $_.ObjectId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) | Where-Object {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $_.EndDate &lt;span style="color:#f92672"&gt;-lt&lt;/span&gt; (Get-Date).AddDays($LimitExpirationDays)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } | ForEach-Object {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $id = &lt;span style="color:#e6db74"&gt;&amp;#34;Not set&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;($_.CustomKeyIdentifier) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $id = [&lt;span style="color:#66d9ef"&gt;System.Text.Encoding&lt;/span&gt;]::UTF8.GetString($_.CustomKeyIdentifier)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#66d9ef"&gt;PSCustomObject&lt;/span&gt;] @{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; App = $app.DisplayName
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ObjectID = $app.ObjectId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; AppId = $app.AppId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Type = $_.GetType().name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; KeyIdentifier = $id
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; EndDate = $_.EndDate
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#Printing the list of secrets that are near to expire&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;($SecretsToExpire.Count &lt;span style="color:#f92672"&gt;-EQ&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Output &lt;span style="color:#e6db74"&gt;&amp;#34;No secrets found that will expire in this range&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Output &lt;span style="color:#e6db74"&gt;&amp;#34;Secrets that will expire in this range:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Output $SecretsToExpire.Count
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Output $SecretsToExpire
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works quite well, but I have to manually run it every so often. I have not found a way to automate this as I don&amp;rsquo;t know how to get past the MFA that is configured on my Azure AD tenant.&lt;/p&gt;
&lt;p&gt;Now lets look at how a Logic App might help us. First of all we need somewhere to query the expiry dates, and Azure Key Vault is the ideal place to store them.&lt;/p&gt;
&lt;p&gt;Create a Key Vault if you don&amp;rsquo;t already have one and add a new Secret. Manually add the Client Secret from Azure AD and give it a name you can identify it with. Tick the Set expiration date and save the expiry date of your secret.&lt;/p&gt;
&lt;p&gt;Now Create a Logic App, I am going to use Consumption, so it only costs you when it runs.&lt;/p&gt;
&lt;p&gt;Using the Logic Apps Designer, start with a Recurrence trigger. This lets you queue up your logic app on a schedule, I&amp;rsquo;ve set mine to run every 30 days.&lt;/p&gt;
&lt;p&gt;Next run a Get Secret command from your Key Vault. Select the identifying name you saved the secret with earlier. This will retrieve the secret from the Key Vault, if you go into the settings you can hide the output as you are only really interested in the expiry date.&lt;/p&gt;
&lt;p&gt;I am doing a low tech solution for now, and just emailing myself the expiry date. It is easy to add the expiry date of the secret to an email.&lt;/p&gt;
&lt;p&gt;Later on I might add a bit more logic to only email myself when the expiry is less than 30 days, a reminder to create a new secret. When creating a new secret in Azure AD, I also need to add the secret and expiry date into Key Vault.&lt;/p&gt;
&lt;p&gt;At the very least this solution will give me an early warning of when my secrets are about to expire. It is possible to get my website to read information from Key Vault directly which I may well investigate.&lt;/p&gt;</description></item><item><title>Upgrading to .Net 7</title><link>https://blog-dev.funkysi1701.com/posts/2022/dotnet7/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Sat, 01 Oct 2022 18:00:45 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2022/dotnet7/</guid><category term="DotNet">DotNet</category><category term="Blazor">Blazor</category><category term="EFCore">EFCore</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/coverage.png"/><description>&lt;p&gt;I have a Blazor application that was built with .Net Core 3, and I have updated it to .Net 5 and later .Net 6 when those versions of were released. In November .Net 7 is released so I have been testing it with the .Net 7 previews.&lt;/p&gt;
&lt;p&gt;However for this update I encountered a few more issues than I remember from the other updates.&lt;/p&gt;
&lt;h2 id="automapper"&gt;Automapper&lt;a class="anchor ms-1" href="#automapper" aria-label="Permalink: Automapper"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first issue I encountered was with Automapper. My Unit tests failed with the following error&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Error Message:
System.ArgumentException : GenericArguments[0], &amp;#39;System.Char&amp;#39;, on &amp;#39;T MaxFloat[T](System.Collections.Generic.IEnumerable`1[T])&amp;#39; violates the constraint of type &amp;#39;T&amp;#39;.
---- System.Security.VerificationException : Method System.Linq.Enumerable.MaxFloat: type argument &amp;#39;System.Char&amp;#39; violates the constraint of type parameter &amp;#39;T&amp;#39;.
Stack Trace:
at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
at AutoMapper.Internal.TypeDetails.&amp;lt;&amp;gt;c__DisplayClass25_1.&amp;lt;GetPublicNoArgExtensionMethods&amp;gt;b__10(MethodInfo extensionMethod)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Enumerable.ConcatIterator`1.MoveNext()
at System.Linq.Enumerable.SelectManyIterator[TSource,TCollection,TResult](IEnumerable`1 source, Func`2 collectionSelector, Func`3 resultSelector)+MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.UnionIterator`1.GetNext()
at System.Linq.Enumerable.UnionIterator`1.MoveNext()
at System.Linq.Enumerable.ConcatIterator`1.MoveNext()
at AutoMapper.Internal.TypeDetails.PossibleNames()
at AutoMapper.Internal.TypeDetails.GetMember(String name)
at AutoMapper.Configuration.Conventions.DefaultName.GetMatchingMemberInfo(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, String nameToSearch)
at AutoMapper.Configuration.Conventions.ParentSourceToDestinationNameMapper.GetMatchingMemberInfo(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, String nameToSearch)
at AutoMapper.Configuration.Conventions.DefaultMember.MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceTypeDetails, Type destType, Type destMemberType, String nameToSearch, List`1 resolvers, IMemberConfiguration parent, Boolean isReverseMap)
at AutoMapper.Configuration.Conventions.MemberConfiguration.MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, String nameToSearch, List`1 resolvers, Boolean isReverseMap)
at AutoMapper.Configuration.Conventions.NameSplitMember.MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, String nameToSearch, List`1 resolvers, IMemberConfiguration parent, Boolean isReverseMap)
at AutoMapper.Configuration.Conventions.MemberConfiguration.MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, String nameToSearch, List`1 resolvers, Boolean isReverseMap)
at AutoMapper.ProfileMap.MapDestinationPropertyToSource(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, String destMemberName, List`1 members, Boolean reverseNamingConventions)
at AutoMapper.TypeMap..ctor(Type sourceType, Type destinationType, ProfileMap profile, Boolean isReverseMap)
at AutoMapper.ProfileMap.BuildTypeMap(IGlobalConfiguration configurationProvider, ITypeMapConfiguration config)
at AutoMapper.ProfileMap.Register(IGlobalConfiguration configurationProvider)
at AutoMapper.MapperConfiguration.Seal()
at AutoMapper.MapperConfiguration..ctor(MapperConfigurationExpression configurationExpression)
at AutoMapper.MapperConfiguration..ctor(Action`1 configure)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
----- Inner Stack Trace -----
at System.RuntimeMethodHandle.GetStubIfNeeded(RuntimeMethodHandleInternal method, RuntimeType declaringType, RuntimeType[] methodInstantiation)
at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At first I didn&amp;rsquo;t realise it was an issue with Automapper and I started searching for breaking changes in dotnet 7. Eventually this lead to finding that a change to .Net had created an issue with version 11 of Automapper. The following github issues explains more about what was involved in fixing this.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dotnet/runtime/issues/69119" target="_blank" rel="noopener noreferrer"&gt;https://github.com/dotnet/runtime/issues/69119&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/AutoMapper/AutoMapper/pull/3999" target="_blank" rel="noopener noreferrer"&gt;https://github.com/AutoMapper/AutoMapper/pull/3999&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;This week &lt;a href="https://github.com/AutoMapper/AutoMapper/releases/tag/v12.0.0" target="_blank" rel="noopener noreferrer"&gt;version 12&lt;/a&gt;
of Automapper was released and it fixed this issue for me.&lt;/p&gt;
&lt;h2 id="code-coverage"&gt;Code Coverage&lt;a class="anchor ms-1" href="#code-coverage" aria-label="Permalink: Code Coverage"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My build includes the calculation of code coverage of my unit tests. For some reason this step was failing.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; - task: DotNetCoreCLI@2
displayName: &amp;#39;Running API Tests&amp;#39;
inputs:
command: &amp;#39;test&amp;#39;
arguments: &amp;#39;--no-restore --no-build --configuration $(buildConfiguration) --runtime win-x64 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover&amp;#39;
projects: &amp;#39;Path To csproj file&amp;#39;
nobuild: true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For .Net 6 this step would run my unit tests and include a section that looked a bit like this.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Calculating coverage result...
Generating report &amp;#39;D:\a\1\s\WhatEver\coverage.opencover.xml&amp;#39;
+---------------------------+--------+--------+--------+
| Module | Line | Branch | Method |
+---------------------------+--------+--------+--------+
| Something | 50.91% | 48.77% | 65.44% |
+---------------------------+--------+--------+--------+
| Something.Core | 41.79% | 6.92% | 47.2% |
+---------------------------+--------+--------+--------+
| Something.Core.Email | 96.55% | 50% | 100% |
+---------------------------+--------+--------+--------+
+---------+--------+--------+--------+
| | Line | Branch | Method |
+---------+--------+--------+--------+
| Total | 49.46% | 44.2% | 53.55% |
+---------+--------+--------+--------+
| Average | 63.08% | 35.22% | 70.88% |
+---------+--------+--------+--------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However for .Net 7 the unit tests would pass but the coverage bit above would not run, and no visible errors to help me.&lt;/p&gt;
&lt;p&gt;Testing dotnet test locally I discovered that if I included the path and filename to the csproj file the coverage would not run, if I removed this it would work as per .Net 6. No idea why this was happening.&lt;/p&gt;
&lt;p&gt;I did some testing with the DotNetCoreCLI@2 task, but I couldn&amp;rsquo;t get the coverage to work. However I was testing this locally via the command line so it was a simple thing to swap DotNetCoreCLI@2 for a command line step. After I did that the coverage started working again!&lt;/p&gt;
&lt;p&gt;Here is the command line step I added&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; - task: CmdLine@2
displayName: &amp;#39;Running API Tests&amp;#39;
inputs:
script: |
&amp;#34;C:\Program Files\dotnet\dotnet.exe&amp;#34; test --logger trx --results-directory D:\a\_temp --no-restore --no-build --configuration Release --runtime win-x64 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
workingDirectory: &amp;#39;$(Build.SourcesDirectory)/PathToWhereEver&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="sql-server-tables-with-triggers"&gt;SQL Server tables with triggers&lt;a class="anchor ms-1" href="#sql-server-tables-with-triggers" aria-label="Permalink: SQL Server tables with triggers"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My build was all working so now time to test the application was working. But an error was being thrown if I tried to save any data to the SQL Server database.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Could not save changes because the target table has database triggers. Please configure your entity type accordingly, see https://aka.ms/efcore-docs-sqlserver-save-changes-and-triggers for more information. The target table &amp;#39;Something&amp;#39; of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A nice error, as it gives me a link to the change and what to do to fix.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;protected&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; configurationBuilder.Conventions.Add(_ =&amp;gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; BlankTriggerAddingConvention());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;BlankTriggerAddingConvention&lt;/span&gt; : IModelFinalizingConvention
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;virtual&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; ProcessModelFinalizing(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IConventionModelBuilder modelBuilder,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IConventionContext&amp;lt;IConventionModelBuilder&amp;gt; context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;foreach&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; entityType &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; modelBuilder.Metadata.GetEntityTypes())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; table = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (table != &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;amp;&amp;amp; entityType.GetDeclaredTriggers().All(t =&amp;gt; t.GetDatabaseName(table.Value) == &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; entityType.Builder.HasTrigger(table.Value.Name + &lt;span style="color:#e6db74"&gt;&amp;#34;_Trigger&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;foreach&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; fragment &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; entityType.GetMappingFragments(StoreObjectType.Table))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (entityType.GetDeclaredTriggers().All(t =&amp;gt; t.GetDatabaseName(fragment.StoreObject) == &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; entityType.Builder.HasTrigger(fragment.StoreObject.Name + &lt;span style="color:#e6db74"&gt;&amp;#34;_Trigger&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Adding the following to my DBContext allowed data to be saved even if triggers existed on my database.&lt;/p&gt;
&lt;p&gt;A longer term fix, should be to investigate if the triggers on the database are still needed and remove if not. From reading the docs, it sounds like a performance gain would be had after doing this!&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="anchor ms-1" href="#conclusion" aria-label="Permalink: Conclusion"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These are the issues I have encountered so far updating to .Net 7. A few more than for .Net 5 or 6. However so far I have managed to find a solution for all issues. I expect one more release candidate, with the final version of .Net 7 coming out in November.&lt;/p&gt;</description></item></channel></rss>