How to solve StackOverflowException after upgrading from AutoMapper 8 to 11.0.0 - automapper

https://github.com/beolafsen/TestAutomapperAndStripe
I get this errors after upgrade AutoMapper.Extensions.Microsoft.DependencyInjection from 8.1.1 to 11.0.0.
The messages I get are:
"An unhandled exception of type 'System.StackOverflowException' occurred in System.Private.CoreLib.dll."
(No other hint where or what causes the error).
and
I use .Net 6.0/BlazorServerSide
I have found that the following Automapper definitions are causing the error.
using Stripe;
CreateMap<VMStripeInvoiceItem, Stripe.InvoiceItem>();
CreateMap<Stripe.InvoiceItem, VMStripeInvoiceItem>()
.ForMember(pts => pts.LineAmount, opt => opt.MapFrom(ps => ps.Quantity * (ps.UnitAmountDecimal ?? (decimal)0) * (decimal)0.01))
.ForMember(pts => pts.UnitAmountDecimal, opt => opt.MapFrom(ps => (ps.UnitAmountDecimal ?? (decimal)0) * (decimal)0.01));
public class VMStripeInvoiceItem : InvoiceItem
{
public decimal LineAmount { get; set; }
}
How can this be solved?
Thanks for helping!
Best regards
BEO

Related

AutoMapper ResolutionContext does not contain a definition for engine anymore

After migration from an old version of AutoMapper (before 5) to version 9 there is one spot which causes headache. Old implementation:
.ForMember(a => a.Definition, o =>
{
o.Condition(s => s.TypeId == DocumentationType.Medication);
o.ResolveUsing((d, ctx) => ctx.Engine.Map<MedicationDefinitionContent>(d.Content.MedicationContentData));
})
which uses this extension method:
public static class MappingExtensions
{
public static void ResolveUsing<TType>(this IMemberConfigurationExpression<TType> expression, Func<TType, ResolutionContext, object> map)
{
expression.ResolveUsing(result => map((TType)result.Value, result.Context));
}
}
I fixed the first error that that IMemberConfigurationExpression needs 3 arguments, but then I learned that ResolutionContext does not contain a definition for engine anymore. I looked in the upgrade guide of version 5 and found that the ResolutionContext has been changed, but I do not understand how to fix this. The code seems to be pretty tricky. Can someone help, please?
#Lucian Bargaoanu
Ok, but the member "Definition" is the member wie map with MapFrom(s => s.Content.MedicationContentData). So different to the exception there is already a mapping. The member "Definition" is of type SerialisationHelper a helper class for Json stuff. It also has a mapping.
CreateMap<MedicationDefinitionContent, SerialisationHelper>()
.IgnoreAllUnmapped()
.AfterMap((s, t) => t.Write = s);
And MedicationDefinitionContent has a separate mapping.
CreateMap<MedicationContentData, MedicationDefinitionContent>()
MedicationDefinitionContent is annotated with [JsonObject(MemberSerialization.OptIn)]
so, a direct mapping from MedicationDefinitionContent to "Definition" does not work.
How you see I try to understand it, but maybe it needs more time.

Map or Ignore Indexer Property in Automapper 11

