Skip to content

Conversation

@asp2286
Copy link

@asp2286 asp2286 commented Jan 15, 2026

Fixes #639

Why

The main Mapster package currently only targets net8.0+, which prevents consumers on .NET Framework 4.8 (and other netstandard2.0-based targets) from upgrading.

What changed

  • Multi-target Mapster to netstandard2.0 (src/Mapster/Mapster.csproj).
  • Replace usages of newer BCL APIs that are not available on netstandard2.0 (LINQ ExceptBy / IntersectBy, range indexing).
  • Avoid compile-time dependency on RequiredMemberAttribute (string-based attribute check keeps behavior on newer TFMs while allowing netstandard2.0 compilation).
  • Add minimal shims for nullable flow-analysis attributes and IsExternalInit for netstandard2.0.
  • Add netstandard2.0-only references required for runtime code generation/dynamic binder (System.Reflection.Emit*, Microsoft.CSharp).

Validation

  • dotnet build src/Mapster/Mapster.csproj -c Release -f netstandard2.0
  • dotnet test src/Mapster.Tests/Mapster.Tests.csproj -c Release -f net8.0

@DocSvartz
Copy link
Contributor

Hi @asp2286. Have you tested this in environments where only .NET Framework 4.8 is available?
We tried this before #655, but some tests failed in .NET Framework 4.8.

@asp2286
Copy link
Author

asp2286 commented Jan 16, 2026

CI on ubuntu-latest fails trying to run net48 test targets because the runner image no longer has mono ("Could not find 'mono' host").

This PR now gates net48 in test projects behind '' == 'Windows_NT', so dotnet test ./src/Mapster.sln on Linux runs only net8/9/10 TFMs. (Commit: d2cfe27)

@asp2286
Copy link
Author

asp2286 commented Jan 16, 2026

Correction: the MSBuild property is $(OS) (previous comment lost it due to shell expansion).

Net48 test targets are now gated behind $(OS) == Windows_NT, so dotnet test ./src/Mapster.sln on ubuntu-latest runs only net8/9/10 TFMs.

@asp2286
Copy link
Author

asp2286 commented Jan 16, 2026

Hi @asp2286. Have you tested this in environments where only .NET Framework 4.8 is available? We tried this before #655, but some tests failed in .NET Framework 4.8.

Hi @DocSvartz.
Unit tests are green on both environments (screenshots attached):

Windows (x86): including .NET Framework 4.8 (net48) (also net8.0/net9.0/net10.0) — 1,489 tests (0 failed)
macOS (arm64): all solution tests — 1,122 tests (0 failed)
image
image

@DocSvartz
Copy link
Contributor

@asp2286 Ok 👍

LangVersion == lastest not recommended for use.
Did you replace it as a solution to some problem?

@asp2286
Copy link
Author

asp2286 commented Jan 16, 2026

@asp2286 Ok 👍

LangVersion == lastest not recommended for use. Did you replace it as a solution to some problem?

@DocSvartz
Good call — I had LangVersion=latest there mostly out of habit, not to address a specific issue in this PR. I’ve updated it to LangVersion=12 instead so it’s pinned and won’t change behavior with future SDK updates.

Fixes MapsterMapper#639

Includes packaging changes to ship a netstandard2.0 assembly again, plus test/CI updates to keep net48 tests running only on Windows.
@asp2286 asp2286 force-pushed the fix/netstandard2-0 branch from 1b59b75 to 15a8f22 Compare January 16, 2026 12:06
@DocSvartz
Copy link
Contributor

@asp2286 👍

@andrerav @stagep @DevTKSS What do you think about this?
This could be released as the next pre-release version with netstandard2.0 support (though without the Mapster Tool 😅) if you don't mind?

@DevTKSS
Copy link
Collaborator

DevTKSS commented Jan 16, 2026

@DocSvartz @asp2286 would prefer, if you would:

  1. use rather #if NET8_OR_GREATER Conditionals and the #elif with suitable other one(s) Identifyer for backwards compartiblity to netstandard2.0 and Framework

  2. Do not replace current net8.0+ code with lower version/tfm code then instead add the condition enclosed code needed for the netstandard2.0

    If you would merge it like now, you would maybe possibly improve it for the framework and standard users, but potentially downgrade user experience and feature/performance for users of latest stable and future .NET version which we should target to avoid!

  3. Do not combine LangVersion upgrade changes (upgrades especially) in the same PR as your primary feature/fix here(!!!) this is likely to introduce unexpected changes!

    I used to develop with an Assembly that where requiring NET Framework 4.0 compartiblity and there were users of it, which managed to create working apps up to .net Core 5/6 but higher targeting ones did mostly run into unexpected errors! So if you upgrade here the same time to the LangVersion of 12, this potentially will happen here too. That is one strong argument for even have the LangVersion usage removed completely and rather depend on the tfm automatic provided LangVersion! This will also help you with this PR, if this is would be done&merged before this PR, so you can clearly depend on it. You will get editor version selection in your IDE too and the compiler will be much more helpful then cause you and everyone else working with this code way less headaches when it comes to bugfixing!

@DocSvartz
Copy link
Contributor

