Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define mappings with interfaces #163

Closed
blemasle opened this issue Oct 18, 2019 · 9 comments
Closed

Define mappings with interfaces #163

blemasle opened this issue Oct 18, 2019 · 9 comments
Assignees
Labels
bug in-preview A feature or bug fix exists in a preview release, and a full release will follow

Comments

@blemasle
Copy link

blemasle commented Oct 18, 2019

Hi,

According to the documentation, I assumed that mappings defined using interfaces would be applied to derived types. To my surprise, it did not.

Am i doing something wrong ? Is this a bug or is this by design ?
Note it is working if your using base classes instead of interfaces.

See this fiddle.

using AgileObjects.AgileMapper;
using System;
using System.Collections.Generic;

namespace MapperSandbox
{
    class Program
    {
        static void Main(string[] args)
        {
            Mapper
                .WhenMapping
                .From<IA>()
                .To<IB>()
                .Map<ImplementedA>()
                .To<ImplementedB>()
                .And
                .Map((src, dst) => src.Status).To(dst => dst.SomethingUsingStatus);

            var implementB = Mapper.Map(new ImplementedA { Status = 72 }).ToANew<ImplementedB>();
            Console.WriteLine($"Implemented : {implementB.SomethingUsingStatus}");
			
            Mapper
                .WhenMapping
                .From<ABase>()
                .To<BBase>()
                .Map<InheritedA>()
                .To<InheritedB>()
                .And
                .Map((src, dst) => src.Status).To(dst => dst.SomethingUsingStatus);
			
            var inheritedB = Mapper.Map(new InheritedA { Status = 72 }).ToANew<InheritedB>();
            Console.WriteLine($"Inherited : {inheritedB.SomethingUsingStatus}");
        }
    }

    interface IA
    {
        public int Status { get; set; }
    }

    interface IB
    {
        public int SomethingUsingStatus { get; set; }
    }

    class ImplementedA : IA
    {
        public int Status { get; set; }
    }

    class ImplementedB : IB
    {
        public int SomethingUsingStatus { get; set; }
    }
	
    class ABase
    {
        public int Status { get; set; }
    }
	
    class BBase
    {
        public int SomethingUsingStatus { get; set; }
    }
	
    class InheritedA : ABase { }
    class InheritedB : BBase { }
}

Regards

@blemasle blemasle changed the title Defining mappings with interface Define mappings with interface Oct 18, 2019
@blemasle blemasle changed the title Define mappings with interface Define mappings with interfaces Oct 18, 2019
@SteveWilkes SteveWilkes self-assigned this Oct 18, 2019
@SteveWilkes
Copy link
Member

Hi!

Yep, that's a bug. I'll fix it and get back to you. Thanks for the feedback!

Steve

@SteveWilkes
Copy link
Member

This is fixed as of v1.6-preview2, which is now on NuGet.

Thanks again!

Steve

@SteveWilkes SteveWilkes added the in-preview A feature or bug fix exists in a preview release, and a full release will follow label Oct 20, 2019
@blemasle
Copy link
Author

Perfect! This a great improvement, especially because you do not have to do this anymore

 Mapper
     .WhenMapping
     .From<IA>()
     .To<IB>()
     .Map<ImplementedA>()
     .To<ImplementedB>()

You can now define mappings based solely on interfaces, and they will be picked up by the execution plan.

 Mapper
    .WhenMapping
    .From<IA>()
    .To<IB>();

I think you should add this fact to the documentation once 1.6.0 is released as it provides very flexible and reusable ways of defining mappings.

Thanks for the blazing fast implementation/correction of this issue !

@SteveWilkes
Copy link
Member

Great point, thanks! I'll update the documentation when 1.6 is finished :)

Thanks again!

Steve

@blemasle
Copy link
Author

The changes you've made introduced a behaviour change but I'm not sure of the actual reason. For instance :

using Microsoft.AspNetCore.JsonPatch;

JsonPatchDocument<A> a = new JsonPatchDocument<A>();
var b = Mapper.Map(a).ToANew<JsonPatchDocument<B>>();

Works fine on preview1 but throws an exception on preview2
No coercion operator is defined between types 'Microsoft.AspNetCore.JsonPatch.JsonPatchDocument``1[MapperSandbox.B]' and 'Newtonsoft.Json.Serialization.DefaultContractResolver'.'

In this use case, there is no specific mapping defined and I'm only relying on implicit, name based mappings.

@SteveWilkes
Copy link
Member

Oh! Must be a hole in my tests somewhere - I'll check it out and report back.

Cheers!

Steve

@SteveWilkes
Copy link
Member

Hello again!

I've just uploaded v1.6 preview3 to NuGet, which behaves like preview 1 with respect to the JsonPatchDocument issue, and preview 2 with respect to type pairing using interfaces. Let me know how you get on!

All the best,

Steve

@blemasle
Copy link
Author

Hi,

Sorry for the delay. I can confirm that both my (slightly more complicated) use cases are working now.

Thank you!

@SteveWilkes
Copy link
Member

These fixes are included in v1.6, which is now available on NuGet.

Thanks again for the feedback!

SteveWilkes added a commit that referenced this issue Feb 16, 2020
* Updating to v1.6