I'm having some issues mapping two classes using the AutoMapper in version 11. The destination class has an indexer-property, which causes the issue.
Since Automapper 11, the indexer property is no longer automatically ignored.
For testing purposes I used three classes:
public class Source {}
public class Target {
public float this[int key]
{
get
{
return 0;
}
set
{
}
}
}
public class MapperProfile: Profile
{
public MapperProfile()
{
CreateMap<Source, Target>();
}
}
During startup I'm calling mapper.ConfigurationProvider.AssertConfigurationIsValid() to validate the configuration. This fails with an unmapped Item property.
While it is possible to ignore all properties starting with Item using
this.AddGlobalIgnore("Item")
inside the Profile, I'd rather not use such a general way to ignore it, especially since the first parameter is labeled propertyNameStartingWith - this would suggest to me, that other properties such as ItemWithSuffix might be ignored as well.
Another strategy I tried to employ is to use an explicit ignore on a property. Using the expression notation fails, due to compiler errors:
CreateMap<Source, Target>()
.ForMember(dest => dest[], opt => opt.Ignore())
.ReverseMap();
Adding an arbitrary index to the expression fails with another error, so that does not seem to be a viable solution as well:
CreateMap<Source, Target>()
.ForMember(dest => dest[0], opt => opt.Ignore())
.ReverseMap();
In this case the error notes, that we may not map to child property.
When using the member name syntax, there are some different errors.
CreateMap<Source, Target>()
.ForMember("Item", opt => opt.Ignore())
.ReverseMap();
In this case it fails with the following message:
Incorrect number of arguments supplied for call to method 'Double get_Item(Int32)' (Parameter 'property')
Using [] or Item[] fails with a missing property notification.
The last strategy I employed was using the ForAllMembers call. This succeeds, however, I'm wondering if there is a better solution to handle this logic which allows using a specific mapping logic for a single member.
CreateMap<Source, Target>()
.ForAllMembers(x =>
{
if (x.DestinationMember.Name == "Item")
{
x.Ignore();
}
});

Entity Framework Core 5 tries to insert value for computed column

Using Entity Framework 5 and EF Core Power Tools (whith the setting to generate ef 5 code enabled) we get something like the following code generated for each computed column:
...HasComputedColumnSql("([LastName]+isnull(' '+[FirstName],''))", true);
which is perfectly right.
Unfortunately, trying to create a new entity that has computed columns and trying to save we get the following exception from ef core 5:
The column '...' cannot be modified because it is either a computed column or is the result of a UNION operator
When manualy appending the ValueGeneratedOnAddOrUpdate after the HasComputedColumnSql like so:
...HasComputedColumnSql("([LastName]+isnull(' '+[FirstName],''))", true).ValueGeneratedOnAddOrUpdate();
everything works fine.
According to the docs this should be automatically be called by HasComputedColumnSql.
What can be done to overcome this issue ?
We are using Microsoft.EntityFrameworkCore v5.0.5 package and also we are using an Azure Managed Sql Server Instance
The OP already pointed out the solution: in EF core 5, when scaffolding is used, you have to manually add ValueGeneratedOnAddOrUpdate(). This problem has been fixed in EF core 6.
The patch has been mentioned in earlier answers.
This answer elaborates on how to remove manual code from generated code.
Let's say the scaffolded file GenDbContext.cs contains:
modelBuilder.Entity<MyEntity>(entity =>
{
entity.Property(e => e.ComputedName).HasMaxLength(100).IsUnicode(false)
.HasComputedColumnSql("([LastName]+isnull(' '+[FirstName],''))", true);
});
There are two options: partial class or inheritance.
A) Partial class in GenDbContext.Extension.cs - requires EF core 3.1+
public partial class GenDbContext : DbContext
{
partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>(entity =>
entity.Property(e => e.ComputedName).ValueGeneratedOnAddOrUpdate()
);
}
}
B) Subclass in InheritedDbContext.cs.
public class InheritedDbContext : GenDbContext
{
public InheritedDbContext(DbContextOptions<GenDbContext> options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyEntity>(entity =>
entity.Property(e => e.ComputedName).ValueGeneratedOnAddOrUpdate()
);
}
}
Then in Startup.cs:
// The first line is still necessary as it declares DbContextOptions<GenDbContext>.
services.AddDbContext<GenDbContext>(...options...);
services.AddDbContext<InheritedDbContext>(...same options...);
There is no need to repeat the rest of the column settings such as HasMaxLength, IsUnicode and HasComputedColumnSql.

Error about deprecated Puppet MySQL keeps showing up

