diff --git a/src/AutoMapper/ApiCompatBaseline.txt b/src/AutoMapper/ApiCompatBaseline.txt index cdebaea815..074c0e4a71 100644 --- a/src/AutoMapper/ApiCompatBaseline.txt +++ b/src/AutoMapper/ApiCompatBaseline.txt @@ -3,7 +3,11 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection> AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' is present in the contract but not in the implementation. MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyCollection> AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.Configuration.ICtorParamConfigurationExpression.ExplicitExpansion()' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IProjectionMemberConfiguration.ExplicitExpansion()' is present in the contract but not in the implementation. +MembersMustExist : Member 'public void AutoMapper.IProjectionMemberConfiguration.ExplicitExpansion()' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IProjectionMemberConfiguration.ExplicitExpansion(System.Boolean)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.Configuration.ICtorParamConfigurationExpression.ExplicitExpansion(System.Boolean)' is present in the implementation but not in the contract. +MembersMustExist : Member 'public void AutoMapper.Configuration.MemberConfigurationExpression.ExplicitExpansion()' does not exist in the implementation but it does exist in the contract. CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.IgnoreAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation. CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MapAtRuntimeAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation. CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MappingOrderAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation. @@ -13,4 +17,4 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueConverterAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation. CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueResolverAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation. TypeCannotChangeClassification : Type 'AutoMapper.Execution.TypeMapPlanBuilder' is a 'ref struct' in the implementation but is a 'struct' in the contract. -Total Issues: 14 +Total Issues: 18 diff --git a/src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs b/src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs index e59e6b3894..74175252fb 100644 --- a/src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs +++ b/src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs @@ -9,7 +9,8 @@ public interface ICtorParamConfigurationExpression /// /// Ignore this member for LINQ projections unless explicitly expanded during projection /// - void ExplicitExpansion(); + /// Is explicitExpansion active + void ExplicitExpansion(bool value = true); } public interface ICtorParamConfigurationExpression : ICtorParamConfigurationExpression { @@ -61,7 +62,7 @@ public void MapFrom(string sourceMembersPath) _ctorParamActions.Add(cpm => cpm.MapFrom(sourceMembersPath, sourceMembers)); } - public void ExplicitExpansion() => _ctorParamActions.Add(cpm => cpm.ExplicitExpansion = true); + public void ExplicitExpansion(bool value) => _ctorParamActions.Add(cpm => cpm.ExplicitExpansion = value); public void Configure(TypeMap typeMap) { diff --git a/src/AutoMapper/Configuration/IMemberConfigurationExpression.cs b/src/AutoMapper/Configuration/IMemberConfigurationExpression.cs index 5d52a81442..4f5f685249 100644 --- a/src/AutoMapper/Configuration/IMemberConfigurationExpression.cs +++ b/src/AutoMapper/Configuration/IMemberConfigurationExpression.cs @@ -290,7 +290,8 @@ public interface IProjectionMemberConfiguration /// /// Ignore this member for LINQ projections unless explicitly expanded during projection /// - void ExplicitExpansion(); + /// Is explicitExpansion active + void ExplicitExpansion(bool value = true); /// /// Apply a transformation function after any resolved destination member value with the given type /// diff --git a/src/AutoMapper/Configuration/MemberConfigurationExpression.cs b/src/AutoMapper/Configuration/MemberConfigurationExpression.cs index d3edb3d1b0..c3a23ef2c9 100644 --- a/src/AutoMapper/Configuration/MemberConfigurationExpression.cs +++ b/src/AutoMapper/Configuration/MemberConfigurationExpression.cs @@ -80,7 +80,7 @@ private void PreConditionCore(Expression pm.PreCondition = expr); public void AddTransform(Expression> transformer) => PropertyMapActions.Add(pm => pm.AddValueTransformation(new ValueTransformerConfiguration(pm.DestinationType, transformer))); - public void ExplicitExpansion() => PropertyMapActions.Add(pm => pm.ExplicitExpansion = true); + public void ExplicitExpansion(bool value) => PropertyMapActions.Add(pm => pm.ExplicitExpansion = value); public void Ignore() => Ignore(ignorePaths: true); public void Ignore(bool ignorePaths) { diff --git a/src/IntegrationTests/ExplicitExpansion/ConstructorExplicitExpansionOverride.cs b/src/IntegrationTests/ExplicitExpansion/ConstructorExplicitExpansionOverride.cs new file mode 100644 index 0000000000..355a78d92c --- /dev/null +++ b/src/IntegrationTests/ExplicitExpansion/ConstructorExplicitExpansionOverride.cs @@ -0,0 +1,38 @@ +namespace AutoMapper.IntegrationTests.ExplicitExpansion; + +public class ConstructorExplicitExpansionOverride : IntegrationTest { + public class Entity { + public int Id { get; set; } + public string Name { get; set; } + } + + public class SubEntity : Entity { + } + + record Dto(string Name); + record SubDto(string Name) : Dto(Name) { } + + public class Context : LocalDbContext { + public DbSet Entities { get; set; } + public DbSet SubEntities { get; set; } + } + public class DatabaseInitializer : DropCreateDatabaseAlways { + protected override void Seed(Context context) { + context.Entities.Add(new() { Name = "base" }); + context.SubEntities.Add(new() { Name = "derived" }); + base.Seed(context); + } + } + protected override MapperConfiguration CreateConfiguration() => new(c => { + c.CreateMap().ForCtorParam("Name", o => o.ExplicitExpansion()); + c.CreateMap().IncludeBase().ForCtorParam("Name", o => o.ExplicitExpansion(false)); + }); + [Fact] + public void Should_work() { + using var context = new Context(); + var dtos = ProjectTo(context.Entities).ToList(); + dtos.Count.ShouldBe(2); + dtos[0].ShouldBeOfType().Name.ShouldBeNull(); + dtos[1].ShouldBeOfType().Name.ShouldBe("derived"); + } +} \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/ExpandCollectionsOverride.cs b/src/IntegrationTests/ExplicitExpansion/ExpandCollectionsOverride.cs new file mode 100644 index 0000000000..eefbca2db3 --- /dev/null +++ b/src/IntegrationTests/ExplicitExpansion/ExpandCollectionsOverride.cs @@ -0,0 +1,110 @@ +namespace AutoMapper.IntegrationTests.ExplicitExpansion; + +public class ExpandCollectionsOverride : IntegrationTest +{ + TrainingCourseDto _course; + + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + cfg.CreateMap() + .ForMember(p => p.CourseName, opt => opt.ExplicitExpansion()); + cfg.CreateMap().ForMember(c => c.Category, o => o.ExplicitExpansion()); + cfg.CreateMap() + .IncludeBase() + .ForMember(c => c.CourseName, opt => opt.ExplicitExpansion(false)); + }); + + [Fact] + public void Should_notexpand_courseName() { + using (var context = new ClientContext()) { + _course = ProjectTo(context.TrainingCourses).FirstOrDefault(); + } + _course.CourseName.ShouldBeNull(); + } + + [Fact] + public void Should_expand_courseName() { + using (var context = new ClientContext()) { + _course = ProjectTo(context.TrainingCourses).FirstOrDefault(); + } + _course.CourseName.ShouldBe("Course 1"); + } + + public class DatabaseInitializer : DropCreateDatabaseAlways + { + protected override void Seed(ClientContext context) + { + var category = new Category { CategoryName = "Category 1" }; + var course = new TrainingCourse { CourseName = "Course 1" }; + context.TrainingCourses.Add(course); + var content = new TrainingContent { ContentName = "Content 1", Category = category }; + context.TrainingContents.Add(content); + course.Content.Add(content); + } + } + + public class ClientContext : LocalDbContext + { + public DbSet Categories { get; set; } + public DbSet TrainingCourses { get; set; } + public DbSet TrainingContents { get; set; } + } + + public class TrainingCourse + { + [Key] + public int CourseId { get; set; } + + public string CourseName { get; set; } + + public virtual IList Content { get; set; } = new List(); + } + + public class TrainingContent + { + [Key] + public int ContentId { get; set; } + + public string ContentName { get; set; } + public string CaptionName { get; set; } + + public Category Category { get; set; } + } + + public class Category + { + public int CategoryId { get; set; } + public string CategoryName { get; set; } + } + + + public class TrainingCourseDto + { + public int CourseId { get; set; } + + public string CourseName { get; set; } + + public virtual IList Content { get; set; } + } + + public class TrainingCourseDetailDto : TrainingCourseDto + { + } + + public class CategoryDto + { + public int CategoryId { get; set; } + public string CategoryName { get; set; } + } + + public class TrainingContentDto + { + public int ContentId { get; set; } + + public string ContentName { get; set; } + + public CategoryDto Category { get; set; } + } + +} \ No newline at end of file