* Fixing numeric to non-int-derived enum mapping

* v1.6-preview1 NuGet package

* Mapping non-mappable-element enumerables to empty collections

* Support for type-pairing using interfaces, re: #163

* v1.6-preview2

* Adding project icon

* Bugs/issue163 (#164)

* Adding .NET Core 3 test project

* Removing assembly scanning for interfaces / Handling interface -> implementation type pairing / Tidying

* Removing issue-specific test + updating .NET Core 3 package versions

* Updating icon, adding v1.6-preview3 package

* Tidying

* Tidying

* Tidying

* Splitting Root- and MemberDataSourceSetFactories

* Adding EmptyDataSourceSet / Filtering out unusable fallback data sources in DataSourceSet factory method instead of MemberPopulator

* Making EmptyDataSourceSet a singleton / Adding NullMemberPopulator

* Removing IEnumerable from IDataSourceSet

* Extending interface mapping test coverage

* Fixing .NET 3.5 source filters

* Features/simple type create instances using (#169)

* Failing unit test / Simplifying configured factory creation

* Tidying

* Support for configured object factories for simple (not primitive) types, re: #165

* Splitting out incorrect object factory configuration tests

* Removing unnecessary ignore from test

* Implementing simple type factories using ObjectFactories

* Adding lightweight IMemberMapperData implementation

* Creating element mapper data when using a configured simple type object factory for an enumerable element

* Support for simple type factory use in simple type enumerable mapping

* Support for conditional simple-type factories / Splitting simple-type factory tests into dedicated test class

* Support for conditional simple value factories with fallback to default conversion

* Support for simple type factory Funcs

* Support for nullable simple type factories

* Optimising simple type factory expression creation

* Test coverage for nullable simple type to simple type factory / Support for TimeSpan mapping (?!)

* Handling nested access checks in nullable-to-simple type factory use

* TimeSpan mapping test coverage

* Fixing test for .NET 3.5

* Adding package icon setting

* Bugs/issue166 (#170)

* Only populating MapperDatas in maptime-created ObjectMappingDatas if necessary + available, re: #166

* Renames for clarity

* Tidying

* Updating release notes

* Fixing translation of mapping plans with assignment of a local enum variable, re: #168 (#171)

Using GetVariableNameInCamelCase() for multi-invocation local variables

* Only creting a mapping LambdaExpression when necessary

* Organising mapping data source factory classes

* Tidying

* Updating to v1.6-preview4

* Lazy-loading ObjectMapperData ChidMapperData and DataSourcesByTargetMember

* Replacing Dictionary<,> with simple array-based alternative

* General tidying

* Ensuring root mapping plans include the mapper func parameter

* Removing capture creation in QualifiedMember pathfactories

* Using less derived parameter types

* Handling runtime-typed, simple-to-complex data sources configured using Map(s => s, t=> t), re: #174 (#177)

* Features/element index (#178)

* Renaming EnumerableIndex to ElementIndex

* Adding ElementKey through, adding failing test

* Support for ElementKey!

* Support for ElementKey with element-value-typed source Dictionaries

* Extra test coverage

* Updating to v1.6-preview5

* Type-Specific naming rules in the static API (#181)

* Fixing API / Adding ConfiguredNamingPattern / Adding type-specific naming tests

* Tidying

* Updating naming settings to be non-ruleset-specific

* Moving MapperContext into BasicMapperData

* Tidying

* Renaming BasicMapperData

* Setting QualifiedMemberContext on members

* Fixing tests

* Updating documentation

* Updating release notes

* Bugs/183 abstract member validation (#185)

* Support for applying custom data sources to base types only
* Improved detection of unmappable target types in mapping validation
* Tidying

* Extra test coverage

* Explicit support for DateTimeOffset mapping, re: #183

* Counting System.Drawing as a Base Class Library, re: #180

* Removing root source dynamic mapping tests from .NET Standard 1.0, re: #183

* Features/derived type mapping improvements (#186)

* Support for using MapTo() without specifying a derived source type, re: #172
* Test coverage for nested interface type pairing, re: #172

* Bugs/176 complex type data source method (#187)

* Adding failing tests re: using a custom method as a data source for a complex type

* Splitting NestedAccessCheck finding and Multi-invocation finding / Moving multi-invocation handling to MemberPopulator

* Making ExpressionInfoFinder static

* Moving multi-invocation handling back into DataSourceBase

* Applying multi-invocation replacements to DataSourceBase populations

* Handling assignment of chained multi-invocation variables / Optimising Expression replacement

* Handling null return values from custom object factories / Optimising Member Binding generation

* Registering static method complex type data sources as factoey methods

* Optimising for single multi-invocation

* Tidying

* Processing multi-invocations in DataSource finalisation

* Revert "Processing multi-invocations in DataSource finalisation"

This reverts commit 5312747.

* Avoiding multi-invocation processing of alternate population branches

* Processing multi-invocations in DataSource finalisation

* Skipping multi-invocation checks for composite data source value expressions
Optimising empty child mapper data collection access

* Fixing .NET 3.5 invocation comparison
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug in-preview A feature or bug fix exists in a preview release, and a full release will follow
Projects
None yet
Development

No branches or pull requests

2 participants