This is my manifest:
class capstone() {
include apache
include mysql
class {'apache::vhost':
port => 80,
docroot => '/var/www/wordpress',
}
include 'apache::mod::php'
class {'mysql::server' :
root_password => 'foo',
override_options => {
'mysqld' => { 'max_connections' => '1024' },
}
}
class {'mysql::bindings' :
php_enable => true
}
}
I wrote this in modules/capstone/manifests/init.pp
Inside modules, I have stdlib, apache, concat, capstone, mysql, wordpress which are all downloaded except capstone.
My error is:
Error: ERROR: This class has been deprecated and the functionality moved
into mysql::server. If you run mysql::server without correctly calling
mysql:: server with the new override_options hash syntax you will revert
your MySQL to the stock settings. Do not proceed without removing this
class and using mysql::server correctly.
If you are brave you may set attempt_compatibility_mode in this class which
attempts to automap the previous settings to appropriate calls to
mysql::server at /root/radiant/modules/mysql/manifests/init.pp:89 on node kim.puppetlabs.vm
Error: ERROR: This class has been deprecated and the functionality moved
into mysql::server. If you run mysql::server without correctly calling
mysql:: server with the new override_options hash syntax you will revert
your MySQL to the stock settings. Do not proceed without removing this
class and using mysql::server correctly.
If you are brave you may set attempt_compatibility_mode in this class w
I have googled around and have followed the suggestions in other links but I still get the same error. Not sure where I have done wrong.
Please advise.
Two mistakes:
1) do not include mysql
2) did not state the vhosts name correctly
This is the working manifest:
class capstone() {
include apache
include apache::mod::php
apache::vhost { 'wordpress.example.com':
port => 80,
docroot => '/var/www/wordpress',
}
class {'mysql::server' :
root_password => 'foo',
override_options => {
'mysqld' => { 'max_connections' => '1024' },
}
}
class {'mysql::bindings' :
php_enable => true
}
}

Access MemberName/PropertyMap from ResolutionContext in an Automapper custom ValueResolver

I need to trace any complex (i.e. non-default) mappings in our project.
To achieve this, I'm using a custom value resolver, and publishing out a log event during resolution. As part of this message I'd like to know the destination member being mapped, which I was hoping to find in source.Context.MemberName - but this is always null.
ValueResolver:
public class Resolver : IValueResolver
{
public event MappingEventHandler MappingEvent;
public delegate void MappingEventHandler(MappingMessage m);
public ResolutionResult Resolve(ResolutionResult source)
{
var src = (SourceDTO)source.Context.SourceValue;
if (!String.IsNullOrWhiteSpace(src.Status) && src.Status == "Alert")
{
var newValue = source.Value + " - Fail";
var fieldName = source.Context.MemberName; //Always null
MappingEvent(new MappingMessage(fieldName , newValue));
return source.New(value, typeof(String));
}
return source;
}
}
... and its usage:
Resolver resolver = new Resolver();
//... subscribe to resolver events etc.
Mapper.CreateMap<SourceDTO, Common>()
.ForMember(dest => dest.ReferenceIdentifier
, opt => opt.ResolveUsing<Resolver>()
.FromMember(src => src.Reference)
.ConstructedBy(() => resolver)
I can see in the Automapper code that MemberName only returns if the PropertyMap is non-null, and since PropertyMap is null in this case, I'm not getting my MemberName back.
Is there a reason the PropertyMap isn't being defined in this here? There's a relevant candidate via source.Context.TypeMap.GetPropertyMaps(), but it's not being pushed into this context.
Any ideas? Perhaps there's a means of pulling the right PropertyMap out of the Context.TypeMap set?
Tried with the more recent Automapper build - looks like the problem has been resolved.
Version with issue: 2.1.266
Working version: 2.2.1
Also found it's a lot easier to use the following syntax to resolve from an existing instance:
Resolver resolver = new Resolver();
//... subscribe to resolver events etc.
Mapper.CreateMap<SourceDTO, Common>()
.ForMember(dest => dest.ReferenceIdentifier
, opt => opt.ResolveUsing(resolver)
.FromMember(src => src.Reference) )

Resources