@DevTKSS I might be wrong, but potential incompatibility can occur if the machine has a runtime that doesn't support a LangVersion higher than the one set during compilation?
Example:
We're building on a Net 10 machine using LangVersion 14 for TFM (8, 9, 10), so something might go wrong with the Net8 build run to machine have runtime 8.0 only (not 10 runtime).

@DevTKSS
Copy link
Collaborator

DevTKSS commented Jan 16, 2026

@DocSvartz you can use net8 with all lower but if the user is not having net8+ then instead maybe only framework, he will get errors when the project here is using a specific hogher lang version. Thats why I would not set it explicitly then instead rely on the net standard 2.0 to provide all it needs and only those things it is meant to be capable of!

@DocSvartz
Copy link
Contributor

@DevTKSS Yes, But then all the code would have to be downgrade to like netstandard2.0.

And only those parts that will give performance in the new TFMs can be rewritten using #if NET8_OR_GREATER

Otherwise, it will be a spaghetti monster of #if constructions 😅 (at least netstandard2.0 no nullable reference type)

@DevTKSS
Copy link
Collaborator

DevTKSS commented Jan 16, 2026

Yes, But then all the code would have to be downgrade to like netstandard2.0.

@DocSvartz 🤔 ehm... but this downgrading seems to be exactly what this PR suggests, "just" to have the netstandard2.0 and Framework compartibility:
copying from the OP above:

Replace usages of newer BCL APIs that are not available on netstandard2.0 (LINQ ExceptBy / IntersectBy, range indexing).
Avoid compile-time dependency on RequiredMemberAttribute (string-based attribute check keeps behavior on newer TFMs while allowing netstandard2.0 compilation).

and <LangVersion>12</LangVersion>:

https://github.com/MapsterMapper/Mapster/pull/849/changes#diff-1b7ae41fe8a27384133f2ff6073cefe0c4b28486b1f0434ccc8710f3a3c7d562R17

and the "Spagetti Monster" how you call the Conditionals is not as worse as it seems:

https://github.com/MapsterMapper/Mapster/pull/849/changes#diff-6e5285bab587cf35cab1768f75b5e387e5a1b6f26f194bae155ba0953d41da12L284-L301

and the "Spagetti Monster" how you call the Conditionals is not as worse as it seems. Of course, it adds some lines, but in my opinion this is simply the price of keeping legacy targets (even Microsoft does not actively bring out Framework features anymore) and let's assume realistically, this PR is about the Mapster Project, so when will the first one come "hey, you introduced this compartibility Layer for Framework, can you do the same for the other parts too?" then what? Do you want to downgrade the whole Mapster Solution? just asking 🤷

https://github.com/MapsterMapper/Mapster/pull/849/changes#diff-6e5285bab587cf35cab1768f75b5e387e5a1b6f26f194bae155ba0953d41da12L284-L301

But yes, I totally can understand them too! I was also annoyed of e.g. the cool Source Generators like CommunityToolkit.Mvvm was having has not been supported or relyable use-able with that Framework App I tryed to enhance with it!
That is why I don't say, "Dont introduce netstandard2.0 Multi Targeting" then instead only ask for seperating the both Upgrades (LangVersion - which I still see potentially problematic to use in this setup! - and the Multi Targeting itself) cleanly and use the Conditionals with then proper IDE supported LangVersion Feature Lintings so we even get warned from the compiler before problems occur

@DocSvartz
Copy link
Contributor

DocSvartz commented Jan 16, 2026

Project, so when will the first one come "hey, you introduced this compartibility Layer for Framework, can you do the same for the other parts too?" then what? Do you want to downgrade the whole Mapster Solution? just asking 🤷
That is why I don't say, "Dont introduce netstandard2.0 Multi Targeting"

@DevTKSS I'm not against multi-targeting either.
But multi-targeting leads to two approaches:

  1. The development of new features should take into account the capabilities of the minimum target ("effectively a "downgrade to the minimum target" );
  2. Backporting new features to the minimum targeting ("Spagetti Monster" and various APIs if the feature can't be transferred).

As far as I understand, you are suggesting to adhere to approach number 2?

Example

#if NET8_0_OR_GREATER 
#nullable enable     
#endif   
// other code

#if NET8_0_OR_GREATER 
        protected Expression? TryRestoreRecordMember(IMemberModelEx member, ClassModel? restorRecordModel, Expression? destination)
        {
#else
        protected Expression TryRestoreRecordMember(IMemberModelEx member, ClassModel restorRecordModel, Expression destination)
        {
#endif   
            if (restorRecordModel != null && destination != null)
            {
                var find = restorRecordModel.Members
                               .Where(x => x.Name == member.Name).FirstOrDefault();

                if (find != null)
                {
                    var compareNull = Expression.Equal(destination, Expression.Constant(null, destination.Type));
#if NET10_0_OR_GREATER
                    return Expression.Condition(compareNull, member.Type.CreateExprDefault, Expression.MakeMemberAccess(destination, (MemberInfo)find.Info));
#else
                    return Expression.Condition(compareNull, member.Type.CreateDefault(), Expression.MakeMemberAccess(destination, (MemberInfo)find.Info));
#endif

                }

            }

            return null;
        }


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants