From 1f93ba9328c7732262e55d87d455bbb10d55fbcd Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 9 Mar 2017 17:10:45 +0700 Subject: [PATCH 001/363] Reference --- Reference.md | 499 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 Reference.md diff --git a/Reference.md b/Reference.md new file mode 100644 index 00000000..c866c4fb --- /dev/null +++ b/Reference.md @@ -0,0 +1,499 @@ +###Reference +[Mapping](#Mapping) +- [Mapping to a new object](#MappingNew) +- [Mapping to an existing object](#MappingToTarget) +- [Queryable Extensions](#Projection) +- [Mapper instance](#UnitTest) + +[Conversion](#Conversion) +- [Conversion of immutable types](#ConversionImmutable) +- [Conversion from/to enum](#ConversionEnum) +- [Mapping POCO](#ConversionPOCO) +- [Mapping Lists](#ConversionList) +- [Conversion from/to Dictionary](#ConversionDictionary) +- [Conversion from/to Record Types](#ConversionRecordType) + +[Settings](#Settings) +- [Settings per type](#SettingsPerType) +- [Global Settings](#SettingsGlobal) +- [Settings inheritance](#SettingsInheritance) +- [Rule based settings](#SettingsRuleBased) +- [Setting instance](#SettingsOverload) +- [Assembly scanning](#AssemblyScanning) + +[Basic Customization](#Basic) +- [Ignore properties & attributes](#Ignore) +- [Custom Property Mapping](#Map) +- [Flexible Name Mapping](#NameMatchingStrategy) +- [Merge Objects](#Merge) +- [Shallow Copy](#ShallowCopy) +- [Preserve reference (preventing circular reference stackoverflow)](#PreserveReference) + +[Advance Customization](#Advance) +- [Custom instance creation](#ConstructUsing) +- [After mapping action](#AfterMapping) +- [Passing runtime value](#RuntimeValue) +- [Type-Specific Destination Transforms](#Transform) +- [Custom Type Resolvers](#ConverterFactory) + +[Validation](#Validate) +- [Explicit Mapping](#ExplicitMapping) +- [Checking Destination Member](#CheckDestinationMember) +- [Validating Mappings](#Compile) + +####Mapping +#####Mapping to a new object +Mapster creates the destination object and maps values to it. + + var destObject = TypeAdapter.Adapt(sourceObject); + +or just + + var destObject = TypeAdapter.Adapt(sourceObject); + +or using extension methods + + var destObject = sourceObject.Adapt(); + +#####Mapping to an existing object +You make the object, Mapster maps to the object. + + TDestination destObject = new TDestination(); + destObject = TypeAdapter.Adapt(sourceObject, destObject); + +or using extension methods + + TDestination destObject = new TDestination(); + destObject = sourceObject.Adapt(destObject); + +#####Queryable Extensions +Mapster also provides extensions to map queryables. + + using(MyDbContext context = new MyDbContext()) + { + // Build a Select Expression from DTO + var destinations = context.Sources.ProjectToType().ToList(); + + // Versus creating by hand: + var destinations = context.Sources.Select(c => new Destination(){ + Id = p.Id, + Name = p.Name, + Surname = p.Surname, + .... + }) + .ToList(); + } + +#####Mapper Instance +In some cases, you need an instance of a mapper (or a factory function) to pass into a DI container. Mapster has +the IAdapter and Adapter to fill this need: + + IAdapter adapter = new Adapter(); + +And usage is the same as with the static methods. + + var result = adapter.Adapt(source); + +####Conversion +Mapster can map nearly all kind of objects. Here are some details. + +#####Conversion of immutable types +Converting between primitive types (ie. int, string, bool, double, decimal) is supported, including when those types are nullable. For all other types, if you can cast types in c#, you can also cast in Mapster. + + var i = TypeAdapter.Adapt("123"); //123 + +#####Conversion from/to enum +Mapster maps enums to numerics automatically, but it also maps strings to and from enums automatically in a fast manner. +The default Enum.ToString() in .Net is quite slow. The implementation in Mapster is double the speed. +Likewise, a fast conversion from strings to enums is also included. If the string is null or empty, +the enum will initialize to the first enum value. + +In Mapster 2.0, flagged enums are also supported. + + var e = TypeAdapter.Adapt("Read, Write, Delete"); + //FileShare.Read | FileShare.Write | FileShare.Delete + +#####Mapping POCO +Mapster can map 2 different POCO types using the following rules +- Source and destination property names are the same. Ex: `dest.Name = src.Name` +- Source has get method. Ex: `dest.Name = src.GetName()` +- Source property has child object which can flatten to destination. Ex: `dest.ContactName = src.Contact.Name` or `dest.Contact_Name = src.Contact.Name` + +In Mapster 2.0, POCO structs are also supported. + + class Staff { + public string Name { get; set; } + public int GetAge() { return (DateTime.Now - this.BirthDate).TotalDays / 365.25; } + public Staff Supervisor { get; set; } + ... + } + + struct StaffDto { + public string Name { get; set; } + public int Age { get; set; } + public string SupervisorName { get; set; } + } + + var dto = TypeAdapter.Adapt(staff); + //dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name + +#####Mapping Lists +This includes mapping among lists, arrays, collections, dictionary including various interfaces: IList, ICollection, IEnumerable etc... + + var target = TypeAdapter.Adapt, IEnumerable>(list); + +#####Conversion from/to Dictionary +Mapster supports conversion from object to dictionary and dictionary to object. + +``` +var point = new { X = 2, Y = 3 }; +var dict = src.Adapt>(); +dict["Y"].ShouldBe(3); +``` + +#####Conversion from/to Record Types +Record types are types with no setter, all parameters will be initiated from constructor. + +``` +class Person { + public string Name { get; } + public int Age { get; } + + public Person(string name, int age) { + this.Name = name; + this.Age = age; + } +} + +var src = new { Name = "Mapster", Age = 3 }; +var target = src.Adapt(); +``` + +There is limitation on record type mapping. Record type must not have setter and have only one non-empty constructor. And all parameter names must match with properties. + +####Settings +#####Settings per type +You can easily create settings for a type mapping by using: `TypeAdapterConfig.NewConfig()`. +When `NewConfig` is called, any previous configuration for this particular TSource => TDestination mapping is dropped. + + TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.Age) + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + +As an alternative to `NewConfig`, you can use `ForType` in the same way: + + TypeAdapterConfig + .ForType() + .Ignore(dest => dest.Age) + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + +`ForType` differs in that it will create a new mapping if one doesn't exist, but if the specified TSource => TDestination +mapping does already exist, it will enhance the existing mapping instead of dropping and replacing it. + +#####Global Settings +Use global settings to apply policies to all mappings. + + TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); + +Then for individual type mappings, you can easily override the global setting(s). + + TypeAdapterConfig.NewConfig().PreserveReference(false); + +#####Settings inheritance +Type mappings will automatically inherit for source types. Ie. if you set up following config. + + TypeAdapterConfig.NewConfig() + .Map(dest => dest.Name, src => src.Name + "_Suffix"); + +A derived type of `SimplePoco` will automatically apply the base mapping config. + + var dest = TypeAdapter.Adapt(src); //dest.Name = src.Name + "_Suffix" + +If you don't wish a derived type to use the base mapping, just define `NoInherit` for that type. + + TypeAdapterConfig.NewConfig().NoInherit(true); + + //or at the global level + TypeAdapterConfig.GlobalSettings.Default.NoInherit(true); + +And by default, Mapster will not inherit destination type mappings. You can turn on by `AllowImplicitDestinationInheritance`. + + TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; + +Finally, Mapster also provides methods to inherit explicitly. + + TypeAdapterConfig.NewConfig() + .Inherits(); + +#####Rule based settings +To set the setting at a more granular level. You can use the `When` method in global settings. +In the example below, when any source type and destination type are the same, we will not the copy the `Id` property. + + TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType) + .Ignore("Id"); + +In this example, the config would only apply to Query Expressions (projections). + + TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection) + .IgnoreAttribute(typeof(NotMapAttribute)); + +#####Setting instance +You may wish to have different settings in different scenarios. +If you would not like to apply setting at a static level, Mapster also provides setting instance configurations. + + var config = new TypeAdapterConfig(); + config.Default.Ignore("Id"); + +For instance configurations, you can use the same `NewConfig` and `ForType` methods that are used at the global level with +the same behavior: `NewConfig` drops any existing configuration and `ForType` creates or enhances a configuration. + + config.NewConfig() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + + config.ForType() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + +You can apply a specific config instance by passing it to the `Adapt` method. (NOTE: please reuse your config instance to prevent recompilation) + + var result = TypeAdapter.Adapt(src, config); + +Or to an Adapter instance. + + var adapter = new Adapter(config); + var result = adapter.Adapt(src); + +If you would like to create configuration instance from existing configuration, you can use `Clone` method. For example, if you would like to clone from global setting. + + var newConfig = TypeAdapterConfig.GlobalSettings.Clone(); + +Or clone from existing configuration instance + + var newConfig = oldConfig.Clone(); + +#####Assembly scanning +It's relatively common to have mapping configurations spread across a number of different assemblies. +Perhaps your domain assembly has some rules to map to domain objects and your web api has some specific rules to map to your +api contracts. In these cases, it can be helpful to allow assemblies to be scanned for these rules so you have some basic +method of organizing your rules and not forgetting to have the registration code called. In some cases, it may even be necessary to +register the assemblies in a particular order, so that some rules override others. Assembly scanning helps with this. +Assembly scanning is simple, just create any number of IRegister implementations in your assembly, then call `Scan` from your TypeAdapterConfig class: + + public class MyRegister : IRegister + { + public void Register(TypeAdapterConfig config){ + config.NewConfig(); + + //OR to create or enhance an existing configuration + + config.ForType(); + } + } + +To scan and register at the Global level: + + TypeAdapterConfig.GlobalSettings.Scan(assembly1, assembly2, assemblyN) + +For a specific config instance: + + var config = new TypeAdapterConfig(); + config.Scan(assembly1, assembly2, assemblyN); + +If you use other assembly scanning library such as MEF, you can easily apply registration with `Apply` method. + + var registers = container.GetExports(); + config.Apply(registers); + +####Basic Customization +When the default convention mappings aren't enough to do the job, you can specify complex source mappings. + +#####Ignore Members & Attributes +Mapster will automatically map properties with the same names. You can ignore members by using the `Ignore` method. + + TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.Id); + +You can ignore members conditionally, with condition based on source or target. When the condition is met, mapping of the property +will be skipped altogether. This is the difference from custom `Map` with condition, where destination is set to `null` +when condition is met. + + TypeAdapterConfig + .NewConfig() + .IgnoreIf((src, dest) => !string.IsNullOrEmpty(dest.Name), dest => dest.Name); + +You can ignore members annotated with specific attributes by using the `IgnoreAttribute` method. + + TypeAdapterConfig + .NewConfig() + .IgnoreAttribute(typeof(JsonIgnoreAttribute)); + +#####Custom property mapping +You can customize how Mapster maps values to a property. + + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + +The Map configuration can accept a third parameter that provides a condition based on the source. +If the condition is not met, Mapster will retry with next conditions. Default condition should be added at the end without specifying condition. If you do not specify default condition, null or default value will be assigned. + + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.FullName, src => "Sig. " + src.FullName, srcCond => srcCond.Country == "Italy") + .Map(dest => dest.FullName, src => "Sr. " + src.FullName, srcCond => srcCond.Country == "Spain") + .Map(dest => dest.FullName, src => "Mr. " + src.FullName); + +In Mapster 2.0, you can even map when source and destination property types are different. + + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.Gender, //Genders.Male or Genders.Female + src => src.GenderString); //"Male" or "Female" + +#####Flexible name mapping +By default, Mapster will map property with case sensitive name. You can adjust to flexible name mapping by setting `NameMatchingStrategy.Flexible` to `NameMatchingStrategy` method. This setting will allow matching between `PascalCase`, `camelCase`, `lower_case`, and `UPPER_CASE`. + +This setting will apply flexible naming globally. + +``` +TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.Flexible); +``` + +or by specific type mapping. + +``` +TypeAdapterConfig.NewConfig().NameMatchingStrategy(NameMatchingStrategy.Flexible); +``` + +#####Merge object +By default, Mapster will map all properties, even source properties containing null values. +You can copy only properties that have values by using `IgnoreNullValues` method. + + TypeAdapterConfig + .NewConfig() + .IgnoreNullValues(true); + +#####Shallow copy +By default, Mapster will recursively map nested objects. You can do shallow copying by setting `ShallowCopyForSameType` to `true`. + + TypeAdapterConfig + .NewConfig() + .ShallowCopyForSameType(true); + +#####Preserve reference (preventing circular reference stackoverflow) +When mapping objects with circular references, a stackoverflow exception will result. +This is because Mapster will get stuck in a loop tring to recursively map the circular reference. +If you would like to map circular references or preserve references (such as 2 properties pointing to the same object), you can do it by setting `PreserveReference` to `true` + + TypeAdapterConfig + .NewConfig() + .PreserveReference(true); + +NOTE: Projection doesn't support circular reference. To overcome, you might use `Adapt` instead of `ProjectToType`. + + TypeAdaptConfig.GlobalSettings.Default.PreserveReference(true); + var students = context.Student.Include(p => p.Schools).Adapt>(); + +####Advance Customization +#####Custom Destination Object Creation +You can provide a function call to create your destination objects instead of using the default object creation +(which expects an empty constructor). To do so, use the `ConstructUsing` method when configuring. This method expects +a function that will provide the destination instance. You can call your own constructor, a factory method, +or anything else that provides an object of the expected type. + + //Example using a non-default constructor + TypeAdapterConfig.NewConfig() + .ConstructUsing(src => new TDestination(src.Id, src.Name)); + + //Example using an object initializer + TypeAdapterConfig.NewConfig() + .ConstructUsing(src => new TDestination{Unmapped = "unmapped"}); + +#####After mapping action +You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. + +``` +TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); +``` + +Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. + +``` +TypeAdapterConfig.GlobalSettings.ForDestinationType() + .AfterMapping(dest => dest.Validate()); +``` + +#####Passing runtime value +In some cases, you might would like to pass runtime values (ie, current user). On configuration, we can receive run-time value by `MapContext.Current.Parameters`. + +``` +TypeAdapterConfig.NewConfig() + .Map(dest => dest.CreatedBy, + src => MapContext.Current.Parameters["user"]); +``` + +To pass run-time value, we need to use `BuildAdapter` method, and call `AddParameters` method to add each parameter. + +``` +var dto = poco.BuildAdapter() + .AddParameters("user", this.User.Identity.Name) + .AdaptToType(); +``` + +#####Type-Specific Destination Transforms +This allows transforms for all items of a type, such as trimming all strings. But really any operation +can be performed on the destination value before assignment. + + //Global + TypeAdapterConfig.GlobalSettings.Default.AddDestinationTransforms((string x) => x.Trim()); + + //Per mapping configuration + TypeAdapterConfig.NewConfig() + .AddDestinationTransforms((string x) => x.Trim()); + +#####Custom Type Resolvers +In some cases, you may want to have complete control over how an object is mapped. You can register specific transformations using the `MapWith` method. + + //Example of transforming string to char[]. + TypeAdapterConfig.NewConfig() + .MapWith(str => str.ToCharArray()); + +`MapWith` also useful if you would like to copy instance rather than deep copy the object, for instance, `JObject` or `DbGeography`, these should treat as primitive types rather than POCO. + + TypeAdapterConfig.NewConfig() + .MapWith(json => json); + +####Validation +To validate your mapping in unit tests and in order to help with "Fail Fast" situations, the following strict mapping modes have been added. + +#####Explicit Mapping +Forcing all classes to be explicitly mapped: + + //Default is "false" + TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; + //This means you have to have an explicit configuration for each class, even if it's just: + TypeAdapterConfig.NewConfig(); + +#####Checking Destination Member +Forcing all destination properties to have a corresponding source member or explicit mapping/ignore: + + //Default is "false" + TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + +#####Validating Mappings +Both a specific TypeAdapterConfig or all current configurations can be validated. In addition, if Explicit Mappings (above) are enabled, it will also include errors for classes that are not registered at all with the mapper. + + //Validate a specific config + var config = TypeAdapterConfig.NewConfig(); + config.Compile(); + + //Validate globally + TypeAdapterConfig.NewConfig(); + TypeAdapterConfig.NewConfig(); + TypeAdapterConfig.GlobalSettings.Compile(); From 085ad7a1a1bee58318faa9282843f626248e2395 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 9 Mar 2017 17:10:46 +0700 Subject: [PATCH 002/363] Initial Home page --- Home.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 Home.md diff --git a/Home.md b/Home.md new file mode 100644 index 00000000..778ea539 --- /dev/null +++ b/Home.md @@ -0,0 +1 @@ +Welcome to the Mapster wiki! From 40f590aa40ebe230c0db5bb5683cd9ca1335f903 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 9 Mar 2017 17:12:08 +0700 Subject: [PATCH 003/363] Updated Reference (markdown) --- Reference.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Reference.md b/Reference.md index c866c4fb..2092f734 100644 --- a/Reference.md +++ b/Reference.md @@ -1,4 +1,3 @@ -###Reference [Mapping](#Mapping) - [Mapping to a new object](#MappingNew) - [Mapping to an existing object](#MappingToTarget) From 07de6d322b7ed0b319e7945f749006c4bcb67bd4 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Wed, 24 May 2017 14:45:28 +0700 Subject: [PATCH 004/363] Destroyed Reference (markdown) --- Reference.md | 498 --------------------------------------------------- 1 file changed, 498 deletions(-) delete mode 100644 Reference.md diff --git a/Reference.md b/Reference.md deleted file mode 100644 index 2092f734..00000000 --- a/Reference.md +++ /dev/null @@ -1,498 +0,0 @@ -[Mapping](#Mapping) -- [Mapping to a new object](#MappingNew) -- [Mapping to an existing object](#MappingToTarget) -- [Queryable Extensions](#Projection) -- [Mapper instance](#UnitTest) - -[Conversion](#Conversion) -- [Conversion of immutable types](#ConversionImmutable) -- [Conversion from/to enum](#ConversionEnum) -- [Mapping POCO](#ConversionPOCO) -- [Mapping Lists](#ConversionList) -- [Conversion from/to Dictionary](#ConversionDictionary) -- [Conversion from/to Record Types](#ConversionRecordType) - -[Settings](#Settings) -- [Settings per type](#SettingsPerType) -- [Global Settings](#SettingsGlobal) -- [Settings inheritance](#SettingsInheritance) -- [Rule based settings](#SettingsRuleBased) -- [Setting instance](#SettingsOverload) -- [Assembly scanning](#AssemblyScanning) - -[Basic Customization](#Basic) -- [Ignore properties & attributes](#Ignore) -- [Custom Property Mapping](#Map) -- [Flexible Name Mapping](#NameMatchingStrategy) -- [Merge Objects](#Merge) -- [Shallow Copy](#ShallowCopy) -- [Preserve reference (preventing circular reference stackoverflow)](#PreserveReference) - -[Advance Customization](#Advance) -- [Custom instance creation](#ConstructUsing) -- [After mapping action](#AfterMapping) -- [Passing runtime value](#RuntimeValue) -- [Type-Specific Destination Transforms](#Transform) -- [Custom Type Resolvers](#ConverterFactory) - -[Validation](#Validate) -- [Explicit Mapping](#ExplicitMapping) -- [Checking Destination Member](#CheckDestinationMember) -- [Validating Mappings](#Compile) - -####Mapping -#####Mapping to a new object -Mapster creates the destination object and maps values to it. - - var destObject = TypeAdapter.Adapt(sourceObject); - -or just - - var destObject = TypeAdapter.Adapt(sourceObject); - -or using extension methods - - var destObject = sourceObject.Adapt(); - -#####Mapping to an existing object -You make the object, Mapster maps to the object. - - TDestination destObject = new TDestination(); - destObject = TypeAdapter.Adapt(sourceObject, destObject); - -or using extension methods - - TDestination destObject = new TDestination(); - destObject = sourceObject.Adapt(destObject); - -#####Queryable Extensions -Mapster also provides extensions to map queryables. - - using(MyDbContext context = new MyDbContext()) - { - // Build a Select Expression from DTO - var destinations = context.Sources.ProjectToType().ToList(); - - // Versus creating by hand: - var destinations = context.Sources.Select(c => new Destination(){ - Id = p.Id, - Name = p.Name, - Surname = p.Surname, - .... - }) - .ToList(); - } - -#####Mapper Instance -In some cases, you need an instance of a mapper (or a factory function) to pass into a DI container. Mapster has -the IAdapter and Adapter to fill this need: - - IAdapter adapter = new Adapter(); - -And usage is the same as with the static methods. - - var result = adapter.Adapt(source); - -####Conversion -Mapster can map nearly all kind of objects. Here are some details. - -#####Conversion of immutable types -Converting between primitive types (ie. int, string, bool, double, decimal) is supported, including when those types are nullable. For all other types, if you can cast types in c#, you can also cast in Mapster. - - var i = TypeAdapter.Adapt("123"); //123 - -#####Conversion from/to enum -Mapster maps enums to numerics automatically, but it also maps strings to and from enums automatically in a fast manner. -The default Enum.ToString() in .Net is quite slow. The implementation in Mapster is double the speed. -Likewise, a fast conversion from strings to enums is also included. If the string is null or empty, -the enum will initialize to the first enum value. - -In Mapster 2.0, flagged enums are also supported. - - var e = TypeAdapter.Adapt("Read, Write, Delete"); - //FileShare.Read | FileShare.Write | FileShare.Delete - -#####Mapping POCO -Mapster can map 2 different POCO types using the following rules -- Source and destination property names are the same. Ex: `dest.Name = src.Name` -- Source has get method. Ex: `dest.Name = src.GetName()` -- Source property has child object which can flatten to destination. Ex: `dest.ContactName = src.Contact.Name` or `dest.Contact_Name = src.Contact.Name` - -In Mapster 2.0, POCO structs are also supported. - - class Staff { - public string Name { get; set; } - public int GetAge() { return (DateTime.Now - this.BirthDate).TotalDays / 365.25; } - public Staff Supervisor { get; set; } - ... - } - - struct StaffDto { - public string Name { get; set; } - public int Age { get; set; } - public string SupervisorName { get; set; } - } - - var dto = TypeAdapter.Adapt(staff); - //dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name - -#####Mapping Lists -This includes mapping among lists, arrays, collections, dictionary including various interfaces: IList, ICollection, IEnumerable etc... - - var target = TypeAdapter.Adapt, IEnumerable>(list); - -#####Conversion from/to Dictionary -Mapster supports conversion from object to dictionary and dictionary to object. - -``` -var point = new { X = 2, Y = 3 }; -var dict = src.Adapt>(); -dict["Y"].ShouldBe(3); -``` - -#####Conversion from/to Record Types -Record types are types with no setter, all parameters will be initiated from constructor. - -``` -class Person { - public string Name { get; } - public int Age { get; } - - public Person(string name, int age) { - this.Name = name; - this.Age = age; - } -} - -var src = new { Name = "Mapster", Age = 3 }; -var target = src.Adapt(); -``` - -There is limitation on record type mapping. Record type must not have setter and have only one non-empty constructor. And all parameter names must match with properties. - -####Settings -#####Settings per type -You can easily create settings for a type mapping by using: `TypeAdapterConfig.NewConfig()`. -When `NewConfig` is called, any previous configuration for this particular TSource => TDestination mapping is dropped. - - TypeAdapterConfig - .NewConfig() - .Ignore(dest => dest.Age) - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); - -As an alternative to `NewConfig`, you can use `ForType` in the same way: - - TypeAdapterConfig - .ForType() - .Ignore(dest => dest.Age) - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); - -`ForType` differs in that it will create a new mapping if one doesn't exist, but if the specified TSource => TDestination -mapping does already exist, it will enhance the existing mapping instead of dropping and replacing it. - -#####Global Settings -Use global settings to apply policies to all mappings. - - TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); - -Then for individual type mappings, you can easily override the global setting(s). - - TypeAdapterConfig.NewConfig().PreserveReference(false); - -#####Settings inheritance -Type mappings will automatically inherit for source types. Ie. if you set up following config. - - TypeAdapterConfig.NewConfig() - .Map(dest => dest.Name, src => src.Name + "_Suffix"); - -A derived type of `SimplePoco` will automatically apply the base mapping config. - - var dest = TypeAdapter.Adapt(src); //dest.Name = src.Name + "_Suffix" - -If you don't wish a derived type to use the base mapping, just define `NoInherit` for that type. - - TypeAdapterConfig.NewConfig().NoInherit(true); - - //or at the global level - TypeAdapterConfig.GlobalSettings.Default.NoInherit(true); - -And by default, Mapster will not inherit destination type mappings. You can turn on by `AllowImplicitDestinationInheritance`. - - TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; - -Finally, Mapster also provides methods to inherit explicitly. - - TypeAdapterConfig.NewConfig() - .Inherits(); - -#####Rule based settings -To set the setting at a more granular level. You can use the `When` method in global settings. -In the example below, when any source type and destination type are the same, we will not the copy the `Id` property. - - TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType) - .Ignore("Id"); - -In this example, the config would only apply to Query Expressions (projections). - - TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection) - .IgnoreAttribute(typeof(NotMapAttribute)); - -#####Setting instance -You may wish to have different settings in different scenarios. -If you would not like to apply setting at a static level, Mapster also provides setting instance configurations. - - var config = new TypeAdapterConfig(); - config.Default.Ignore("Id"); - -For instance configurations, you can use the same `NewConfig` and `ForType` methods that are used at the global level with -the same behavior: `NewConfig` drops any existing configuration and `ForType` creates or enhances a configuration. - - config.NewConfig() - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); - - config.ForType() - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); - -You can apply a specific config instance by passing it to the `Adapt` method. (NOTE: please reuse your config instance to prevent recompilation) - - var result = TypeAdapter.Adapt(src, config); - -Or to an Adapter instance. - - var adapter = new Adapter(config); - var result = adapter.Adapt(src); - -If you would like to create configuration instance from existing configuration, you can use `Clone` method. For example, if you would like to clone from global setting. - - var newConfig = TypeAdapterConfig.GlobalSettings.Clone(); - -Or clone from existing configuration instance - - var newConfig = oldConfig.Clone(); - -#####Assembly scanning -It's relatively common to have mapping configurations spread across a number of different assemblies. -Perhaps your domain assembly has some rules to map to domain objects and your web api has some specific rules to map to your -api contracts. In these cases, it can be helpful to allow assemblies to be scanned for these rules so you have some basic -method of organizing your rules and not forgetting to have the registration code called. In some cases, it may even be necessary to -register the assemblies in a particular order, so that some rules override others. Assembly scanning helps with this. -Assembly scanning is simple, just create any number of IRegister implementations in your assembly, then call `Scan` from your TypeAdapterConfig class: - - public class MyRegister : IRegister - { - public void Register(TypeAdapterConfig config){ - config.NewConfig(); - - //OR to create or enhance an existing configuration - - config.ForType(); - } - } - -To scan and register at the Global level: - - TypeAdapterConfig.GlobalSettings.Scan(assembly1, assembly2, assemblyN) - -For a specific config instance: - - var config = new TypeAdapterConfig(); - config.Scan(assembly1, assembly2, assemblyN); - -If you use other assembly scanning library such as MEF, you can easily apply registration with `Apply` method. - - var registers = container.GetExports(); - config.Apply(registers); - -####Basic Customization -When the default convention mappings aren't enough to do the job, you can specify complex source mappings. - -#####Ignore Members & Attributes -Mapster will automatically map properties with the same names. You can ignore members by using the `Ignore` method. - - TypeAdapterConfig - .NewConfig() - .Ignore(dest => dest.Id); - -You can ignore members conditionally, with condition based on source or target. When the condition is met, mapping of the property -will be skipped altogether. This is the difference from custom `Map` with condition, where destination is set to `null` -when condition is met. - - TypeAdapterConfig - .NewConfig() - .IgnoreIf((src, dest) => !string.IsNullOrEmpty(dest.Name), dest => dest.Name); - -You can ignore members annotated with specific attributes by using the `IgnoreAttribute` method. - - TypeAdapterConfig - .NewConfig() - .IgnoreAttribute(typeof(JsonIgnoreAttribute)); - -#####Custom property mapping -You can customize how Mapster maps values to a property. - - TypeAdapterConfig - .NewConfig() - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); - -The Map configuration can accept a third parameter that provides a condition based on the source. -If the condition is not met, Mapster will retry with next conditions. Default condition should be added at the end without specifying condition. If you do not specify default condition, null or default value will be assigned. - - TypeAdapterConfig - .NewConfig() - .Map(dest => dest.FullName, src => "Sig. " + src.FullName, srcCond => srcCond.Country == "Italy") - .Map(dest => dest.FullName, src => "Sr. " + src.FullName, srcCond => srcCond.Country == "Spain") - .Map(dest => dest.FullName, src => "Mr. " + src.FullName); - -In Mapster 2.0, you can even map when source and destination property types are different. - - TypeAdapterConfig - .NewConfig() - .Map(dest => dest.Gender, //Genders.Male or Genders.Female - src => src.GenderString); //"Male" or "Female" - -#####Flexible name mapping -By default, Mapster will map property with case sensitive name. You can adjust to flexible name mapping by setting `NameMatchingStrategy.Flexible` to `NameMatchingStrategy` method. This setting will allow matching between `PascalCase`, `camelCase`, `lower_case`, and `UPPER_CASE`. - -This setting will apply flexible naming globally. - -``` -TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.Flexible); -``` - -or by specific type mapping. - -``` -TypeAdapterConfig.NewConfig().NameMatchingStrategy(NameMatchingStrategy.Flexible); -``` - -#####Merge object -By default, Mapster will map all properties, even source properties containing null values. -You can copy only properties that have values by using `IgnoreNullValues` method. - - TypeAdapterConfig - .NewConfig() - .IgnoreNullValues(true); - -#####Shallow copy -By default, Mapster will recursively map nested objects. You can do shallow copying by setting `ShallowCopyForSameType` to `true`. - - TypeAdapterConfig - .NewConfig() - .ShallowCopyForSameType(true); - -#####Preserve reference (preventing circular reference stackoverflow) -When mapping objects with circular references, a stackoverflow exception will result. -This is because Mapster will get stuck in a loop tring to recursively map the circular reference. -If you would like to map circular references or preserve references (such as 2 properties pointing to the same object), you can do it by setting `PreserveReference` to `true` - - TypeAdapterConfig - .NewConfig() - .PreserveReference(true); - -NOTE: Projection doesn't support circular reference. To overcome, you might use `Adapt` instead of `ProjectToType`. - - TypeAdaptConfig.GlobalSettings.Default.PreserveReference(true); - var students = context.Student.Include(p => p.Schools).Adapt>(); - -####Advance Customization -#####Custom Destination Object Creation -You can provide a function call to create your destination objects instead of using the default object creation -(which expects an empty constructor). To do so, use the `ConstructUsing` method when configuring. This method expects -a function that will provide the destination instance. You can call your own constructor, a factory method, -or anything else that provides an object of the expected type. - - //Example using a non-default constructor - TypeAdapterConfig.NewConfig() - .ConstructUsing(src => new TDestination(src.Id, src.Name)); - - //Example using an object initializer - TypeAdapterConfig.NewConfig() - .ConstructUsing(src => new TDestination{Unmapped = "unmapped"}); - -#####After mapping action -You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. - -``` -TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); -``` - -Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. - -``` -TypeAdapterConfig.GlobalSettings.ForDestinationType() - .AfterMapping(dest => dest.Validate()); -``` - -#####Passing runtime value -In some cases, you might would like to pass runtime values (ie, current user). On configuration, we can receive run-time value by `MapContext.Current.Parameters`. - -``` -TypeAdapterConfig.NewConfig() - .Map(dest => dest.CreatedBy, - src => MapContext.Current.Parameters["user"]); -``` - -To pass run-time value, we need to use `BuildAdapter` method, and call `AddParameters` method to add each parameter. - -``` -var dto = poco.BuildAdapter() - .AddParameters("user", this.User.Identity.Name) - .AdaptToType(); -``` - -#####Type-Specific Destination Transforms -This allows transforms for all items of a type, such as trimming all strings. But really any operation -can be performed on the destination value before assignment. - - //Global - TypeAdapterConfig.GlobalSettings.Default.AddDestinationTransforms((string x) => x.Trim()); - - //Per mapping configuration - TypeAdapterConfig.NewConfig() - .AddDestinationTransforms((string x) => x.Trim()); - -#####Custom Type Resolvers -In some cases, you may want to have complete control over how an object is mapped. You can register specific transformations using the `MapWith` method. - - //Example of transforming string to char[]. - TypeAdapterConfig.NewConfig() - .MapWith(str => str.ToCharArray()); - -`MapWith` also useful if you would like to copy instance rather than deep copy the object, for instance, `JObject` or `DbGeography`, these should treat as primitive types rather than POCO. - - TypeAdapterConfig.NewConfig() - .MapWith(json => json); - -####Validation -To validate your mapping in unit tests and in order to help with "Fail Fast" situations, the following strict mapping modes have been added. - -#####Explicit Mapping -Forcing all classes to be explicitly mapped: - - //Default is "false" - TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; - //This means you have to have an explicit configuration for each class, even if it's just: - TypeAdapterConfig.NewConfig(); - -#####Checking Destination Member -Forcing all destination properties to have a corresponding source member or explicit mapping/ignore: - - //Default is "false" - TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; - -#####Validating Mappings -Both a specific TypeAdapterConfig or all current configurations can be validated. In addition, if Explicit Mappings (above) are enabled, it will also include errors for classes that are not registered at all with the mapper. - - //Validate a specific config - var config = TypeAdapterConfig.NewConfig(); - config.Compile(); - - //Validate globally - TypeAdapterConfig.NewConfig(); - TypeAdapterConfig.NewConfig(); - TypeAdapterConfig.GlobalSettings.Compile(); From cbc6c2b80671b1a073885aa266fe8a4b932554db Mon Sep 17 00:00:00 2001 From: chaowlert Date: Wed, 24 May 2017 14:46:30 +0700 Subject: [PATCH 005/363] Created Debugging (markdown) --- Debugging.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 Debugging.md diff --git a/Debugging.md b/Debugging.md new file mode 100644 index 00000000..90a1d60a --- /dev/null +++ b/Debugging.md @@ -0,0 +1 @@ +... \ No newline at end of file From 9e6ad85c66ab2bd7b74cb450a0461b759ac342a6 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Wed, 24 May 2017 16:24:28 +0700 Subject: [PATCH 006/363] Created EF6 (markdown) --- EF6.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 EF6.md diff --git a/EF6.md b/EF6.md new file mode 100644 index 00000000..90a1d60a --- /dev/null +++ b/EF6.md @@ -0,0 +1 @@ +... \ No newline at end of file From f5f6a42c7c54ec9e2bfe1354873d5be61a16375e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 25 May 2017 14:57:42 +0700 Subject: [PATCH 007/363] Created Setting values (markdown) --- Setting-values.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Setting-values.md diff --git a/Setting-values.md b/Setting-values.md new file mode 100644 index 00000000..204d1510 --- /dev/null +++ b/Setting-values.md @@ -0,0 +1,37 @@ +### Computed value + +You can use `Map` method to specify logic to compute value. For example, compute full name from first name and last name. + +``` +TypeAdapterConfig.NewConfig() + .Map(dest => dest.FullName, src => src.FirstName + " " + src.LastName); +``` + +### Transform value + +While `Map` method specify logic for single property, `AddDestinationTransform` allows transforms for all items of a type, such as trimming all strings. But really any operation can be performed on the destination value before assignment. + + //Global + TypeAdapterConfig.GlobalSettings.Default.AddDestinationTransforms((string x) => x.Trim()); + + //Per mapping configuration + TypeAdapterConfig.NewConfig() + .AddDestinationTransforms((string x) => x.Trim()); + +### Passing run-time value + +In some cases, you might would like to pass runtime values (ie, current user). On configuration, we can receive run-time value by `MapContext.Current.Parameters`. + +``` +TypeAdapterConfig.NewConfig() + .Map(dest => dest.CreatedBy, + src => MapContext.Current.Parameters["user"]); +``` + +To pass run-time value, we need to use `BuildAdapter` method, and call `AddParameters` method to add each parameter. + +``` +var dto = poco.BuildAdapter() + .AddParameters("user", this.User.Identity.Name) + .AdaptToType(); +``` \ No newline at end of file From fe1a3b6dce9cce7f4e68ce59efbe5a854992e976 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 26 May 2017 14:18:16 +0700 Subject: [PATCH 008/363] Created Rule based member mapping (markdown) --- Rule-based-member-mapping.md | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Rule-based-member-mapping.md diff --git a/Rule-based-member-mapping.md b/Rule-based-member-mapping.md new file mode 100644 index 00000000..06d05fde --- /dev/null +++ b/Rule-based-member-mapping.md @@ -0,0 +1,56 @@ +By default, Mapster will include public fields and properties, but we can change this behavior by `IncludeMember` and `IgnoreMember` method. The methods require predicate, and input types of predicate are: + + public interface IMemberModel + { + Type Type { get; } + string Name { get; } + object Info { get; } + AccessModifier SetterModifier { get; } + AccessModifier AccessModifier { get; } + IEnumerable GetCustomAttributes(bool inherit); + } + + public enum MemberSide + { + Source, + Destination, + } + +### Not allow fields + +If you would like to allow only properties not public field to be mapped, you can check from `Info`. Possible values could be `PropertyInfo`, `FieldInfo`, or `ParameterInfo`. In this case, we will reject member of type `FieldInfo`. + + TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => member.Info is FieldInfo); + +### Allow only some list of types to be mapped + +Suppose you are working with EF, and you would like to skip all navigation properties. Then we will allow only short list of types. + + TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); + +### Allow internal members + +If you would like to map members marked as internal, you can do it by: + + TypeAdapterConfig.GlobalSettings.Default + .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal + || member.AccessModifier == AccessModifier.ProtectedInternal); + +### Allow only DataMember attribute + +If you would like to include all members decorated with DataMember attribute, and ignore all members with no DataMember attribute, you can set up by: + + TypeAdapterConfig.GlobalSettings.Default + .IncludeMember((member, side) => member.GetCustomAttributes(true).OfType().Any()); + TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => !member.GetCustomAttributes(true).OfType().Any()); + +### Allow non-public setters + +Mapster 3.0 required you to make explicit mapping in order to map to non-public setters. But you can override by: + + TypeAdapterConfig.GlobalSettings.Default + .IncludeMember((member, side) => side == MemberSide.Destination + && member.SetterModifier != AccessModifier.None); //allow all except no setter From 8fc1495a748998abef40e2e2caa976a6f0e4a0bc Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 26 May 2017 14:30:57 +0700 Subject: [PATCH 009/363] Created Setting by attributes (markdown) --- Setting-by-attributes.md | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Setting-by-attributes.md diff --git a/Setting-by-attributes.md b/Setting-by-attributes.md new file mode 100644 index 00000000..4fc954b1 --- /dev/null +++ b/Setting-by-attributes.md @@ -0,0 +1,58 @@ +### AdaptIgnore attribute + +When a property decorated with `[AdaptIgnore]`, that property will be excluded from Mapping. For example, if we would like to exclude price to be mapped. + +``` +public class Product { + public string Id { get; set; } + public string Name { get; set; } + + [AdaptIgnore] + public decimal Price { get; set; } +} +``` + +### Ignore custom attributes + +You can ignore members annotated with any attributes by using the `IgnoreAttribute` method. + + TypeAdapterConfig.GlobalSettings.Default + .IgnoreAttribute(typeof(JsonIgnoreAttribute)); + +### AdaptMember attribute + +With `AdaptMember` attribute, you can specify name of source or target to be mapped. For example, if we would like to map `Id` to `Code`. + +``` +public class Product { + [AdaptMember("Code")] + public string Id { get; set; } + public string Name { get; set; } +} +``` + +You can also map non-public members with `AdaptMember` attribute. + +``` +public class Product { + [AdaptMember] + private string HiddenId { get; set; } + public string Name { get; set; } +} +``` + +### Rename from custom attributes + +You can rename member to be matched by `GetMemberName`. For example, if we would like to rename property based on `JsonProperty` attribute. + + TypeAdapterConfig.GlobalSettings.Default + .GetMemberName(member => member.GetCustomAttributes(true) + .OfType() + .FirstOrDefault()?.PropertyName); //if return null, property will not be renamed + +### Include custom attributes + +And if we would like to include non-public members decorated with `JsonProperty` attribute, we can do it by `IncludeAttribute`. + + TypeAdapterConfig.GlobalSettings.Default + .IncludeAttribute(typeof(JsonPropertyAttribute)); From 7b89416769420b9bf853018a9bc215f203891f68 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 26 May 2017 14:44:48 +0700 Subject: [PATCH 010/363] Created Ignoring members (markdown) --- Ignoring-members.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Ignoring-members.md diff --git a/Ignoring-members.md b/Ignoring-members.md new file mode 100644 index 00000000..109bba4b --- /dev/null +++ b/Ignoring-members.md @@ -0,0 +1,41 @@ +### Ignore + +Mapster will automatically map properties with the same names. You can ignore members by using the `Ignore` method. + + TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.Id); + +### Rule based ignore + +You can ignore based on member information by `IgnoreMember` command. Please see https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping for more info. + +### IgnoreNonMapped + +You can ignore all non-mapped members by IgnoreNonMapped command. For example, we would like to map only Id and Name. + + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.Id, src => src.Id) + .Map(dest => dest.Name, src => src.Name) + .IgnoreNonMapped(true); + +### Ignore by attribute + +You can ignore member by decorate with `[AdaptIgnore]`, and you can ignore custom attributes by `IgnoreAttribute` command. Please see https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes for more info. + +### Ignore conditionally + +You can ignore members conditionally, with condition based on source or target. When the condition is met, mapping of the property will be skipped altogether. This is the difference from custom `Map` with condition, where destination is set to `null` when condition is met. + + TypeAdapterConfig + .NewConfig() + .IgnoreIf((src, dest) => !string.IsNullOrEmpty(dest.Name), dest => dest.Name); + +### IgnoreNullValues + +You might would like to merge from input object, By default, Mapster will map all properties, even source properties containing null values. You can copy only properties that have values by using `IgnoreNullValues` method. + + TypeAdapterConfig + .NewConfig() + .IgnoreNullValues(true); \ No newline at end of file From 5e7386e7db29f7530b3ea211a28e9bd17100c6af Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 26 May 2017 15:20:11 +0700 Subject: [PATCH 011/363] Created Mapping (markdown) --- Mapping.md | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Mapping.md diff --git a/Mapping.md b/Mapping.md new file mode 100644 index 00000000..15fd24a6 --- /dev/null +++ b/Mapping.md @@ -0,0 +1,99 @@ +### Primitives + +Converting between primitive types (ie. int, bool, double, decimal) is supported, including when those types are nullable. For all other types, if you can cast types in c#, you can also cast in Mapster. + + decimal i = 123.Adapt(); //equal to (decimal)123; + +### Enums + +Mapster maps enums to numerics automatically, but it also maps strings to and from enums automatically in a fast manner. +The default Enum.ToString() in .Net is quite slow. The implementation in Mapster is double the speed. Likewise, a fast conversion from strings to enums is also included. If the string is null or empty, the enum will initialize to the first enum value. + +In Mapster, flagged enums are also supported. + + var e = "Read, Write, Delete".Adapt(); + //FileShare.Read | FileShare.Write | FileShare.Delete + +For enum to enum with different type, by default, Mapster will map enum by value. You can override to map enum by name by: + + TypeAdapterConfig.GlobalSettings.Default + .EnumMappingStrategy(EnumMappingStrategy.ByName); + +### Strings + +When Mapster maps other types to string, Mapster will use `ToString` method. And when ever Mapster maps string to the other types, Mapster will use `Parse` method. + + var s = 123.Adapt(); //equal to 123.ToString(); + var i = "123".Adapt(); //equal to int.Parse("123"); + +### Collections + +This includes mapping among lists, arrays, collections, dictionary including various interfaces: IList, ICollection, IEnumerable etc... + + var list = db.Pocos.ToList(); + var target = list.Adapt>(); + +### Object + +Mapster can map 2 different objects using the following rules +- Source and destination property names are the same. Ex: `dest.Name = src.Name` +- Source has get method. Ex: `dest.Name = src.GetName()` +- Source property has child object which can flatten to destination. Ex: `dest.ContactName = src.Contact.Name` or `dest.Contact_Name = src.Contact.Name` + +Example: + + class Staff { + public string Name { get; set; } + public int GetAge() { return (DateTime.Now - this.BirthDate).TotalDays / 365.25; } + public Staff Supervisor { get; set; } + ... + } + + struct StaffDto { + public string Name { get; set; } + public int Age { get; set; } + public string SupervisorName { get; set; } + } + + var dto = staff.Adapt(); + //dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name + +You can make custom mapping logic by +- Setting custom mapping (https://github.com/chaowlert/Mapster/wiki/Custom-mapping) +- Setting custom naming convention (https://github.com/chaowlert/Mapster/wiki/Naming-convention) +- Setting by attributes (https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes) +- Ignoring members (https://github.com/chaowlert/Mapster/wiki/Ignoring-members) +- Setting rule based mapping (https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping) + +Object types are included: +- POCO classes +- POCO structs +- Dictionary type implement IDictionary +- Record types + +Example for object to dictionary + +``` +var point = new { X = 2, Y = 3 }; +var dict = src.Adapt>(); +dict["Y"].ShouldBe(3); +``` + +Example for record types + +``` +class Person { + public string Name { get; } + public int Age { get; } + + public Person(string name, int age) { + this.Name = name; + this.Age = age; + } +} + +var src = new { Name = "Mapster", Age = 3 }; +var target = src.Adapt(); +``` + +There is limitation on record type mapping. Record type must not have setter and have only one non-empty constructor. And all parameter names must match with properties. \ No newline at end of file From 8cb70a6c425a17515a4eb0cee707653d6b7a2fe6 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 26 May 2017 15:25:47 +0700 Subject: [PATCH 012/363] Created Basic (markdown) --- Basic.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Basic.md diff --git a/Basic.md b/Basic.md new file mode 100644 index 00000000..df7f3e27 --- /dev/null +++ b/Basic.md @@ -0,0 +1,30 @@ +### Mapping to a new object +Mapster creates the destination object and maps values to it. + + var destObject = sourceObject.Adapt(); + +### Mapping to an existing object + +You make the object, Mapster maps to the object. + + TDestination destObject = new TDestination(); + destObject = sourceObject.Adapt(destObject); + +### Queryable Extensions + +Mapster also provides extensions to map queryables. + + using (MyDbContext context = new MyDbContext()) + { + // Build a Select Expression from DTO + var destinations = context.Sources.ProjectToType().ToList(); + + // Versus creating by hand: + var destinations = context.Sources.Select(c => new Destination(){ + Id = p.Id, + Name = p.Name, + Surname = p.Surname, + .... + }) + .ToList(); + } From 96e79c191a0eb9023c62f65fff426ad18e613423 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 26 May 2017 15:54:53 +0700 Subject: [PATCH 013/363] Created Mapper (markdown) --- Mapper.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Mapper.md diff --git a/Mapper.md b/Mapper.md new file mode 100644 index 00000000..5dcc6c22 --- /dev/null +++ b/Mapper.md @@ -0,0 +1,48 @@ +### Extension method + +You can simply call `Adapt` method from anywhere. + + var destObject = sourceObject.Adapt(); + +or just + + var destObject = sourceObject.Adapt(); + +You might notice that there are 2 extension methods doing the same thing. In fact, `sourceObject.Adapt` is a bit better in term of performance (different is just casting from object type). If your application doesn't require high performance, you can just use `sourceObject.Adapt` signature. + +### Builder + +In most case `Adapt` method is enough, but sometimes we need builder to support fancy scenario. Basic example, is to pass run-time value. + +``` +var dto = poco.BuildAdapter() + .AddParameters("user", this.User.Identity.Name) + .AdaptToType(); +``` + +Or we can see how Mapster generate mapping logic with Debugger plugin (https://github.com/chaowlert/Mapster/wiki/Debugging). + +``` +var script = poco.BuildAdapter() + .CreateMapExpression() + .ToScript(); +``` + +Or we can map to EF6 object context (https://github.com/chaowlert/Mapster/wiki/EF6). + +``` +var poco = dto.BuildAdapter() + .CreateEntityFromContext(db) + .AdaptToType(); +``` + +### Mapper instance + +In some cases, you need an instance of a mapper (or a factory function) to pass into a DI container. Mapster has +the IAdapter and Adapter to fill this need: + + IAdapter adapter = new Adapter(); + +And usage is the same as with the static methods. + + var result = adapter.Adapt(source); From e4c6e25c50a5572e1128280add8f1862f52c7e04 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 09:36:03 +0700 Subject: [PATCH 014/363] Updated Ignoring members (markdown) --- Ignoring-members.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Ignoring-members.md b/Ignoring-members.md index 109bba4b..0fe4e17b 100644 --- a/Ignoring-members.md +++ b/Ignoring-members.md @@ -10,6 +10,9 @@ Mapster will automatically map properties with the same names. You can ignore me You can ignore based on member information by `IgnoreMember` command. Please see https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping for more info. + TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); + ### IgnoreNonMapped You can ignore all non-mapped members by IgnoreNonMapped command. For example, we would like to map only Id and Name. @@ -24,6 +27,16 @@ You can ignore all non-mapped members by IgnoreNonMapped command. For example, w You can ignore member by decorate with `[AdaptIgnore]`, and you can ignore custom attributes by `IgnoreAttribute` command. Please see https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes for more info. +``` +public class Product { + public string Id { get; set; } + public string Name { get; set; } + + [AdaptIgnore] + public decimal Price { get; set; } +} +``` + ### Ignore conditionally You can ignore members conditionally, with condition based on source or target. When the condition is met, mapping of the property will be skipped altogether. This is the difference from custom `Map` with condition, where destination is set to `null` when condition is met. From f79887e95097cbae600f0e136d78655d2187ccaa Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 09:43:39 +0700 Subject: [PATCH 015/363] Updated Mapping (markdown) --- Mapping.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mapping.md b/Mapping.md index 15fd24a6..815a75d4 100644 --- a/Mapping.md +++ b/Mapping.md @@ -33,7 +33,7 @@ This includes mapping among lists, arrays, collections, dictionary including var var list = db.Pocos.ToList(); var target = list.Adapt>(); -### Object +### Mappable Objects Mapster can map 2 different objects using the following rules - Source and destination property names are the same. Ex: `dest.Name = src.Name` @@ -65,7 +65,7 @@ You can make custom mapping logic by - Ignoring members (https://github.com/chaowlert/Mapster/wiki/Ignoring-members) - Setting rule based mapping (https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping) -Object types are included: +Mappable Object types are included: - POCO classes - POCO structs - Dictionary type implement IDictionary From f22dca5304ced3a32c3fc32a1b698e386d1000ff Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 09:46:11 +0700 Subject: [PATCH 016/363] Updated Rule based member mapping (markdown) --- Rule-based-member-mapping.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Rule-based-member-mapping.md b/Rule-based-member-mapping.md index 06d05fde..d5926168 100644 --- a/Rule-based-member-mapping.md +++ b/Rule-based-member-mapping.md @@ -47,10 +47,10 @@ If you would like to include all members decorated with DataMember attribute, an TypeAdapterConfig.GlobalSettings.Default .IgnoreMember((member, side) => !member.GetCustomAttributes(true).OfType().Any()); -### Allow non-public setters +### Turn-off non-public setters -Mapster 3.0 required you to make explicit mapping in order to map to non-public setters. But you can override by: +Mapster always allows non-public setters. But you can override by: TypeAdapterConfig.GlobalSettings.Default - .IncludeMember((member, side) => side == MemberSide.Destination - && member.SetterModifier != AccessModifier.None); //allow all except no setter + .IgnoreMember((member, side) => side == MemberSide.Destination + && member.SetterModifier != AccessModifier.Public); From d5644e6e88a51cf5b5c8ff40772bb4ccd6db79ee Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 12:00:05 +0700 Subject: [PATCH 017/363] Created Configuration (markdown) --- Configuration.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Configuration.md diff --git a/Configuration.md b/Configuration.md new file mode 100644 index 00000000..f842b2e6 --- /dev/null +++ b/Configuration.md @@ -0,0 +1,76 @@ +### Setting per type pair + +You can easily create settings for a type mapping by using: `TypeAdapterConfig.NewConfig()`. +When `NewConfig` is called, any previous configuration for this particular TSource => TDestination mapping is dropped. + + TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.Age) + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + +As an alternative to `NewConfig`, you can use `ForType` in the same way: + + TypeAdapterConfig + .ForType() + .Ignore(dest => dest.Age) + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + +`ForType` differs in that it will create a new mapping if one doesn't exist, but if the specified TSource => TDestination +mapping does already exist, it will enhance the existing mapping instead of dropping and replacing it. + +### Global setting + +Use global settings to apply policies to all mappings. + + TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); + +Then for individual type mappings, you can easily override the global setting(s). + + TypeAdapterConfig.NewConfig().PreserveReference(false); + +### Settings inheritance + +Type mappings will automatically inherit for source types. Ie. if you set up following config. + + TypeAdapterConfig.NewConfig() + .Map(dest => dest.Name, src => src.Name + "_Suffix"); + +A derived type of `SimplePoco` will automatically apply the base mapping config. + + var dest = TypeAdapter.Adapt(src); //dest.Name = src.Name + "_Suffix" + +If you don't wish a derived type to use the base mapping, you can turn off by `AllowImplicitSourceInheritance` + + TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = true; + +And by default, Mapster will not inherit destination type mappings. You can turn on by `AllowImplicitDestinationInheritance`. + + TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; + +Finally, Mapster also provides methods to inherit explicitly. + + TypeAdapterConfig.NewConfig() + .Inherits(); + +### Rule based settings + +To set the setting at a more granular level. You can use the `When` method in global settings. +In the example below, when any source type and destination type are the same, we will not the copy the `Id` property. + + TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType) + .Ignore("Id"); + +In this example, the config would only apply to Query Expressions (projections). + + TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection) + .IgnoreAttribute(typeof(NotMapAttribute)); + +### Open generics + +If mapping type is generic, you can create setting by passing generic type definition to `ForType`. + + TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) + .Map("value", "Value"); + From 2ee2dea2597286edeec847e9d70e30eaa0ad6c63 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 12:07:42 +0700 Subject: [PATCH 018/363] Created Config Instance (markdown) --- Config-Instance.md | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Config-Instance.md diff --git a/Config-Instance.md b/Config-Instance.md new file mode 100644 index 00000000..f8e3e553 --- /dev/null +++ b/Config-Instance.md @@ -0,0 +1,47 @@ +### Config instance + +You may wish to have different settings in different scenarios. +If you would not like to apply setting at a static level, Mapster also provides setting instance configurations. + + var config = new TypeAdapterConfig(); + config.Default.Ignore("Id"); + +For instance configurations, you can use the same `NewConfig` and `ForType` methods that are used at the global level with +the same behavior: `NewConfig` drops any existing configuration and `ForType` creates or enhances a configuration. + + config.NewConfig() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + + config.ForType() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + +You can apply a specific config instance by passing it to the `Adapt` method. (NOTE: please reuse your config instance to prevent recompilation) + + var result = src.Adapt(config); + +Or to an Adapter instance. + + var adapter = new Adapter(config); + var result = adapter.Adapt(src); + +### Clone + +If you would like to create configuration instance from existing configuration, you can use `Clone` method. For example, if you would like to clone from global setting. + + var newConfig = TypeAdapterConfig.GlobalSettings.Clone(); + +Or clone from existing configuration instance + + var newConfig = oldConfig.Clone(); + +### Fork + +`Fork` is similar to `Clone`, but `Fork` will allow you to keep configuration and mapping in the same location. See (https://github.com/chaowlert/Mapster/wiki/Config-location) for more info. + + var forked = mainConfig.Fork(config => + config.ForType() + .Map(dest => dest.code, src => src.Id)); + + var dto = poco.Adapt(forked); \ No newline at end of file From 14a47da7b32934981a6d1807e815dae407eb583f Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 12:51:48 +0700 Subject: [PATCH 019/363] Created Config location (markdown) --- Config-location.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Config-location.md diff --git a/Config-location.md b/Config-location.md new file mode 100644 index 00000000..461c115c --- /dev/null +++ b/Config-location.md @@ -0,0 +1,74 @@ +### Entry point + +Configuration should be set only once and reuse for mapping. Therefore, we should not keep configuration and mapping in the same location. For example: + + config.ForType().Ignore("Id"); + var dto1 = poco1.Adapt(config); + + config.ForType().Ignore("Id"); <--- Exception occurred here, because config was already compiled + var dto2 = poco2.Adapt(config); + +Therefore, you should separate configuration and mapping. Configuration should keep in entry point such as `Main` function or `Global.asax.cs` or `Startup.cs`. + +``` +// Application_Start in Global.asax.cs +config.ForType().Ignore("Id"); +``` + +``` +// in Controller class +var dto1 = poco1.Adapt(config); +var dto2 = poco2.Adapt(config); +``` + +### Keep together with mapping + +Problem with separating configuration and mapping is, coding will be separated into 2 locations. You might remove or alter mapping, and you can forget to update the configuration. `Fork` method allow you to keep config and mapping inline. + + var dto = poco.Adapt( + config.Fork(forked => forked.ForType().Ignore("Id")); + +Don't worry about performance, forked config will be compiled only once. When mapping occurs for the second time, `Fork` function will return config from cache. + +### In separated assemblies + +It's relatively common to have mapping configurations spread across a number of different assemblies. +Perhaps your domain assembly has some rules to map to domain objects and your web api has some specific rules to map to your api contracts. + +#### Scan method + +It can be helpful to allow assemblies to be scanned for these rules so you have some basic method of organizing your rules and not forgetting to have the registration code called. In some cases, it may even be necessary to register the assemblies in a particular order, so that some rules override others. Assembly scanning helps with this. + +Assembly scanning is simple, just create any number of `IRegister` implementations in your assembly, then call `Scan` from your `TypeAdapterConfig` class: + + public class MyRegister : IRegister + { + public void Register(TypeAdapterConfig config) + { + config.NewConfig(); + + //OR to create or enhance an existing configuration + config.ForType(); + } + } + +To scan and register at the Global level: + + TypeAdapterConfig.GlobalSettings.Scan(assembly1, assembly2, assemblyN) + +For a specific config instance: + + var config = new TypeAdapterConfig(); + config.Scan(assembly1, assembly2, assemblyN); + +#### Apply method + +If you use other assembly scanning library such as MEF, you can easily apply registration with `Apply` method. + + var registers = container.GetExports(); + config.Apply(registers); + +`Apply` method also allow you to selectively pick from one or more `IRegister` rather than every `IRegister` in assembly. + + var register = new MockingRegister(); + config.Apply(register); From bd473f538ff8ba93164db3f50b41b3c4bfab5f89 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 12:55:28 +0700 Subject: [PATCH 020/363] Created Config validation (markdown) --- Config-validation.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Config-validation.md diff --git a/Config-validation.md b/Config-validation.md new file mode 100644 index 00000000..3e0291ee --- /dev/null +++ b/Config-validation.md @@ -0,0 +1,27 @@ +To validate your mapping in unit tests and in order to help with "Fail Fast" situations, the following strict mapping modes have been added. + +### Explicit Mapping +Forcing all classes to be explicitly mapped: + + //Default is "false" + TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; + //This means you have to have an explicit configuration for each class, even if it's just: + TypeAdapterConfig.NewConfig(); + +### Checking Destination Member +Forcing all destination properties to have a corresponding source member or explicit mapping/ignore: + + //Default is "false" + TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + +### Validating Mappings +Both a specific TypeAdapterConfig or all current configurations can be validated. In addition, if Explicit Mappings (above) are enabled, it will also include errors for classes that are not registered at all with the mapper. + + //Validate a specific config + var config = TypeAdapterConfig.NewConfig(); + config.Compile(); + + //Validate globally + TypeAdapterConfig.NewConfig(); + TypeAdapterConfig.NewConfig(); + TypeAdapterConfig.GlobalSettings.Compile(); From 58a03995e97ff927727f8a7cddf685045fa66b72 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 13:01:05 +0700 Subject: [PATCH 021/363] Updated Configuration (markdown) --- Configuration.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Configuration.md b/Configuration.md index f842b2e6..4b2a7efb 100644 --- a/Configuration.md +++ b/Configuration.md @@ -67,6 +67,13 @@ In this example, the config would only apply to Query Expressions (projections). TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection) .IgnoreAttribute(typeof(NotMapAttribute)); +### Destination type only + +Setting also be able to done without knowing the source type, by using `ForDestinationType`. For example, you can do `AfterMapping` setting to validate after mapping. + + TypeAdapterConfig.GlobalSettings.ForDestinationType() + .AfterMapping(dest => dest.Validate()); + ### Open generics If mapping type is generic, you can create setting by passing generic type definition to `ForType`. From 0672ed9280c0e1d4e76714b9abf760fa7e32bbe5 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 13:04:55 +0700 Subject: [PATCH 022/363] Updated Configuration (markdown) --- Configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Configuration.md b/Configuration.md index 4b2a7efb..a287c937 100644 --- a/Configuration.md +++ b/Configuration.md @@ -74,6 +74,8 @@ Setting also be able to done without knowing the source type, by using `ForDesti TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMapping(dest => dest.Validate()); +NOTE: `ForDestinationType` above will always apply to all types assignable to `IValidator`. If destination class implements `IValidator`, it will also apply the `AfterMapping` config. + ### Open generics If mapping type is generic, you can create setting by passing generic type definition to `ForType`. From 30f7f7b09b42034f0e343e422695fe8707a18c3d Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 13:24:47 +0700 Subject: [PATCH 023/363] Created Custom mapping (markdown) --- Custom-mapping.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Custom-mapping.md diff --git a/Custom-mapping.md b/Custom-mapping.md new file mode 100644 index 00000000..f0c5b4dc --- /dev/null +++ b/Custom-mapping.md @@ -0,0 +1,38 @@ +### Custom member mapping + +You can customize how Mapster maps values to a property. + + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); + +You can even map when source and destination property types are different. + + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.Gender, //Genders.Male or Genders.Female + src => src.GenderString); //"Male" or "Female" + +### Mapping with condition + +The Map configuration can accept a third parameter that provides a condition based on the source. +If the condition is not met, Mapster will retry with next conditions. Default condition should be added at the end without specifying condition. If you do not specify default condition, null or default value will be assigned. + + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.FullName, src => "Sig. " + src.FullName, srcCond => srcCond.Country == "Italy") + .Map(dest => dest.FullName, src => "Sr. " + src.FullName, srcCond => srcCond.Country == "Spain") + .Map(dest => dest.FullName, src => "Mr. " + src.FullName); + +NOTE: if you would like to skip mapping, when condition is met, you can use `IgnoreIf` (https://github.com/chaowlert/Mapster/wiki/Ignoring-members#ignore-conditionally). + +### Mapping to non-public members + +`Map` command can map to private member by specify name of the members. + + TypeAdapterConfig + .NewConfig() + .Map("PrivateDestName", "PrivateSrcName"); + +For more information about mapping non-public members, please see https://github.com/chaowlert/Mapster/wiki/Mapping-non-public-members. From d8aa3feb9ac69ba74217a76253b2727c30a95566 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 15:47:01 +0700 Subject: [PATCH 024/363] Created Naming convention (markdown) --- Naming-convention.md | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Naming-convention.md diff --git a/Naming-convention.md b/Naming-convention.md new file mode 100644 index 00000000..784e22a9 --- /dev/null +++ b/Naming-convention.md @@ -0,0 +1,46 @@ +### Flexible name + +By default, Mapster will map property with case sensitive name. You can adjust to flexible name mapping by setting `NameMatchingStrategy.Flexible` to `NameMatchingStrategy` method. This setting will allow matching between `PascalCase`, `camelCase`, `lower_case`, and `UPPER_CASE`. + +This setting will apply flexible naming globally. + +``` +TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.Flexible); +``` + +or by specific type mapping. + +``` +TypeAdapterConfig.NewConfig().NameMatchingStrategy(NameMatchingStrategy.Flexible); +``` + +### Ignore cases + +Flexible name could not map between `MiXcAsE` and `MixCase`, because flexible name will break `MiXcAsE` into `Mi-Xc-As-E` rather than just `Mix-Case`. In this case, we need to use `IgnoreCase` to perform case insensitive matching. + +``` +TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.IgnoreCase); +``` + +### Prefix & Replace + +For custom rules, you can use either `ConvertSourceMemberName` or `ConvertDestinationMemberName` up to which side you would like to convert. For example, you might would like to add `m_` to all properties. + + TypeAdapterConfig.NewConfig() + .NameMatchingStrategy(NameMatchingStrategy.ConvertSourceMemberName(name => "m_" + name)); + +This example is to replace foreign letter from name. + + TypeAdapterConfig.NewConfig() + .NameMatchingStrategy(NameMatchingStrategy.ConvertSourceMemberName(name => name.Replace("Ä", "A")); + +### Naming Convention with IDictionary + +If you would like to change case from POCO to `IDictionary` to camelCase, you can use `ToCamelCase`. Another way around, if you would like to map `IDictionary` back to POCO, you can use `FromCamelCase`. + + TypeAdapterConfig>.NewConfig() + .NameMatchingStrategy(NameMatchingStrategy.ToCamelCase); + TypeAdapterConfig, Poco>.NewConfig() + .NameMatchingStrategy(NameMatchingStrategy.FromCamelCase); + +NOTE: mapping from `IDictionary` to POCO, you can also use `Flexible` or `IgnoreCase`, but both will be slower since it will scan through dictionary entries rather than lookup. \ No newline at end of file From b0c6ab9dd240d0dfb3f219966d18a905d82e35a4 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 16:10:41 +0700 Subject: [PATCH 025/363] Updated Naming convention (markdown) --- Naming-convention.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Naming-convention.md b/Naming-convention.md index 784e22a9..b4e21ef4 100644 --- a/Naming-convention.md +++ b/Naming-convention.md @@ -43,4 +43,25 @@ If you would like to change case from POCO to `IDictionary` to camelC TypeAdapterConfig, Poco>.NewConfig() .NameMatchingStrategy(NameMatchingStrategy.FromCamelCase); -NOTE: mapping from `IDictionary` to POCO, you can also use `Flexible` or `IgnoreCase`, but both will be slower since it will scan through dictionary entries rather than lookup. \ No newline at end of file +NOTE: mapping from `IDictionary` to POCO, you can also use `Flexible` or `IgnoreCase`, but both will be slower since it will scan through dictionary entries rather than lookup. + +### Rule based Naming + +You can change name based on rule by `GetMemberName` method. For example, if we would like to rename property based on `JsonProperty` attribute. + + TypeAdapterConfig.GlobalSettings.Default + .GetMemberName(member => member.GetCustomAttributes(true) + .OfType() + .FirstOrDefault()?.PropertyName); //if return null, property will not be renamed + +Then in your class + + public class Poco + { + [JsonProperty("code")] + public string Id { get; set; } + + ... + } + +With above config, `Id` will be mapped to `code`. \ No newline at end of file From 64b9e588eb6a2f11619252815660e5fdf985d935 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 16:19:54 +0700 Subject: [PATCH 026/363] Created Object references (markdown) --- Object-references.md | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Object-references.md diff --git a/Object-references.md b/Object-references.md new file mode 100644 index 00000000..59cd5e4c --- /dev/null +++ b/Object-references.md @@ -0,0 +1,47 @@ +### Preserve reference (preventing circular reference stackoverflow) + +When mapping objects with circular references, a stackoverflow exception will result. This is because Mapster will get stuck in a loop trying to recursively map the circular reference. If you would like to map circular references or preserve references (such as 2 properties pointing to the same object), you can do it by setting `PreserveReference` to `true` + + TypeAdapterConfig + .NewConfig() + .PreserveReference(true); + +NOTE: Projection doesn't support circular reference. To overcome, you might use `Adapt` instead of `ProjectToType`. + + TypeAdaptConfig.GlobalSettings.Default.PreserveReference(true); + var students = context.Student.Include(p => p.Schools).Adapt>(); + +NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/chaowlert/Mapster/wiki/Nested-class-mapping). Therefore, you need to apply config to all type pairs. + + TypeAdapterConfig.NewConfig().PreserveReference(true); + TypeAdapterConfig.NewConfig().PreserveReference(true); + TypeAdapterConfig.NewConfig().PreserveReference(true); + +Or you can set `PreserveReference` in global setting. + + TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); + +If you don't want to set config in global setting, you can also use `Fork`. + + var forked = TypeAdapterConfig.GlobalSettings.Default.Fork(config => config.Default.PreserveReference(true)); + var parentDto = parentPoco.Adapt(forked); + +### Shallow copy + +By default, Mapster will recursively map nested objects. You can do shallow copying by setting `ShallowCopyForSameType` to `true`. + + TypeAdapterConfig + .NewConfig() + .ShallowCopyForSameType(true); + +### Mapping very large objects + +For performance optimization, Mapster tried to inline class mapping. This process will takes time if your models are complex. + +![](https://cloud.githubusercontent.com/assets/21364231/25666644/ce38c8c0-3029-11e7-8793-8a51c519c2a0.png) + +You can skip inlining process by: + + TypeAdapterConfig.GlobalSettings.Default + .AvoidInlineMapping(true); + From bb4c2f09ebfdd79b8b5c3805dbdb69aa57221205 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 16:22:07 +0700 Subject: [PATCH 027/363] Created Object creation and validation (markdown) --- Object-creation-and-validation.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Object-creation-and-validation.md diff --git a/Object-creation-and-validation.md b/Object-creation-and-validation.md new file mode 100644 index 00000000..1073a8a0 --- /dev/null +++ b/Object-creation-and-validation.md @@ -0,0 +1,29 @@ +### Custom Destination Object Creation + +You can provide a function call to create your destination objects instead of using the default object creation +(which expects an empty constructor). To do so, use the `ConstructUsing` method when configuring. This method expects +a function that will provide the destination instance. You can call your own constructor, a factory method, +or anything else that provides an object of the expected type. + + //Example using a non-default constructor + TypeAdapterConfig.NewConfig() + .ConstructUsing(src => new TDestination(src.Id, src.Name)); + + //Example using an object initializer + TypeAdapterConfig.NewConfig() + .ConstructUsing(src => new TDestination{Unmapped = "unmapped"}); + +### After mapping action + +You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. + +``` +TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); +``` + +Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. + +``` +TypeAdapterConfig.GlobalSettings.ForDestinationType() + .AfterMapping(dest => dest.Validate()); +``` From bead44e2ce08171586afb2eb5b6f0e6df83fe376 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 16:35:47 +0700 Subject: [PATCH 028/363] Created Custom conversion logic (markdown) --- Custom-conversion-logic.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Custom-conversion-logic.md diff --git a/Custom-conversion-logic.md b/Custom-conversion-logic.md new file mode 100644 index 00000000..b1ecacb2 --- /dev/null +++ b/Custom-conversion-logic.md @@ -0,0 +1,27 @@ +### Custom type conversion + +In some cases, you may want to have complete control over how an object is mapped. You can register specific transformations using the `MapWith` method. + + //Example of transforming string to char[]. + TypeAdapterConfig.NewConfig() + .MapWith(str => str.ToCharArray()); + +`MapWith` also useful if you would like to copy instance rather than deep copy the object, for instance, `JObject` or `DbGeography`, these should treat as primitive types rather than POCO. + + TypeAdapterConfig.NewConfig() + .MapWith(json => json); + +In case you would like to combine `MapWith` with other settings, for example, `PreserveReference`, `Include`, or `AfterMapping`, you can pass `applySettings` to true. + + TypeAdapterConfig.NewConfig() + .PreserveReference(true) + .MapWith(poco => poco.ToDto(), applySettings: true); + +### Custom mapping data to existing object + +You can control mapping to existing object logic by `MapToTargetWith`. For example, you can copy data to existing array. + + TypeAdapterConfig.NewConfig() + .MapToTargetWith((src, dest) => Array.Copy(src, dest, src.Length)); + +NOTE: if you set `MapWith` setting but no `MapToTargetWith` setting, Mapster will use logic from `MapWith` setting. \ No newline at end of file From 68dc90f5de2c5de474c658f216d624bfed5d5e2f Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 18:36:27 +0700 Subject: [PATCH 029/363] Updated Basic (markdown) --- Basic.md => Basic-usages.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Basic.md => Basic-usages.md (100%) diff --git a/Basic.md b/Basic-usages.md similarity index 100% rename from Basic.md rename to Basic-usages.md From 92171171303ad53f42dc6e2a757207901047141d Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 18:37:13 +0700 Subject: [PATCH 030/363] Updated Mapper (markdown) --- Mapper.md => Mappers.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Mapper.md => Mappers.md (100%) diff --git a/Mapper.md b/Mappers.md similarity index 100% rename from Mapper.md rename to Mappers.md From 7385d52d26c27eeea5bb644b575d5983211d09ab Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 18:37:50 +0700 Subject: [PATCH 031/363] Updated Mapping (markdown) --- Mapping.md => Data-types.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Mapping.md => Data-types.md (100%) diff --git a/Mapping.md b/Data-types.md similarity index 100% rename from Mapping.md rename to Data-types.md From 63a8acbd53739b77cb119de0914c7a690872ec82 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 18:44:13 +0700 Subject: [PATCH 032/363] Updated Configuration (markdown) --- Configuration.md | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/Configuration.md b/Configuration.md index a287c937..2db49a15 100644 --- a/Configuration.md +++ b/Configuration.md @@ -30,30 +30,6 @@ Then for individual type mappings, you can easily override the global setting(s) TypeAdapterConfig.NewConfig().PreserveReference(false); -### Settings inheritance - -Type mappings will automatically inherit for source types. Ie. if you set up following config. - - TypeAdapterConfig.NewConfig() - .Map(dest => dest.Name, src => src.Name + "_Suffix"); - -A derived type of `SimplePoco` will automatically apply the base mapping config. - - var dest = TypeAdapter.Adapt(src); //dest.Name = src.Name + "_Suffix" - -If you don't wish a derived type to use the base mapping, you can turn off by `AllowImplicitSourceInheritance` - - TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = true; - -And by default, Mapster will not inherit destination type mappings. You can turn on by `AllowImplicitDestinationInheritance`. - - TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; - -Finally, Mapster also provides methods to inherit explicitly. - - TypeAdapterConfig.NewConfig() - .Inherits(); - ### Rule based settings To set the setting at a more granular level. You can use the `When` method in global settings. From fd251efe5bd25b92b66cfcfe1b5213d31dfbc220 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 18:51:13 +0700 Subject: [PATCH 033/363] Created Config inheritance (markdown) --- Config-inheritance.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Config-inheritance.md diff --git a/Config-inheritance.md b/Config-inheritance.md new file mode 100644 index 00000000..31b9fbb3 --- /dev/null +++ b/Config-inheritance.md @@ -0,0 +1,38 @@ +### Implicit inheritance + +Type mappings will automatically inherit for source types. Ie. if you set up following config. + + TypeAdapterConfig.NewConfig() + .Map(dest => dest.Name, src => src.Name + "_Suffix"); + +A derived type of `SimplePoco` will automatically apply the base mapping config. + + var dest = TypeAdapter.Adapt(src); //dest.Name = src.Name + "_Suffix" + +If you don't wish a derived type to use the base mapping, you can turn off by `AllowImplicitSourceInheritance` + + TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = true; + +And by default, Mapster will not inherit destination type mappings. You can turn on by `AllowImplicitDestinationInheritance`. + + TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; + +### Explicit inheritance + +You can copy setting from based type explicitly. + + TypeAdapterConfig.NewConfig() + .Inherits(); + +### Include derived types + +You can also include derived type to the based type declaration. For example: + + TypeAdapterConfig.NewConfig() + .Include(); + + Vehicle vehicle = new Car { Id = 1, Name = "Car", Make = "Toyota" }; + var dto = vehicle.Adapt(); + + dto.ShouldBeOfType(); + ((CarDto)dto).Make.ShouldBe("Toyota"); //Make property doesn't exist in Vehicle From 921533b2f95d9adedca6caf30ea8532848bbc333 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 18:52:17 +0700 Subject: [PATCH 034/363] Updated Config Instance (markdown) --- Config-Instance.md => Config-instance.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Config-Instance.md => Config-instance.md (100%) diff --git a/Config-Instance.md b/Config-instance.md similarity index 100% rename from Config-Instance.md rename to Config-instance.md From 9a46b29bdb2a1488cbf615c6b574d7ac9bb07728 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 19:00:11 +0700 Subject: [PATCH 035/363] Updated Config inheritance (markdown) --- Config-inheritance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Config-inheritance.md b/Config-inheritance.md index 31b9fbb3..12c8908e 100644 --- a/Config-inheritance.md +++ b/Config-inheritance.md @@ -11,7 +11,7 @@ A derived type of `SimplePoco` will automatically apply the base mapping config. If you don't wish a derived type to use the base mapping, you can turn off by `AllowImplicitSourceInheritance` - TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = true; + TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = false; And by default, Mapster will not inherit destination type mappings. You can turn on by `AllowImplicitDestinationInheritance`. From eeb2dc472b26c12eed593f30959f1bca7d354387 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 19:17:35 +0700 Subject: [PATCH 036/363] Updated Config location (markdown) --- Config-location.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Config-location.md b/Config-location.md index 461c115c..71e4b674 100644 --- a/Config-location.md +++ b/Config-location.md @@ -30,6 +30,20 @@ Problem with separating configuration and mapping is, coding will be separated i Don't worry about performance, forked config will be compiled only once. When mapping occurs for the second time, `Fork` function will return config from cache. +##### Using Fork in generic class or method + +`Fork` method uses filename and line number and the key. But if you use `Fork` method inside generic class or method, you must specify your own key (with all type names) to prevent `Fork` to return invalid config from different type arguments. + +``` +IQueryable GetItems() +{ + var forked = config.Fork( + f => f.ForType().Ignore("Id"), + $"MyKey|{typeof(TPoco).FullName}|{typeof(TDto).FullName}"); + return db.Set().ProjectToType(forked); +} +``` + ### In separated assemblies It's relatively common to have mapping configurations spread across a number of different assemblies. From 630606d2f810d70677b8916070f1c63cd8eb0376 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 19:25:12 +0700 Subject: [PATCH 037/363] Updated Object references (markdown) --- Object-references.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Object-references.md b/Object-references.md index 59cd5e4c..cd035c78 100644 --- a/Object-references.md +++ b/Object-references.md @@ -11,20 +11,7 @@ NOTE: Projection doesn't support circular reference. To overcome, you might use TypeAdaptConfig.GlobalSettings.Default.PreserveReference(true); var students = context.Student.Include(p => p.Schools).Adapt>(); -NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/chaowlert/Mapster/wiki/Nested-class-mapping). Therefore, you need to apply config to all type pairs. - - TypeAdapterConfig.NewConfig().PreserveReference(true); - TypeAdapterConfig.NewConfig().PreserveReference(true); - TypeAdapterConfig.NewConfig().PreserveReference(true); - -Or you can set `PreserveReference` in global setting. - - TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); - -If you don't want to set config in global setting, you can also use `Fork`. - - var forked = TypeAdapterConfig.GlobalSettings.Default.Fork(config => config.Default.PreserveReference(true)); - var parentDto = parentPoco.Adapt(forked); +NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/chaowlert/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. ### Shallow copy From be65ade3c5299cace8d550c00bad77d96f238351 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 19:28:27 +0700 Subject: [PATCH 038/363] Created Config for nested classes (markdown) --- Config-for-nested-classes.md | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Config-for-nested-classes.md diff --git a/Config-for-nested-classes.md b/Config-for-nested-classes.md new file mode 100644 index 00000000..4b416596 --- /dev/null +++ b/Config-for-nested-classes.md @@ -0,0 +1,42 @@ +Configuration is per type pair, not per type hierarchy. For example if you have parent and child classes. + +``` +class ParentPoco +{ + public string Id { get; set; } + public List Children { get; set; } +} +class ChildPoco +{ + public string Id { get; set; } + public List GrandChildren { get; set; } +} +class GrandChildPoco +{ + public string Id { get; set; } +} +``` + +And if you have setting on parent type. + +``` +TypeAdapterConfig.NewConfig().PreserveReference(true); +``` + +When mapping, child type will not get effect from `PreserveReference`. + +To do so, you must specify all type pairs inside `ParentPoco`. + + TypeAdapterConfig.NewConfig().PreserveReference(true); + TypeAdapterConfig.NewConfig().PreserveReference(true); + TypeAdapterConfig.NewConfig().PreserveReference(true); + +Or you can set `PreserveReference` in global setting. + + TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); + +If you don't want to set config in global setting, you can also use `Fork`. + + var forked = TypeAdapterConfig.GlobalSettings.Fork(config => config.Default.PreserveReference(true)); + var parentDto = parentPoco.Adapt(forked); + From 500f29221a15c74b970392a4805c585bbbaa83cf Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 19:53:50 +0700 Subject: [PATCH 039/363] Created Mapping non-public members (markdown) --- Mapping-non-public-members.md | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Mapping-non-public-members.md diff --git a/Mapping-non-public-members.md b/Mapping-non-public-members.md new file mode 100644 index 00000000..67a514a8 --- /dev/null +++ b/Mapping-non-public-members.md @@ -0,0 +1,44 @@ +### EnableNonPublicMember + +This will allow Mapster to set to all non-public members. You can turn on per type pair. + + TypeAdapterConfig.NewConfig().EnableNonPublicMember(true); + +Or global! + + TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); + +If you turn on at global level, you must also declare type pair to ensure Mapster will treat type as POCO rather than primitive. + + TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); + TypeAdapterConfig.NewConfig(); + +### AdaptMember attribute + +You can also map non-public members with `AdaptMember` attribute. + +``` +public class Product +{ + [AdaptMember] + private string HiddenId { get; set; } + public string Name { get; set; } +} +``` + +### Map + +`Map` command can map to private member by specify name of the members. + + TypeAdapterConfig + .NewConfig() + .Map("PrivateDestName", "PrivateSrcName"); + +### IncludeMember + +With `IncludeMember`, you can select which access modifier to allow. + + TypeAdapterConfig.GlobalSettings.Default + .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal + || member.AccessModifier == AccessModifier.ProtectedInternal); + From 2feab93ff4bb813b3cf9db97329145cad2523a2b Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 20:03:40 +0700 Subject: [PATCH 040/363] Updated Custom conversion logic (markdown) --- Custom-conversion-logic.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Custom-conversion-logic.md b/Custom-conversion-logic.md index b1ecacb2..4a011d3b 100644 --- a/Custom-conversion-logic.md +++ b/Custom-conversion-logic.md @@ -24,4 +24,11 @@ You can control mapping to existing object logic by `MapToTargetWith`. For examp TypeAdapterConfig.NewConfig() .MapToTargetWith((src, dest) => Array.Copy(src, dest, src.Length)); -NOTE: if you set `MapWith` setting but no `MapToTargetWith` setting, Mapster will use logic from `MapWith` setting. \ No newline at end of file +NOTE: if you set `MapWith` setting but no `MapToTargetWith` setting, Mapster will use logic from `MapWith` setting. + +### Custom actions after mapping + +You might not need to specify custom mapping logic completely. You can let Mapster do the mapping, and you do logic where Mapster cannot cover by using `AfterMapping`. + + TypeAdapterConfig.NewConfig() + .AfterMapping((src, dest) => SpecialSetFn(src, dest)); From 11f48a616d474984113c8fe07e3de46d57c8726d Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 20:52:41 +0700 Subject: [PATCH 041/363] Updated Debugging (markdown) --- Debugging.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Debugging.md b/Debugging.md index 90a1d60a..369c85be 100644 --- a/Debugging.md +++ b/Debugging.md @@ -1 +1,38 @@ -... \ No newline at end of file +### Step-into debugging + + PM> Install-Package Mapster.Diagnostics + +This plugin allow you to perform step-into debugging! + +In order to step-into debugging, make sure you turn off just my code feature. +![turn off just my code](https://cloud.githubusercontent.com/assets/5763993/23740682/47608676-04d7-11e7-842d-77c18a459515.png) + +##### Usage + +Just declare `EnableDebugging`, this command is working only in debug mode. Therefore, you can keep this flag turn on. But do not forget to build your application in release mode for production, otherwise, there will be huge performance impact. + + TypeAdapterConfig.GlobalSettings.EnableDebugging(); + + var dto = poco.Adapt(); <--- Press F11 on this line to step-into this function!! + +![image](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) + +##### Net Core support + +Currently, step-into debugging is working only in .NET 4.x. .NET Core doesn't yet support. + +##### Using internal classes or members + +`private` and `protected` don't allow in debug mode. You can access to `internal`, but you have to add following attribute to your assembly. + + [assembly: InternalsVisibleTo("Mapster.Dynamic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ED6C7EE4B2E0AC62548FAA596F267469CD877990306F7C97ACBD889455E5D5846FFC5DCD5D87A72263DC7EB276629261C115DA3456485AE051D0C4EDBED3DD1A0092280F6408407D88FF3A76F2E6EECA4DE369E31A2A631052C99CAC2105B9A273969A66B076D5FC2B9B57F8E45D358906F58A0D959EA9BA2D725131A0E372AB")] + +### Get mapping script + +We can also see how Mapster generate mapping logic with `ToScript` method. + +``` +var script = poco.BuildAdapter() + .CreateMapExpression() + .ToScript(); +``` From aed60203bce64edc71b3c36f2653a9b36cc2027a Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 20:53:49 +0700 Subject: [PATCH 042/363] Updated Home (markdown) --- Getting-Started.md | 31 +++++++++++++++++++++++++++++++ Home.md | 1 - 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 Getting-Started.md delete mode 100644 Home.md diff --git a/Getting-Started.md b/Getting-Started.md new file mode 100644 index 00000000..4bf3c47c --- /dev/null +++ b/Getting-Started.md @@ -0,0 +1,31 @@ +### Getting Started + +##### Mapping +* [Basic usages](https://github.com/chaowlert/Mapster/wiki/Basic-usages) +* [3 kind of mappers](https://github.com/chaowlert/Mapster/wiki/Mappers) +* [Data types](https://github.com/chaowlert/Mapster/wiki/Data-types) + +##### Configuration +* [Configuration](https://github.com/chaowlert/Mapster/wiki/Configuration) +* [Config inheritance](https://github.com/chaowlert/Mapster/wiki/Config-inheritance) +* [Config instance](https://github.com/chaowlert/Mapster/wiki/Config-instance) +* [Config location](https://github.com/chaowlert/Mapster/wiki/Config-location) +* [Config validation](https://github.com/chaowlert/Mapster/wiki/Config-validation) +* [Config for nested classes](https://github.com/chaowlert/Mapster/wiki/Config-for-nested-classes) + +##### Settings +* Custom member matching logic + * [Custom mapping](https://github.com/chaowlert/Mapster/wiki/Custom-mapping) + * [Custom naming convention](https://github.com/chaowlert/Mapster/wiki/Naming-convention) + * [Setting by attributes](https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes) + * [Ignoring members](https://github.com/chaowlert/Mapster/wiki/Ignoring-members) + * [Rule-based member matching](https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping) + * [Mapping non-public members](https://github.com/chaowlert/Mapster/wiki/Mapping-non-public-members) +* [Object creation & validation](https://github.com/chaowlert/Mapster/wiki/Object-creation-and-validation) +* [Setting values](https://github.com/chaowlert/Mapster/wiki/Setting-values) +* [Working with object references](https://github.com/chaowlert/Mapster/wiki/Object-references) +* [Custom conversion logic](https://github.com/chaowlert/Mapster/wiki/Custom-conversion-logic) + +##### Plugins +* [Debugging](https://github.com/chaowlert/Mapster/wiki/Debugging) +* [EF6](https://github.com/chaowlert/Mapster/wiki/EF6) \ No newline at end of file diff --git a/Home.md b/Home.md deleted file mode 100644 index 778ea539..00000000 --- a/Home.md +++ /dev/null @@ -1 +0,0 @@ -Welcome to the Mapster wiki! From 251bd335876e17523470c2e0c5fda3f86ecff579 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 27 May 2017 20:54:13 +0700 Subject: [PATCH 043/363] Updated Getting Started (markdown) --- Getting-Started.md => Home.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Getting-Started.md => Home.md (100%) diff --git a/Getting-Started.md b/Home.md similarity index 100% rename from Getting-Started.md rename to Home.md From 0250b693c293c4f3a9e4b275782e8be90c11392b Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 28 May 2017 00:04:40 +0700 Subject: [PATCH 044/363] Updated EF6 (markdown) --- EF6.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/EF6.md b/EF6.md index 90a1d60a..6680f9ba 100644 --- a/EF6.md +++ b/EF6.md @@ -1 +1,23 @@ -... \ No newline at end of file +In EntityFramework, when we got data from dto, and we would like to map data to entities. We need to get objects from store first and then mapping data to it. Problem is, when the object contains complex graph, it is very difficult to map data correctly. Because some child property might reference to another object in store, and some children collection may or may not exist in store. This plugin will help you to copy data to object in store correctly. If data doesn't found, new object will be created. + + PM> Install-Package Mapster.EF6 + +Sample + +``` +var poco = dto.BuildAdapter() + .CreateEntityFromContext(db) + .AdaptToType(); +``` + +NOTE: If your data contains complex graph, it would be better to dump related records in single command to store first. This is to avoid plugin from downloading records one-by-one, which should be slower. + +``` +//just make the query, objects will be cached in store +db.DomainPoco.Include("Children").Where(item => item.Id == dto.Id).FirstOrDefault(); + +//when mapping, Mapster will get object from cache rather than lookup in db +var poco = dto.BuildAdapter() + .CreateEntityFromContext(db) + .AdaptToType(); +``` From 3e0bb86725591bdd69a66b57d962380aae1ed309 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 28 May 2017 14:44:30 +0700 Subject: [PATCH 045/363] Updated Debugging (markdown) --- Debugging.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Debugging.md b/Debugging.md index 369c85be..6b9c691f 100644 --- a/Debugging.md +++ b/Debugging.md @@ -9,11 +9,14 @@ In order to step-into debugging, make sure you turn off just my code feature. ##### Usage -Just declare `EnableDebugging`, this command is working only in debug mode. Therefore, you can keep this flag turn on. But do not forget to build your application in release mode for production, otherwise, there will be huge performance impact. +Just declare `EnableDebugging`, this command is working only in debug mode. Therefore, you can keep this flag turn on. But do not forget to build your application in release mode for production, otherwise, there will be huge performance impact. This command should be called before you start mapping. TypeAdapterConfig.GlobalSettings.EnableDebugging(); - var dto = poco.Adapt(); <--- Press F11 on this line to step-into this function!! +And whenever you would like to step into mapping function, you need to mark `MapsterDebugger.BreakOnEnterAdaptMethod` to `true`. You can turn on or off any time by coding or Immediate window. + + MapsterDebugger.BreakOnEnterAdaptMethod = true; + var dto = poco.Adapt(); <--- After this line, you will step-into this function!! ![image](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) From 059685db7ee8eac29ff3c2bec872b5714ce16072 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 3 Jun 2017 09:01:31 +0700 Subject: [PATCH 046/363] Created _Sidebar (markdown) --- _Sidebar.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 _Sidebar.md diff --git a/_Sidebar.md b/_Sidebar.md new file mode 100644 index 00000000..4bf3c47c --- /dev/null +++ b/_Sidebar.md @@ -0,0 +1,31 @@ +### Getting Started + +##### Mapping +* [Basic usages](https://github.com/chaowlert/Mapster/wiki/Basic-usages) +* [3 kind of mappers](https://github.com/chaowlert/Mapster/wiki/Mappers) +* [Data types](https://github.com/chaowlert/Mapster/wiki/Data-types) + +##### Configuration +* [Configuration](https://github.com/chaowlert/Mapster/wiki/Configuration) +* [Config inheritance](https://github.com/chaowlert/Mapster/wiki/Config-inheritance) +* [Config instance](https://github.com/chaowlert/Mapster/wiki/Config-instance) +* [Config location](https://github.com/chaowlert/Mapster/wiki/Config-location) +* [Config validation](https://github.com/chaowlert/Mapster/wiki/Config-validation) +* [Config for nested classes](https://github.com/chaowlert/Mapster/wiki/Config-for-nested-classes) + +##### Settings +* Custom member matching logic + * [Custom mapping](https://github.com/chaowlert/Mapster/wiki/Custom-mapping) + * [Custom naming convention](https://github.com/chaowlert/Mapster/wiki/Naming-convention) + * [Setting by attributes](https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes) + * [Ignoring members](https://github.com/chaowlert/Mapster/wiki/Ignoring-members) + * [Rule-based member matching](https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping) + * [Mapping non-public members](https://github.com/chaowlert/Mapster/wiki/Mapping-non-public-members) +* [Object creation & validation](https://github.com/chaowlert/Mapster/wiki/Object-creation-and-validation) +* [Setting values](https://github.com/chaowlert/Mapster/wiki/Setting-values) +* [Working with object references](https://github.com/chaowlert/Mapster/wiki/Object-references) +* [Custom conversion logic](https://github.com/chaowlert/Mapster/wiki/Custom-conversion-logic) + +##### Plugins +* [Debugging](https://github.com/chaowlert/Mapster/wiki/Debugging) +* [EF6](https://github.com/chaowlert/Mapster/wiki/EF6) \ No newline at end of file From 33a4ab22de89b77fc5b404dbf0357a48bdc15c98 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Wed, 11 Oct 2017 21:51:07 +0700 Subject: [PATCH 047/363] update url --- Config-instance.md | 2 +- Custom-mapping.md | 4 ++-- Data-types.md | 10 +++++----- Home.md | 42 +++++++++++++++++++++--------------------- Ignoring-members.md | 4 ++-- Mappers.md | 4 ++-- Object-references.md | 2 +- _Sidebar.md | 42 +++++++++++++++++++++--------------------- 8 files changed, 55 insertions(+), 55 deletions(-) diff --git a/Config-instance.md b/Config-instance.md index f8e3e553..bb3a7481 100644 --- a/Config-instance.md +++ b/Config-instance.md @@ -38,7 +38,7 @@ Or clone from existing configuration instance ### Fork -`Fork` is similar to `Clone`, but `Fork` will allow you to keep configuration and mapping in the same location. See (https://github.com/chaowlert/Mapster/wiki/Config-location) for more info. +`Fork` is similar to `Clone`, but `Fork` will allow you to keep configuration and mapping in the same location. See (https://github.com/MapsterMapper/Mapster/wiki/Config-location) for more info. var forked = mainConfig.Fork(config => config.ForType() diff --git a/Custom-mapping.md b/Custom-mapping.md index f0c5b4dc..6cf1dd57 100644 --- a/Custom-mapping.md +++ b/Custom-mapping.md @@ -25,7 +25,7 @@ If the condition is not met, Mapster will retry with next conditions. Default co .Map(dest => dest.FullName, src => "Sr. " + src.FullName, srcCond => srcCond.Country == "Spain") .Map(dest => dest.FullName, src => "Mr. " + src.FullName); -NOTE: if you would like to skip mapping, when condition is met, you can use `IgnoreIf` (https://github.com/chaowlert/Mapster/wiki/Ignoring-members#ignore-conditionally). +NOTE: if you would like to skip mapping, when condition is met, you can use `IgnoreIf` (https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members#ignore-conditionally). ### Mapping to non-public members @@ -35,4 +35,4 @@ NOTE: if you would like to skip mapping, when condition is met, you can use `Ign .NewConfig() .Map("PrivateDestName", "PrivateSrcName"); -For more information about mapping non-public members, please see https://github.com/chaowlert/Mapster/wiki/Mapping-non-public-members. +For more information about mapping non-public members, please see https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members. diff --git a/Data-types.md b/Data-types.md index 815a75d4..45c74429 100644 --- a/Data-types.md +++ b/Data-types.md @@ -59,11 +59,11 @@ Example: //dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name You can make custom mapping logic by -- Setting custom mapping (https://github.com/chaowlert/Mapster/wiki/Custom-mapping) -- Setting custom naming convention (https://github.com/chaowlert/Mapster/wiki/Naming-convention) -- Setting by attributes (https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes) -- Ignoring members (https://github.com/chaowlert/Mapster/wiki/Ignoring-members) -- Setting rule based mapping (https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping) +- Setting custom mapping (https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) +- Setting custom naming convention (https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) +- Setting by attributes (https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) +- Ignoring members (https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) +- Setting rule based mapping (https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) Mappable Object types are included: - POCO classes diff --git a/Home.md b/Home.md index 4bf3c47c..84c0b751 100644 --- a/Home.md +++ b/Home.md @@ -1,31 +1,31 @@ ### Getting Started ##### Mapping -* [Basic usages](https://github.com/chaowlert/Mapster/wiki/Basic-usages) -* [3 kind of mappers](https://github.com/chaowlert/Mapster/wiki/Mappers) -* [Data types](https://github.com/chaowlert/Mapster/wiki/Data-types) +* [Basic usages](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) +* [3 kind of mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) +* [Data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) ##### Configuration -* [Configuration](https://github.com/chaowlert/Mapster/wiki/Configuration) -* [Config inheritance](https://github.com/chaowlert/Mapster/wiki/Config-inheritance) -* [Config instance](https://github.com/chaowlert/Mapster/wiki/Config-instance) -* [Config location](https://github.com/chaowlert/Mapster/wiki/Config-location) -* [Config validation](https://github.com/chaowlert/Mapster/wiki/Config-validation) -* [Config for nested classes](https://github.com/chaowlert/Mapster/wiki/Config-for-nested-classes) +* [Configuration](https://github.com/MapsterMapper/Mapster/wiki/Configuration) +* [Config inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) +* [Config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) +* [Config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) +* [Config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation) +* [Config for nested classes](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes) ##### Settings * Custom member matching logic - * [Custom mapping](https://github.com/chaowlert/Mapster/wiki/Custom-mapping) - * [Custom naming convention](https://github.com/chaowlert/Mapster/wiki/Naming-convention) - * [Setting by attributes](https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes) - * [Ignoring members](https://github.com/chaowlert/Mapster/wiki/Ignoring-members) - * [Rule-based member matching](https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping) - * [Mapping non-public members](https://github.com/chaowlert/Mapster/wiki/Mapping-non-public-members) -* [Object creation & validation](https://github.com/chaowlert/Mapster/wiki/Object-creation-and-validation) -* [Setting values](https://github.com/chaowlert/Mapster/wiki/Setting-values) -* [Working with object references](https://github.com/chaowlert/Mapster/wiki/Object-references) -* [Custom conversion logic](https://github.com/chaowlert/Mapster/wiki/Custom-conversion-logic) + * [Custom mapping](https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) + * [Custom naming convention](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) + * [Setting by attributes](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) + * [Ignoring members](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) + * [Rule-based member matching](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) + * [Mapping non-public members](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) +* [Object creation & validation](https://github.com/MapsterMapper/Mapster/wiki/Object-creation-and-validation) +* [Setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) +* [Working with object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) +* [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) ##### Plugins -* [Debugging](https://github.com/chaowlert/Mapster/wiki/Debugging) -* [EF6](https://github.com/chaowlert/Mapster/wiki/EF6) \ No newline at end of file +* [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) +* [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) \ No newline at end of file diff --git a/Ignoring-members.md b/Ignoring-members.md index 0fe4e17b..1e843f84 100644 --- a/Ignoring-members.md +++ b/Ignoring-members.md @@ -8,7 +8,7 @@ Mapster will automatically map properties with the same names. You can ignore me ### Rule based ignore -You can ignore based on member information by `IgnoreMember` command. Please see https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping for more info. +You can ignore based on member information by `IgnoreMember` command. Please see https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping for more info. TypeAdapterConfig.GlobalSettings.Default .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); @@ -25,7 +25,7 @@ You can ignore all non-mapped members by IgnoreNonMapped command. For example, w ### Ignore by attribute -You can ignore member by decorate with `[AdaptIgnore]`, and you can ignore custom attributes by `IgnoreAttribute` command. Please see https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes for more info. +You can ignore member by decorate with `[AdaptIgnore]`, and you can ignore custom attributes by `IgnoreAttribute` command. Please see https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes for more info. ``` public class Product { diff --git a/Mappers.md b/Mappers.md index 5dcc6c22..8b6ebf4c 100644 --- a/Mappers.md +++ b/Mappers.md @@ -20,7 +20,7 @@ var dto = poco.BuildAdapter() .AdaptToType(); ``` -Or we can see how Mapster generate mapping logic with Debugger plugin (https://github.com/chaowlert/Mapster/wiki/Debugging). +Or we can see how Mapster generate mapping logic with Debugger plugin (https://github.com/MapsterMapper/Mapster/wiki/Debugging). ``` var script = poco.BuildAdapter() @@ -28,7 +28,7 @@ var script = poco.BuildAdapter() .ToScript(); ``` -Or we can map to EF6 object context (https://github.com/chaowlert/Mapster/wiki/EF6). +Or we can map to EF6 object context (https://github.com/MapsterMapper/Mapster/wiki/EF6). ``` var poco = dto.BuildAdapter() diff --git a/Object-references.md b/Object-references.md index cd035c78..90e2c0bc 100644 --- a/Object-references.md +++ b/Object-references.md @@ -11,7 +11,7 @@ NOTE: Projection doesn't support circular reference. To overcome, you might use TypeAdaptConfig.GlobalSettings.Default.PreserveReference(true); var students = context.Student.Include(p => p.Schools).Adapt>(); -NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/chaowlert/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. +NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. ### Shallow copy diff --git a/_Sidebar.md b/_Sidebar.md index 4bf3c47c..84c0b751 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -1,31 +1,31 @@ ### Getting Started ##### Mapping -* [Basic usages](https://github.com/chaowlert/Mapster/wiki/Basic-usages) -* [3 kind of mappers](https://github.com/chaowlert/Mapster/wiki/Mappers) -* [Data types](https://github.com/chaowlert/Mapster/wiki/Data-types) +* [Basic usages](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) +* [3 kind of mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) +* [Data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) ##### Configuration -* [Configuration](https://github.com/chaowlert/Mapster/wiki/Configuration) -* [Config inheritance](https://github.com/chaowlert/Mapster/wiki/Config-inheritance) -* [Config instance](https://github.com/chaowlert/Mapster/wiki/Config-instance) -* [Config location](https://github.com/chaowlert/Mapster/wiki/Config-location) -* [Config validation](https://github.com/chaowlert/Mapster/wiki/Config-validation) -* [Config for nested classes](https://github.com/chaowlert/Mapster/wiki/Config-for-nested-classes) +* [Configuration](https://github.com/MapsterMapper/Mapster/wiki/Configuration) +* [Config inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) +* [Config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) +* [Config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) +* [Config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation) +* [Config for nested classes](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes) ##### Settings * Custom member matching logic - * [Custom mapping](https://github.com/chaowlert/Mapster/wiki/Custom-mapping) - * [Custom naming convention](https://github.com/chaowlert/Mapster/wiki/Naming-convention) - * [Setting by attributes](https://github.com/chaowlert/Mapster/wiki/Setting-by-attributes) - * [Ignoring members](https://github.com/chaowlert/Mapster/wiki/Ignoring-members) - * [Rule-based member matching](https://github.com/chaowlert/Mapster/wiki/Rule-based-member-mapping) - * [Mapping non-public members](https://github.com/chaowlert/Mapster/wiki/Mapping-non-public-members) -* [Object creation & validation](https://github.com/chaowlert/Mapster/wiki/Object-creation-and-validation) -* [Setting values](https://github.com/chaowlert/Mapster/wiki/Setting-values) -* [Working with object references](https://github.com/chaowlert/Mapster/wiki/Object-references) -* [Custom conversion logic](https://github.com/chaowlert/Mapster/wiki/Custom-conversion-logic) + * [Custom mapping](https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) + * [Custom naming convention](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) + * [Setting by attributes](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) + * [Ignoring members](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) + * [Rule-based member matching](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) + * [Mapping non-public members](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) +* [Object creation & validation](https://github.com/MapsterMapper/Mapster/wiki/Object-creation-and-validation) +* [Setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) +* [Working with object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) +* [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) ##### Plugins -* [Debugging](https://github.com/chaowlert/Mapster/wiki/Debugging) -* [EF6](https://github.com/chaowlert/Mapster/wiki/EF6) \ No newline at end of file +* [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) +* [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) \ No newline at end of file From 963e8a385be14faf29dc4a65940ae04d372385af Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 15 Oct 2017 15:03:15 +0700 Subject: [PATCH 048/363] Created Json.net (markdown) --- Json.net.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Json.net.md diff --git a/Json.net.md b/Json.net.md new file mode 100644 index 00000000..15fd8ce3 --- /dev/null +++ b/Json.net.md @@ -0,0 +1,17 @@ +# Mapster.JsonNet +Json.net conversion supports + +### Install + + PM> Install-Package Mapster.JsonNet + +### Usage + +Call `EnableJsonMapping` from your `TypeAdapterConfig` to enable Json.Net mapping. + + TypeAdapterConfig.GlobalSettings.EnableJsonMapping(); + +This will allow + +- Mapping between Json.Net types (`JToken`, `JArray`, `JObject`) from/to POCO types +- Serialize and deserialize Json.Net types from/to string From ce4040b21bd1a2c356c228218a1a3dbb62b5f31b Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 15 Oct 2017 15:03:44 +0700 Subject: [PATCH 049/363] Updated _Sidebar (markdown) --- _Sidebar.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 84c0b751..725dbd06 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -28,4 +28,5 @@ ##### Plugins * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) -* [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) \ No newline at end of file +* [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) +* [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) \ No newline at end of file From 6eff84012c28f53e0fce90cb6ceed3b5ebe8bedb Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 15 Oct 2017 15:04:05 +0700 Subject: [PATCH 050/363] Updated Home (markdown) --- Home.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 84c0b751..725dbd06 100644 --- a/Home.md +++ b/Home.md @@ -28,4 +28,5 @@ ##### Plugins * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) -* [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) \ No newline at end of file +* [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) +* [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) \ No newline at end of file From b88b1dcb96ebd6fc9b2fdba002617ab85dcc1e44 Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 10:17:25 -0700 Subject: [PATCH 051/363] Updated Object creation and validation (markdown) --- Object-creation-and-validation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Object-creation-and-validation.md b/Object-creation-and-validation.md index 1073a8a0..64e543c4 100644 --- a/Object-creation-and-validation.md +++ b/Object-creation-and-validation.md @@ -4,7 +4,7 @@ You can provide a function call to create your destination objects instead of us (which expects an empty constructor). To do so, use the `ConstructUsing` method when configuring. This method expects a function that will provide the destination instance. You can call your own constructor, a factory method, or anything else that provides an object of the expected type. - +```csharp //Example using a non-default constructor TypeAdapterConfig.NewConfig() .ConstructUsing(src => new TDestination(src.Id, src.Name)); @@ -12,18 +12,18 @@ or anything else that provides an object of the expected type. //Example using an object initializer TypeAdapterConfig.NewConfig() .ConstructUsing(src => new TDestination{Unmapped = "unmapped"}); - +``` ### After mapping action You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. -``` +```csharp TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); ``` Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. -``` +```csharp TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMapping(dest => dest.Validate()); ``` From 74e7cc1d78385054485f27549dc429695b6917c2 Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 10:18:07 -0700 Subject: [PATCH 052/363] Updated Object creation and validation (markdown) --- Object-creation-and-validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Object-creation-and-validation.md b/Object-creation-and-validation.md index 64e543c4..d7e7b192 100644 --- a/Object-creation-and-validation.md +++ b/Object-creation-and-validation.md @@ -18,12 +18,12 @@ or anything else that provides an object of the expected type. You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. ```csharp -TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); + TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); ``` Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. ```csharp -TypeAdapterConfig.GlobalSettings.ForDestinationType() + TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMapping(dest => dest.Validate()); ``` From da7072414a199ae5ee9e93339abf7867a553abfe Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 10:19:38 -0700 Subject: [PATCH 053/363] adding syntax highlighting --- Object-creation-and-validation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Object-creation-and-validation.md b/Object-creation-and-validation.md index d7e7b192..bce2d003 100644 --- a/Object-creation-and-validation.md +++ b/Object-creation-and-validation.md @@ -16,14 +16,14 @@ or anything else that provides an object of the expected type. ### After mapping action You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. - ```csharp - TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); +// test +TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); ``` Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. ```csharp TypeAdapterConfig.GlobalSettings.ForDestinationType() - .AfterMapping(dest => dest.Validate()); + .AfterMapping(dest => dest.Validate()); ``` From a7ad27033a4fd58cf94369e92ebe527a8741311f Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 10:20:34 -0700 Subject: [PATCH 054/363] Updated Object creation and validation (markdown) --- Object-creation-and-validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Object-creation-and-validation.md b/Object-creation-and-validation.md index bce2d003..cfc94de9 100644 --- a/Object-creation-and-validation.md +++ b/Object-creation-and-validation.md @@ -16,8 +16,8 @@ or anything else that provides an object of the expected type. ### After mapping action You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. + ```csharp -// test TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); ``` From 9665957bc7d97760dc5add1fb6eca49032a44364 Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 10:23:11 -0700 Subject: [PATCH 055/363] Updated Object creation and validation (markdown) --- Object-creation-and-validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Object-creation-and-validation.md b/Object-creation-and-validation.md index cfc94de9..9a64d3aa 100644 --- a/Object-creation-and-validation.md +++ b/Object-creation-and-validation.md @@ -17,13 +17,13 @@ or anything else that provides an object of the expected type. You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. -```csharp +``` TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); ``` Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. -```csharp +``` TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMapping(dest => dest.Validate()); ``` From c50b99c6c0979e1ea5b9e94127b0f674f008ec49 Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 10:23:45 -0700 Subject: [PATCH 056/363] Updated Object creation and validation (markdown) --- Object-creation-and-validation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Object-creation-and-validation.md b/Object-creation-and-validation.md index 9a64d3aa..09680d67 100644 --- a/Object-creation-and-validation.md +++ b/Object-creation-and-validation.md @@ -17,13 +17,13 @@ or anything else that provides an object of the expected type. You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. -``` -TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); +```C# + TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); ``` Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. -``` +```C# TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMapping(dest => dest.Validate()); ``` From 114ed5fa9238c3cfc6545033bb4c36c5d7e16437 Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 11:13:27 -0700 Subject: [PATCH 057/363] type in AddDestinationTransform; added syntax --- Setting-values.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Setting-values.md b/Setting-values.md index 204d1510..d22e193b 100644 --- a/Setting-values.md +++ b/Setting-values.md @@ -2,7 +2,7 @@ You can use `Map` method to specify logic to compute value. For example, compute full name from first name and last name. -``` +```csharp TypeAdapterConfig.NewConfig() .Map(dest => dest.FullName, src => src.FirstName + " " + src.LastName); ``` @@ -10,19 +10,19 @@ TypeAdapterConfig.NewConfig() ### Transform value While `Map` method specify logic for single property, `AddDestinationTransform` allows transforms for all items of a type, such as trimming all strings. But really any operation can be performed on the destination value before assignment. - +```csharp //Global - TypeAdapterConfig.GlobalSettings.Default.AddDestinationTransforms((string x) => x.Trim()); + TypeAdapterConfig.GlobalSettings.Default.AddDestinationTransform((string x) => x.Trim()); //Per mapping configuration TypeAdapterConfig.NewConfig() .AddDestinationTransforms((string x) => x.Trim()); - +``` ### Passing run-time value In some cases, you might would like to pass runtime values (ie, current user). On configuration, we can receive run-time value by `MapContext.Current.Parameters`. -``` +```csharp TypeAdapterConfig.NewConfig() .Map(dest => dest.CreatedBy, src => MapContext.Current.Parameters["user"]); @@ -30,7 +30,7 @@ TypeAdapterConfig.NewConfig() To pass run-time value, we need to use `BuildAdapter` method, and call `AddParameters` method to add each parameter. -``` +```csharp var dto = poco.BuildAdapter() .AddParameters("user", this.User.Identity.Name) .AdaptToType(); From 318e57b973ed848c210a57678ac17ae34925c669 Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 19:14:22 -0700 Subject: [PATCH 058/363] add chsarp to code block --- Data-types.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Data-types.md b/Data-types.md index 45c74429..5fa213d3 100644 --- a/Data-types.md +++ b/Data-types.md @@ -73,7 +73,7 @@ Mappable Object types are included: Example for object to dictionary -``` +```csharp var point = new { X = 2, Y = 3 }; var dict = src.Adapt>(); dict["Y"].ShouldBe(3); @@ -81,7 +81,7 @@ dict["Y"].ShouldBe(3); Example for record types -``` +```csharp class Person { public string Name { get; } public int Age { get; } @@ -96,4 +96,4 @@ var src = new { Name = "Mapster", Age = 3 }; var target = src.Adapt(); ``` -There is limitation on record type mapping. Record type must not have setter and have only one non-empty constructor. And all parameter names must match with properties. \ No newline at end of file +There is limitation on record type mapping. Record type must not have setter and have only one non-empty constructor. And all parameter names must match with properties. From 24c1d42be31f642e1194ee1016a252d79d056ad5 Mon Sep 17 00:00:00 2001 From: Ryan E Date: Tue, 24 Oct 2017 19:15:49 -0700 Subject: [PATCH 059/363] ^^ --- Data-types.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Data-types.md b/Data-types.md index 5fa213d3..4d926953 100644 --- a/Data-types.md +++ b/Data-types.md @@ -1,38 +1,38 @@ ### Primitives Converting between primitive types (ie. int, bool, double, decimal) is supported, including when those types are nullable. For all other types, if you can cast types in c#, you can also cast in Mapster. - +```csharp decimal i = 123.Adapt(); //equal to (decimal)123; - +``` ### Enums Mapster maps enums to numerics automatically, but it also maps strings to and from enums automatically in a fast manner. The default Enum.ToString() in .Net is quite slow. The implementation in Mapster is double the speed. Likewise, a fast conversion from strings to enums is also included. If the string is null or empty, the enum will initialize to the first enum value. In Mapster, flagged enums are also supported. - +```csharp var e = "Read, Write, Delete".Adapt(); //FileShare.Read | FileShare.Write | FileShare.Delete - +``` For enum to enum with different type, by default, Mapster will map enum by value. You can override to map enum by name by: - +```csharp TypeAdapterConfig.GlobalSettings.Default .EnumMappingStrategy(EnumMappingStrategy.ByName); - +``` ### Strings When Mapster maps other types to string, Mapster will use `ToString` method. And when ever Mapster maps string to the other types, Mapster will use `Parse` method. - +```csharp var s = 123.Adapt(); //equal to 123.ToString(); var i = "123".Adapt(); //equal to int.Parse("123"); - +``` ### Collections This includes mapping among lists, arrays, collections, dictionary including various interfaces: IList, ICollection, IEnumerable etc... - +```csharp var list = db.Pocos.ToList(); var target = list.Adapt>(); - +``` ### Mappable Objects Mapster can map 2 different objects using the following rules @@ -41,7 +41,7 @@ Mapster can map 2 different objects using the following rules - Source property has child object which can flatten to destination. Ex: `dest.ContactName = src.Contact.Name` or `dest.Contact_Name = src.Contact.Name` Example: - +```csharp class Staff { public string Name { get; set; } public int GetAge() { return (DateTime.Now - this.BirthDate).TotalDays / 365.25; } @@ -57,6 +57,7 @@ Example: var dto = staff.Adapt(); //dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name +``` You can make custom mapping logic by - Setting custom mapping (https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) From fbeaafbab0e91a6d74999880513a7b77a35313d3 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 9 Sep 2018 21:14:01 +0700 Subject: [PATCH 060/363] Updated Config validation (markdown) --- ...n.md => Config-validation-&-compilation.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) rename Config-validation.md => Config-validation-&-compilation.md (55%) diff --git a/Config-validation.md b/Config-validation-&-compilation.md similarity index 55% rename from Config-validation.md rename to Config-validation-&-compilation.md index 3e0291ee..8908a6ba 100644 --- a/Config-validation.md +++ b/Config-validation-&-compilation.md @@ -25,3 +25,23 @@ Both a specific TypeAdapterConfig or all current configurat TypeAdapterConfig.NewConfig(); TypeAdapterConfig.NewConfig(); TypeAdapterConfig.GlobalSettings.Compile(); + +### Config Compilation +Mapster will automatically compile mapping for first time usage. + + var result = poco.Adapt(); + +However, you can explicitly compile mapping by `Compile` method. + + //Global config + TypeAdapterConfig.GlobalSettings.Compile(); + + //Config instance + var config = new TypeAdapterConfig(); + config.Compile(); + +After compile, when you change setting in config, it will generate errors. Therefore, make sure you finish configuration before calling `Compile`. + +Calling `Compile` method on start up has following benefits. +1. **Validate mapping**: as describe in previous section, `Compile` method helps you validate mapping. Calling on start up, help you detect problem on start-up time, not on run-time. +2. **Prevent compilation error on runtime**: Mapster is thread-safe for `Adapt` method after compilation. But not before compilation. In some corner case, you might got compilation error when calling `Adapt` method concurrently with `TypeAdapterConfig` setup. From c30f2b8413035ddcd64c7077731775784b23edc9 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 9 Sep 2018 21:14:29 +0700 Subject: [PATCH 061/363] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 725dbd06..fbb8a245 100644 --- a/Home.md +++ b/Home.md @@ -10,7 +10,7 @@ * [Config inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) * [Config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) * [Config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) -* [Config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation) +* [Config validation & compilation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) * [Config for nested classes](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes) ##### Settings From d649dba868ac89feed7c9b7a4a9d299f1fd04675 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 9 Sep 2018 21:14:47 +0700 Subject: [PATCH 062/363] Updated _Sidebar (markdown) --- _Sidebar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 725dbd06..fbb8a245 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -10,7 +10,7 @@ * [Config inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) * [Config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) * [Config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) -* [Config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation) +* [Config validation & compilation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) * [Config for nested classes](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes) ##### Settings From 7a92526853a194eba17f7f20d63c55dabc0c9a57 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 9 Sep 2018 22:00:24 +0700 Subject: [PATCH 063/363] Created FastExpressionCompiler (markdown) --- FastExpressionCompiler.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 FastExpressionCompiler.md diff --git a/FastExpressionCompiler.md b/FastExpressionCompiler.md new file mode 100644 index 00000000..233af77c --- /dev/null +++ b/FastExpressionCompiler.md @@ -0,0 +1,11 @@ +Need more speed? Let's compile with [FastExpressionCompiler](https://github.com/dadhi/FastExpressionCompiler). + +Getting the package + + PM> Install-Package FastExpressionCompiler + +Then add following code on start up + + TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileFast(); + +That's it. Now your code will enjoy more than 30% faster! \ No newline at end of file From 3fcd51d3db53e378300a19202dae2b78226acf4e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 9 Sep 2018 22:00:41 +0700 Subject: [PATCH 064/363] Updated Home (markdown) --- Home.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index fbb8a245..1165692e 100644 --- a/Home.md +++ b/Home.md @@ -29,4 +29,5 @@ ##### Plugins * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) * [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) -* [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) \ No newline at end of file +* [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) +* [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) \ No newline at end of file From 969e95e3fe6ae2657bae73f83ee6b9a3acfa5619 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 9 Sep 2018 22:00:59 +0700 Subject: [PATCH 065/363] Updated _Sidebar (markdown) --- _Sidebar.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index fbb8a245..1165692e 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -29,4 +29,5 @@ ##### Plugins * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) * [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) -* [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) \ No newline at end of file +* [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) +* [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) \ No newline at end of file From d4a125e6b2650ffa6e1b85ec2d5806249617aafb Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 9 Sep 2018 22:16:51 +0700 Subject: [PATCH 066/363] Updated FastExpressionCompiler (markdown) --- FastExpressionCompiler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FastExpressionCompiler.md b/FastExpressionCompiler.md index 233af77c..f607c7ad 100644 --- a/FastExpressionCompiler.md +++ b/FastExpressionCompiler.md @@ -8,4 +8,4 @@ Then add following code on start up TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileFast(); -That's it. Now your code will enjoy more than 30% faster! \ No newline at end of file +That's it. Now your code will enjoy performance boost 1.3 to 4.0 times faster! \ No newline at end of file From d8fec67df443b26a19a1932230bd75c2542d9681 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Tue, 29 Jan 2019 23:17:03 +0700 Subject: [PATCH 067/363] Updated Debugging (markdown) --- Debugging.md | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/Debugging.md b/Debugging.md index 6b9c691f..3a70ad50 100644 --- a/Debugging.md +++ b/Debugging.md @@ -1,34 +1,24 @@ ### Step-into debugging - PM> Install-Package Mapster.Diagnostics + PM> Install-Package ExpressionDebugger This plugin allow you to perform step-into debugging! -In order to step-into debugging, make sure you turn off just my code feature. -![turn off just my code](https://cloud.githubusercontent.com/assets/5763993/23740682/47608676-04d7-11e7-842d-77c18a459515.png) - ##### Usage -Just declare `EnableDebugging`, this command is working only in debug mode. Therefore, you can keep this flag turn on. But do not forget to build your application in release mode for production, otherwise, there will be huge performance impact. This command should be called before you start mapping. +Then add following code on start up (or anywhere before mapping is compiled) - TypeAdapterConfig.GlobalSettings.EnableDebugging(); + TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileFast(); -And whenever you would like to step into mapping function, you need to mark `MapsterDebugger.BreakOnEnterAdaptMethod` to `true`. You can turn on or off any time by coding or Immediate window. +Now on your mapping code. - MapsterDebugger.BreakOnEnterAdaptMethod = true; - var dto = poco.Adapt(); <--- After this line, you will step-into this function!! + var dto = poco.Adapt(); <--- you will step-into this function!! ![image](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) -##### Net Core support - -Currently, step-into debugging is working only in .NET 4.x. .NET Core doesn't yet support. - ##### Using internal classes or members -`private` and `protected` don't allow in debug mode. You can access to `internal`, but you have to add following attribute to your assembly. - - [assembly: InternalsVisibleTo("Mapster.Dynamic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ED6C7EE4B2E0AC62548FAA596F267469CD877990306F7C97ACBD889455E5D5846FFC5DCD5D87A72263DC7EB276629261C115DA3456485AE051D0C4EDBED3DD1A0092280F6408407D88FF3A76F2E6EECA4DE369E31A2A631052C99CAC2105B9A273969A66B076D5FC2B9B57F8E45D358906F58A0D959EA9BA2D725131A0E372AB")] +`private` and `protected` don't allow in debug mode. ### Get mapping script @@ -39,3 +29,11 @@ var script = poco.BuildAdapter() .CreateMapExpression() .ToScript(); ``` + +### Visual Studio for Mac +To step-into debugging, you might need to emit file +```CSharp +var opt = new ExpressionCompilationOptions { EmitFile = true }; +var func = lambda.CompileWithDebugInfo(opt); +func(); //<-- you can step-into this function!! +``` From 627b8b633f5dfb6cdc27d4ea4ba28232848c6a88 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Tue, 29 Jan 2019 23:40:19 +0700 Subject: [PATCH 068/363] Updated Debugging (markdown) --- Debugging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Debugging.md b/Debugging.md index 3a70ad50..0d60baf3 100644 --- a/Debugging.md +++ b/Debugging.md @@ -8,7 +8,7 @@ This plugin allow you to perform step-into debugging! Then add following code on start up (or anywhere before mapping is compiled) - TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileFast(); + TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(); Now on your mapping code. From 8683d7b444ae85ffb6b56b98a81b745925a7535c Mon Sep 17 00:00:00 2001 From: chaowlert Date: Tue, 29 Jan 2019 23:41:18 +0700 Subject: [PATCH 069/363] Updated Debugging (markdown) --- Debugging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Debugging.md b/Debugging.md index 0d60baf3..6a41ebd8 100644 --- a/Debugging.md +++ b/Debugging.md @@ -18,7 +18,7 @@ Now on your mapping code. ##### Using internal classes or members -`private` and `protected` don't allow in debug mode. +`private`, `protected` and `internal` don't allow in debug mode. ### Get mapping script From a2f7f94d6e59972074296aa7f3f9db38228e320c Mon Sep 17 00:00:00 2001 From: chaowlert Date: Wed, 30 Jan 2019 18:01:11 +0700 Subject: [PATCH 070/363] Updated Debugging (markdown) --- Debugging.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Debugging.md b/Debugging.md index 6a41ebd8..3a43120c 100644 --- a/Debugging.md +++ b/Debugging.md @@ -8,7 +8,8 @@ This plugin allow you to perform step-into debugging! Then add following code on start up (or anywhere before mapping is compiled) - TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(); + var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached }; + TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); Now on your mapping code. @@ -33,7 +34,7 @@ var script = poco.BuildAdapter() ### Visual Studio for Mac To step-into debugging, you might need to emit file ```CSharp -var opt = new ExpressionCompilationOptions { EmitFile = true }; +var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached, EmitFile = true }; var func = lambda.CompileWithDebugInfo(opt); func(); //<-- you can step-into this function!! ``` From a8c094984ad5616f9c1a8f2edfa7660c374e5439 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Wed, 30 Jan 2019 18:01:57 +0700 Subject: [PATCH 071/363] Updated Debugging (markdown) --- Debugging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Debugging.md b/Debugging.md index 3a43120c..fd03c617 100644 --- a/Debugging.md +++ b/Debugging.md @@ -11,7 +11,7 @@ Then add following code on start up (or anywhere before mapping is compiled) var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached }; TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); -Now on your mapping code. +Now on your mapping code (only in `DEBUG` mode). var dto = poco.Adapt(); <--- you will step-into this function!! From 2e0c75829c83a5389c74dd5f81d5b5209b7c7a20 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Mon, 11 Feb 2019 22:02:48 +0700 Subject: [PATCH 072/363] Updated Debugging (markdown) --- Debugging.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Debugging.md b/Debugging.md index fd03c617..5b1cbd1c 100644 --- a/Debugging.md +++ b/Debugging.md @@ -13,7 +13,7 @@ Then add following code on start up (or anywhere before mapping is compiled) Now on your mapping code (only in `DEBUG` mode). - var dto = poco.Adapt(); <--- you will step-into this function!! + var dto = poco.Adapt(); <--- you can step-into this function!! ![image](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) @@ -35,6 +35,7 @@ var script = poco.BuildAdapter() To step-into debugging, you might need to emit file ```CSharp var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached, EmitFile = true }; -var func = lambda.CompileWithDebugInfo(opt); -func(); //<-- you can step-into this function!! +TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); +... +var dto = poco.Adapt(); //<-- you can step-into this function!! ``` From 07a53f5a4f88305f8d8f0333a6ea57fddb8848eb Mon Sep 17 00:00:00 2001 From: wtfblub Date: Mon, 25 Feb 2019 12:44:33 +0100 Subject: [PATCH 073/363] Fix typo --- Object-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Object-references.md b/Object-references.md index 90e2c0bc..5c979939 100644 --- a/Object-references.md +++ b/Object-references.md @@ -8,7 +8,7 @@ When mapping objects with circular references, a stackoverflow exception will re NOTE: Projection doesn't support circular reference. To overcome, you might use `Adapt` instead of `ProjectToType`. - TypeAdaptConfig.GlobalSettings.Default.PreserveReference(true); + TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); var students = context.Student.Include(p => p.Schools).Adapt>(); NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. From bf678182d2a70f11a95901ac115792be3b99048d Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 5 Apr 2019 12:44:18 +0700 Subject: [PATCH 074/363] Updated Setting values (markdown) --- Setting-values.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Setting-values.md b/Setting-values.md index d22e193b..45790025 100644 --- a/Setting-values.md +++ b/Setting-values.md @@ -10,14 +10,19 @@ TypeAdapterConfig.NewConfig() ### Transform value While `Map` method specify logic for single property, `AddDestinationTransform` allows transforms for all items of a type, such as trimming all strings. But really any operation can be performed on the destination value before assignment. -```csharp - //Global - TypeAdapterConfig.GlobalSettings.Default.AddDestinationTransform((string x) => x.Trim()); - //Per mapping configuration - TypeAdapterConfig.NewConfig() +**Trim string** +```csharp +TypeAdapterConfig.NewConfig() .AddDestinationTransforms((string x) => x.Trim()); ``` + +**Null replacement** +```csharp +TypeAdapterConfig.NewConfig() + .AddDestinationTransforms((string x) => x ?? ""); +``` + ### Passing run-time value In some cases, you might would like to pass runtime values (ie, current user). On configuration, we can receive run-time value by `MapContext.Current.Parameters`. @@ -34,4 +39,5 @@ To pass run-time value, we need to use `BuildAdapter` method, and call `AddParam var dto = poco.BuildAdapter() .AddParameters("user", this.User.Identity.Name) .AdaptToType(); -``` \ No newline at end of file +``` + From c5b96ca66d4056df304f5aa09cdc9ae7857d1223 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 5 Apr 2019 13:11:40 +0700 Subject: [PATCH 075/363] Updated Setting by attributes (markdown) --- Setting-by-attributes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Setting-by-attributes.md b/Setting-by-attributes.md index 4fc954b1..50dbf3d2 100644 --- a/Setting-by-attributes.md +++ b/Setting-by-attributes.md @@ -20,7 +20,7 @@ You can ignore members annotated with any attributes by using the `IgnoreAttribu .IgnoreAttribute(typeof(JsonIgnoreAttribute)); ### AdaptMember attribute - +**Map to different name** With `AdaptMember` attribute, you can specify name of source or target to be mapped. For example, if we would like to map `Id` to `Code`. ``` @@ -31,8 +31,8 @@ public class Product { } ``` +**Map to non-public members** You can also map non-public members with `AdaptMember` attribute. - ``` public class Product { [AdaptMember] From 1ee0eb70935f7643897932e94f14acaa1c3c0c31 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 5 Apr 2019 13:20:50 +0700 Subject: [PATCH 076/363] Updated Rule based member mapping (markdown) --- Rule-based-member-mapping.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Rule-based-member-mapping.md b/Rule-based-member-mapping.md index d5926168..2c3d835e 100644 --- a/Rule-based-member-mapping.md +++ b/Rule-based-member-mapping.md @@ -27,8 +27,16 @@ If you would like to allow only properties not public field to be mapped, you ca Suppose you are working with EF, and you would like to skip all navigation properties. Then we will allow only short list of types. - TypeAdapterConfig.GlobalSettings.Default - .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); +**Allow by types** +``` +TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); +``` +**Allow by Namespaces** +``` +TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => !member.Type.Namespace.StartsWith("System")); +``` ### Allow internal members From e0fbdb1a4ee71a0bf90f3b12816cf868e93cc7d0 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 5 Apr 2019 13:24:50 +0700 Subject: [PATCH 077/363] Updated Mapping non public members (markdown) --- Mapping-non-public-members.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Mapping-non-public-members.md b/Mapping-non-public-members.md index 67a514a8..59a71c41 100644 --- a/Mapping-non-public-members.md +++ b/Mapping-non-public-members.md @@ -8,11 +8,6 @@ Or global! TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); -If you turn on at global level, you must also declare type pair to ensure Mapster will treat type as POCO rather than primitive. - - TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); - TypeAdapterConfig.NewConfig(); - ### AdaptMember attribute You can also map non-public members with `AdaptMember` attribute. @@ -42,3 +37,10 @@ With `IncludeMember`, you can select which access modifier to allow. .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal || member.AccessModifier == AccessModifier.ProtectedInternal); +### Note for non-public member mapping + +If type doesn't contain public properties, Mapster will treat type as primitive, you must also declare type pair to ensure Mapster will apply non-public member mapping. + + TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); + TypeAdapterConfig.NewConfig(); + From d4427adf4b8a77aad2de86494358e594f8526003 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 5 Apr 2019 13:27:44 +0700 Subject: [PATCH 078/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_Sidebar.md b/_Sidebar.md index 1165692e..65bc377c 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -25,6 +25,7 @@ * [Setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) * [Working with object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) * [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) +* [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) ##### Plugins * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) From 9de774c4f5ef458ca4add2d3c27d86b77a49f703 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 5 Apr 2019 13:28:07 +0700 Subject: [PATCH 079/363] Updated Home (markdown) --- Home.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Home.md b/Home.md index 1165692e..65bc377c 100644 --- a/Home.md +++ b/Home.md @@ -25,6 +25,7 @@ * [Setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) * [Working with object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) * [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) +* [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) ##### Plugins * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) From 0a737ec9a1c09fb6f11f031b984ce8e7bc7d9efc Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 13 Apr 2019 15:58:36 +0700 Subject: [PATCH 080/363] update wiki --- Basic-usages.md | 39 ++--- Before-after-mapping.md | 55 +++++++ CodeGen.md | 101 +++++++++++++ ...classes.md => Config-for-nested-mapping.md | 30 ++-- Config-inheritance.md | 37 +++-- Config-instance.md | 48 +++--- Config-location.md | 60 +++++--- Config-validation-&-compilation.md | 50 +++--- Configuration.md | 57 ++++--- Constructor-mapping.md | 67 +++++++++ Custom-conversion-logic.md | 34 +++-- Custom-mapping.md | 40 +++-- Data-types.md | 53 +++---- Debugging.md | 12 +- EF6.md | 15 +- FastExpressionCompiler.md | 4 +- Home.md | 142 +++++++++++++----- Ignoring-members.md | 44 +++--- Json.net.md | 4 +- Mappers.md | 22 ++- Mapping-non-public-members.md | 37 +++-- Naming-convention.md | 58 +++---- Object-creation-and-validation.md | 29 ---- Object-references.md | 40 +++-- Rule-based-member-mapping.md | 62 ++++---- Setting-by-attributes.md | 22 +-- Shallow-merge.md | 18 +++ Two-ways.md | 55 +++++++ _Sidebar.md | 15 +- 29 files changed, 879 insertions(+), 371 deletions(-) create mode 100644 Before-after-mapping.md create mode 100644 CodeGen.md rename Config-for-nested-classes.md => Config-for-nested-mapping.md (53%) create mode 100644 Constructor-mapping.md delete mode 100644 Object-creation-and-validation.md create mode 100644 Shallow-merge.md create mode 100644 Two-ways.md diff --git a/Basic-usages.md b/Basic-usages.md index df7f3e27..8fa56942 100644 --- a/Basic-usages.md +++ b/Basic-usages.md @@ -1,30 +1,35 @@ ### Mapping to a new object Mapster creates the destination object and maps values to it. - var destObject = sourceObject.Adapt(); +```csharp +var destObject = sourceObject.Adapt(); +``` ### Mapping to an existing object You make the object, Mapster maps to the object. - TDestination destObject = new TDestination(); - destObject = sourceObject.Adapt(destObject); +```csharp +sourceObject.Adapt(destObject); +``` ### Queryable Extensions Mapster also provides extensions to map queryables. - using (MyDbContext context = new MyDbContext()) - { - // Build a Select Expression from DTO - var destinations = context.Sources.ProjectToType().ToList(); - - // Versus creating by hand: - var destinations = context.Sources.Select(c => new Destination(){ - Id = p.Id, - Name = p.Name, - Surname = p.Surname, - .... - }) - .ToList(); - } +```csharp +using (MyDbContext context = new MyDbContext()) +{ + // Build a Select Expression from DTO + var destinations = context.Sources.ProjectToType().ToList(); + + // Versus creating by hand: + var destinations = context.Sources.Select(c => new Destination { + Id = p.Id, + Name = p.Name, + Surname = p.Surname, + .... + }) + .ToList(); +} +``` \ No newline at end of file diff --git a/Before-after-mapping.md b/Before-after-mapping.md new file mode 100644 index 00000000..24410ddd --- /dev/null +++ b/Before-after-mapping.md @@ -0,0 +1,55 @@ +### Before mapping action + +You can perform actions before mapping started by using `BeforeMapping` method. + +```csharp +TypeAdapterConfig.ForType() + .BeforeMapping((src, dest) => dest.Initialize()); +``` + +### After mapping action + +You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. + +```csharp +TypeAdapterConfig.ForType() + .AfterMapping((src, dest) => dest.Validate()); +``` + +Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. + +```csharp +TypeAdapterConfig.GlobalSettings.ForDestinationType() + .AfterMapping(dest => dest.Validate()); +``` +### Before & after mapping in code generation + +`BeforeMapping` and `AfterMapping` accept action which allowed you to pass multiple statements. In code generation, you might need to pass expression instead of action using `BeforeMappingInline` and `AfterMappingInline`, expression can be translated into code, but action cannot. + +#### Single line statement + +For single line statement, you can directly change from `BeforeMapping` and `AfterMapping` to `BeforeMappingInline` and `AfterMappingInline`. + +```csharp +TypeAdapterConfig.GlobalSettings.ForDestinationType() + .AfterMappingInline(dest => dest.Validate()); +``` + +#### Multiple statements + +For multiple statements, you need to declare a method for actions. + +```csharp +public static void Validate(Dto dto) { + action1(dto); + action2(dto); + ... +} +``` + +Then you can reference the method to `BeforeMappingInline` and `AfterMappingInline`. + +```csharp +TypeAdapterConfig.GlobalSettings.ForDestinationType() + .AfterMappingInline(dest => PocoToDtoMapper.Validate(dest)); +``` diff --git a/CodeGen.md b/CodeGen.md new file mode 100644 index 00000000..be2c11a1 --- /dev/null +++ b/CodeGen.md @@ -0,0 +1,101 @@ +### Code generation +``` + PM> Install-Package ExpressionTranslator +``` + +Although Mapster allow you to [step-into debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging), but all mapping are generated at runtime. Therefore, error will be captured at runtime, and we also lose the power of static analysis to find the usage. + +Here are steps to add code generation. + +1. Create text template + +![image](https://user-images.githubusercontent.com/5763993/56052976-f9377580-5d7c-11e9-841c-0a911fdb3a7f.png) + +2. In template, add references & mapping logic + +```xml +<#@ template debug="true" language="C#" #> +<#@ output extension=".g.cs" #> +<#@ Assembly Name="netstandard" #> +<#@ Assembly Name="System.Core" #> +<#@ Assembly Name="System.Runtime" #> +<#@ Assembly Name="System.Linq.Expressions" #> +<#@ Assembly Name="$(TargetDir)/$(ProjectName).dll" #> +<#@ Assembly Name="$(TargetDir)/Mapster.dll" #> +<#@ Assembly Name="$(TargetDir)/ExpressionTranslator.dll" #> +<#@ import namespace="ExpressionDebugger" #> +<#@ import namespace="Mapster" #> +<#@ import namespace="YourNamespace" #> +``` +```csharp +<# + //this line is to generate all nested mapping in 1 file + TypeAdapterConfig.GlobalSettings.SelfContainedCodeGeneration = true; + var cust = default(Customer); + var def = new ExpressionDefinitions + { + IsStatic = true, //change to false if you want instance + MethodName = "Map", + Namespace = "YourNamespace", + TypeName = "CustomerMapper" + }; + var code = cust.BuildAdapter() + .CreateMapExpression() + .ToScript(def); + WriteLine(code); +#> +``` + +3. Generate code by right click on template file, and select `Run Custom Tool`. + +That's it. Done! + +--- + +## Information + +### Links + +- Example: [CustomerMapper]( +https://github.com/MapsterMapper/Mapster/blob/master/src/Benchmark/CustomerMapper.tt) +- [T4 Documentation](https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2019) (Microsoft) + +### Q & A + +Q: How to pass lambda to Before/After mapping? +A: Please use `BeforeMappingInline` and `AfterMappingInline` instead. [link](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) + +Q: Can I generate multiple outputs from single template? +A: You can. [link](https://stackoverflow.com/questions/33575419/how-to-create-multiple-output-files-from-a-single-t4-template-using-tangible-edi) + +Q: After running template file, it said library XXX not found. +A: Some unused libraries will be excluded during build. You can direct reference to dll in template file. Or tell Visual Studio to copy all reference libraries to output. [link](https://stackoverflow.com/questions/43837638/how-to-get-net-core-projects-to-copy-nuget-references-to-build-output/43841481) + +```xml + +true + + +true +``` + +Q: After running template file on Mac, it said `netstandard` is not found. +A: You need direct reference. + +```xml + +<#@ Assembly Name="netstandard" #> + + +<#@ Assembly Name="/usr/local/share/dotnet/sdk/2.2.103/Microsoft/Microsoft.NET.Build.Extensions/net461/lib/netstandard.dll" #> +``` + +Q: After running template file in .NET Core project on Windows, it said, System.Runtime version 4.2.x.x not found. +A: You can build using .NET Framework version. Otherwise, you need to update assembly binding in Visual Studio config file. [link](https://stackoverflow.com/questions/51550265/t4-template-could-not-load-file-or-assembly-system-runtime-version-4-2-0-0) + +Q: After running template file, it said Compile items are duplicated. +A: You can set property to skip generated items. + +```xml +**/*.g.cs +``` \ No newline at end of file diff --git a/Config-for-nested-classes.md b/Config-for-nested-mapping.md similarity index 53% rename from Config-for-nested-classes.md rename to Config-for-nested-mapping.md index 4b416596..eed2c801 100644 --- a/Config-for-nested-classes.md +++ b/Config-for-nested-mapping.md @@ -1,6 +1,6 @@ Configuration is per type pair, not per type hierarchy. For example if you have parent and child classes. -``` +```csharp class ParentPoco { public string Id { get; set; } @@ -19,24 +19,34 @@ class GrandChildPoco And if you have setting on parent type. -``` -TypeAdapterConfig.NewConfig().PreserveReference(true); +```csharp +TypeAdapterConfig.NewConfig() + .PreserveReference(true); ``` When mapping, child type will not get effect from `PreserveReference`. To do so, you must specify all type pairs inside `ParentPoco`. - TypeAdapterConfig.NewConfig().PreserveReference(true); - TypeAdapterConfig.NewConfig().PreserveReference(true); - TypeAdapterConfig.NewConfig().PreserveReference(true); +```csharp +TypeAdapterConfig.NewConfig() + .PreserveReference(true); +TypeAdapterConfig.NewConfig() + .PreserveReference(true); +TypeAdapterConfig.NewConfig() + .PreserveReference(true); +``` Or you can set `PreserveReference` in global setting. - TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); +```csharp +TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); +``` If you don't want to set config in global setting, you can also use `Fork`. - var forked = TypeAdapterConfig.GlobalSettings.Fork(config => config.Default.PreserveReference(true)); - var parentDto = parentPoco.Adapt(forked); - +```csharp +var forked = TypeAdapterConfig.GlobalSettings.Fork(config => + config.Default.PreserveReference(true)); +var parentDto = parentPoco.Adapt(forked); +``` diff --git a/Config-inheritance.md b/Config-inheritance.md index 12c8908e..21ce8365 100644 --- a/Config-inheritance.md +++ b/Config-inheritance.md @@ -2,37 +2,50 @@ Type mappings will automatically inherit for source types. Ie. if you set up following config. - TypeAdapterConfig.NewConfig() - .Map(dest => dest.Name, src => src.Name + "_Suffix"); +```csharp +TypeAdapterConfig.NewConfig() + .Map(dest => dest.Name, src => src.Name + "_Suffix"); +``` A derived type of `SimplePoco` will automatically apply the base mapping config. - var dest = TypeAdapter.Adapt(src); //dest.Name = src.Name + "_Suffix" +```csharp +var dest = TypeAdapter.Adapt(src); +//dest.Name = src.Name + "_Suffix" +``` If you don't wish a derived type to use the base mapping, you can turn off by `AllowImplicitSourceInheritance` - TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = false; +```csharp +TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = false; +``` And by default, Mapster will not inherit destination type mappings. You can turn on by `AllowImplicitDestinationInheritance`. - TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; +```csharp +TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; +``` ### Explicit inheritance You can copy setting from based type explicitly. - TypeAdapterConfig.NewConfig() +```csharp +TypeAdapterConfig.NewConfig() .Inherits(); +``` ### Include derived types You can also include derived type to the based type declaration. For example: - TypeAdapterConfig.NewConfig() - .Include(); +```csharp +TypeAdapterConfig.NewConfig() + .Include(); - Vehicle vehicle = new Car { Id = 1, Name = "Car", Make = "Toyota" }; - var dto = vehicle.Adapt(); +Vehicle vehicle = new Car { Id = 1, Name = "Car", Make = "Toyota" }; +var dto = vehicle.Adapt(); - dto.ShouldBeOfType(); - ((CarDto)dto).Make.ShouldBe("Toyota"); //Make property doesn't exist in Vehicle +dto.ShouldBeOfType(); +((CarDto)dto).Make.ShouldBe("Toyota"); //Make property doesn't exist in Vehicle +``` diff --git a/Config-instance.md b/Config-instance.md index bb3a7481..4b45ebdd 100644 --- a/Config-instance.md +++ b/Config-instance.md @@ -3,45 +3,59 @@ You may wish to have different settings in different scenarios. If you would not like to apply setting at a static level, Mapster also provides setting instance configurations. - var config = new TypeAdapterConfig(); - config.Default.Ignore("Id"); +```csharp +var config = new TypeAdapterConfig(); +config.Default.Ignore("Id"); +``` For instance configurations, you can use the same `NewConfig` and `ForType` methods that are used at the global level with the same behavior: `NewConfig` drops any existing configuration and `ForType` creates or enhances a configuration. - config.NewConfig() - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); +```csharp +config.NewConfig() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); - config.ForType() - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); +config.ForType() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); +``` You can apply a specific config instance by passing it to the `Adapt` method. (NOTE: please reuse your config instance to prevent recompilation) - var result = src.Adapt(config); +```csharp +var result = src.Adapt(config); +``` Or to an Adapter instance. - var adapter = new Adapter(config); - var result = adapter.Adapt(src); +```csharp +var adapter = new Adapter(config); +var result = adapter.Adapt(src); +``` ### Clone If you would like to create configuration instance from existing configuration, you can use `Clone` method. For example, if you would like to clone from global setting. - var newConfig = TypeAdapterConfig.GlobalSettings.Clone(); +```csharp +var newConfig = TypeAdapterConfig.GlobalSettings.Clone(); +``` Or clone from existing configuration instance - var newConfig = oldConfig.Clone(); +```csharp +var newConfig = oldConfig.Clone(); +``` ### Fork `Fork` is similar to `Clone`, but `Fork` will allow you to keep configuration and mapping in the same location. See (https://github.com/MapsterMapper/Mapster/wiki/Config-location) for more info. - var forked = mainConfig.Fork(config => - config.ForType() - .Map(dest => dest.code, src => src.Id)); +```csharp +var forked = mainConfig.Fork(config => + config.ForType() + .Map(dest => dest.code, src => src.Id)); - var dto = poco.Adapt(forked); \ No newline at end of file +var dto = poco.Adapt(forked); +``` diff --git a/Config-location.md b/Config-location.md index 71e4b674..8f17fd22 100644 --- a/Config-location.md +++ b/Config-location.md @@ -2,20 +2,22 @@ Configuration should be set only once and reuse for mapping. Therefore, we should not keep configuration and mapping in the same location. For example: - config.ForType().Ignore("Id"); - var dto1 = poco1.Adapt(config); +```csharp +config.ForType().Ignore("Id"); +var dto1 = poco1.Adapt(config); - config.ForType().Ignore("Id"); <--- Exception occurred here, because config was already compiled - var dto2 = poco2.Adapt(config); +config.ForType().Ignore("Id"); //<--- Exception occurred here, because config was already compiled +var dto2 = poco2.Adapt(config); +``` Therefore, you should separate configuration and mapping. Configuration should keep in entry point such as `Main` function or `Global.asax.cs` or `Startup.cs`. -``` +```csharp // Application_Start in Global.asax.cs config.ForType().Ignore("Id"); ``` -``` +```csharp // in Controller class var dto1 = poco1.Adapt(config); var dto2 = poco2.Adapt(config); @@ -25,8 +27,10 @@ var dto2 = poco2.Adapt(config); Problem with separating configuration and mapping is, coding will be separated into 2 locations. You might remove or alter mapping, and you can forget to update the configuration. `Fork` method allow you to keep config and mapping inline. - var dto = poco.Adapt( - config.Fork(forked => forked.ForType().Ignore("Id")); +```csharp +var dto = poco.Adapt( + config.Fork(forked => forked.ForType().Ignore("Id")); +``` Don't worry about performance, forked config will be compiled only once. When mapping occurs for the second time, `Fork` function will return config from cache. @@ -34,7 +38,7 @@ Don't worry about performance, forked config will be compiled only once. When ma `Fork` method uses filename and line number and the key. But if you use `Fork` method inside generic class or method, you must specify your own key (with all type names) to prevent `Fork` to return invalid config from different type arguments. -``` +```csharp IQueryable GetItems() { var forked = config.Fork( @@ -55,34 +59,44 @@ It can be helpful to allow assemblies to be scanned for these rules so you have Assembly scanning is simple, just create any number of `IRegister` implementations in your assembly, then call `Scan` from your `TypeAdapterConfig` class: - public class MyRegister : IRegister +```csharp +public class MyRegister : IRegister +{ + public void Register(TypeAdapterConfig config) { - public void Register(TypeAdapterConfig config) - { - config.NewConfig(); + config.NewConfig(); - //OR to create or enhance an existing configuration - config.ForType(); - } + //OR to create or enhance an existing configuration + config.ForType(); } +} +``` To scan and register at the Global level: - TypeAdapterConfig.GlobalSettings.Scan(assembly1, assembly2, assemblyN) +```csharp +TypeAdapterConfig.GlobalSettings.Scan(assembly1, assembly2, assemblyN) +``` For a specific config instance: - var config = new TypeAdapterConfig(); - config.Scan(assembly1, assembly2, assemblyN); +```csharp +var config = new TypeAdapterConfig(); +config.Scan(assembly1, assembly2, assemblyN); +``` #### Apply method If you use other assembly scanning library such as MEF, you can easily apply registration with `Apply` method. - var registers = container.GetExports(); - config.Apply(registers); +```csharp +var registers = container.GetExports(); +config.Apply(registers); +``` `Apply` method also allow you to selectively pick from one or more `IRegister` rather than every `IRegister` in assembly. - var register = new MockingRegister(); - config.Apply(register); +```csharp +var register = new MockingRegister(); +config.Apply(register); +``` diff --git a/Config-validation-&-compilation.md b/Config-validation-&-compilation.md index 8908a6ba..c3fa9d9e 100644 --- a/Config-validation-&-compilation.md +++ b/Config-validation-&-compilation.md @@ -3,42 +3,52 @@ To validate your mapping in unit tests and in order to help with "Fail Fast" sit ### Explicit Mapping Forcing all classes to be explicitly mapped: - //Default is "false" - TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; - //This means you have to have an explicit configuration for each class, even if it's just: - TypeAdapterConfig.NewConfig(); +```csharp +//Default is "false" +TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; +//This means you have to have an explicit configuration for each class, even if it's just: +TypeAdapterConfig.NewConfig(); +``` ### Checking Destination Member Forcing all destination properties to have a corresponding source member or explicit mapping/ignore: - //Default is "false" - TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; +```csharp +//Default is "false" +TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; +``` ### Validating Mappings Both a specific TypeAdapterConfig or all current configurations can be validated. In addition, if Explicit Mappings (above) are enabled, it will also include errors for classes that are not registered at all with the mapper. - //Validate a specific config - var config = TypeAdapterConfig.NewConfig(); - config.Compile(); +```csharp +//Validate a specific config +var config = TypeAdapterConfig.NewConfig(); +config.Compile(); - //Validate globally - TypeAdapterConfig.NewConfig(); - TypeAdapterConfig.NewConfig(); - TypeAdapterConfig.GlobalSettings.Compile(); +//Validate globally +TypeAdapterConfig.NewConfig(); +TypeAdapterConfig.NewConfig(); +TypeAdapterConfig.GlobalSettings.Compile(); +``` ### Config Compilation Mapster will automatically compile mapping for first time usage. - var result = poco.Adapt(); +```csharp +var result = poco.Adapt(); +``` However, you can explicitly compile mapping by `Compile` method. - //Global config - TypeAdapterConfig.GlobalSettings.Compile(); - - //Config instance - var config = new TypeAdapterConfig(); - config.Compile(); +```csharp +//Global config +TypeAdapterConfig.GlobalSettings.Compile(); + +//Config instance +var config = new TypeAdapterConfig(); +config.Compile(); +``` After compile, when you change setting in config, it will generate errors. Therefore, make sure you finish configuration before calling `Compile`. diff --git a/Configuration.md b/Configuration.md index 2db49a15..8234c0e5 100644 --- a/Configuration.md +++ b/Configuration.md @@ -3,19 +3,23 @@ You can easily create settings for a type mapping by using: `TypeAdapterConfig.NewConfig()`. When `NewConfig` is called, any previous configuration for this particular TSource => TDestination mapping is dropped. - TypeAdapterConfig - .NewConfig() - .Ignore(dest => dest.Age) - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); +```csharp +TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.Age) + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); +``` As an alternative to `NewConfig`, you can use `ForType` in the same way: - TypeAdapterConfig - .ForType() - .Ignore(dest => dest.Age) - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); +```csharp +TypeAdapterConfig + .ForType() + .Ignore(dest => dest.Age) + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); +``` `ForType` differs in that it will create a new mapping if one doesn't exist, but if the specified TSource => TDestination mapping does already exist, it will enhance the existing mapping instead of dropping and replacing it. @@ -24,31 +28,41 @@ mapping does already exist, it will enhance the existing mapping instead of drop Use global settings to apply policies to all mappings. - TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); +```csharp +TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); +``` Then for individual type mappings, you can easily override the global setting(s). - TypeAdapterConfig.NewConfig().PreserveReference(false); +```csharp +TypeAdapterConfig.NewConfig().PreserveReference(false); +``` ### Rule based settings To set the setting at a more granular level. You can use the `When` method in global settings. In the example below, when any source type and destination type are the same, we will not the copy the `Id` property. - TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType) - .Ignore("Id"); +```csharp +TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType) + .Ignore("Id"); +``` In this example, the config would only apply to Query Expressions (projections). - TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection) - .IgnoreAttribute(typeof(NotMapAttribute)); +```csharp +TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection) + .IgnoreAttribute(typeof(NotMapAttribute)); +``` ### Destination type only Setting also be able to done without knowing the source type, by using `ForDestinationType`. For example, you can do `AfterMapping` setting to validate after mapping. - TypeAdapterConfig.GlobalSettings.ForDestinationType() - .AfterMapping(dest => dest.Validate()); +```csharp +TypeAdapterConfig.GlobalSettings.ForDestinationType() + .AfterMapping(dest => dest.Validate()); +``` NOTE: `ForDestinationType` above will always apply to all types assignable to `IValidator`. If destination class implements `IValidator`, it will also apply the `AfterMapping` config. @@ -56,6 +70,7 @@ NOTE: `ForDestinationType` above will always apply to all types assignable to `I If mapping type is generic, you can create setting by passing generic type definition to `ForType`. - TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) - .Map("value", "Value"); - +```csharp +TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) + .Map("value", "Value"); +``` diff --git a/Constructor-mapping.md b/Constructor-mapping.md new file mode 100644 index 00000000..71592f5a --- /dev/null +++ b/Constructor-mapping.md @@ -0,0 +1,67 @@ +### Custom Destination Object Creation + +You can provide a function call to create your destination objects instead of using the default object creation +(which expects an empty constructor). To do so, use the `ConstructUsing` method when configuring. This method expects +a function that will provide the destination instance. You can call your own constructor, a factory method, +or anything else that provides an object of the expected type. +```csharp +//Example using a non-default constructor +TypeAdapterConfig.NewConfig() + .ConstructUsing(src => new TDestination(src.Id, src.Name)); + +//Example using an object initializer +TypeAdapterConfig.NewConfig() + .ConstructUsing(src => new TDestination{Unmapped = "unmapped"}); +``` + +### Map to constructor + +By default, Mapster will only map to fields and properties. You can configure to map to constructors by `MapToConstructor`. + +```csharp +//global level +TypeAdapterConfig.GlobalSettings.Default.MapToConstructor(true); + +//type pair +TypeAdapterConfig.NewConfig().MapToConstructor(true); +``` + +To define custom mapping, you need to use Pascal case. + +```csharp +class Poco { + public string Id { get; set; } + ... +} +class Dto { + public Dto(string code, ...) { + ... + } +} +``` +```csharp +TypeAdapterConfig.NewConfig() + .MapToConstructor(true) + .Map('Code', 'Id'); //use Pascal case +``` + +If a class has 2 or more constructors, Mapster will automatically select largest number of parameters that satisfy mapping. + +```csharp +class Poco { + public int Foo { get; set; } + public int Bar { get; set; } +} +class Dto { + public Dto(int foo) { ... } + public Dto(int foo, int bar) { ...} //<-- Mapster will use this constructor + public Dto(int foo, int bar, int baz) { ... } +} +``` +Or you can also explicitly pass ConstructorInfo to the method. + +```csharp +var ctor = typeof(Dto).GetConstructor(new[] { typeof(int), typeof(int) }); +TypeAdapterConfig.NewConfig() + .MapToConstructor(ctor); +``` diff --git a/Custom-conversion-logic.md b/Custom-conversion-logic.md index 4a011d3b..c00d3322 100644 --- a/Custom-conversion-logic.md +++ b/Custom-conversion-logic.md @@ -2,27 +2,35 @@ In some cases, you may want to have complete control over how an object is mapped. You can register specific transformations using the `MapWith` method. - //Example of transforming string to char[]. - TypeAdapterConfig.NewConfig() - .MapWith(str => str.ToCharArray()); +```csharp +//Example of transforming string to char[]. +TypeAdapterConfig.NewConfig() + .MapWith(str => str.ToCharArray()); +``` `MapWith` also useful if you would like to copy instance rather than deep copy the object, for instance, `JObject` or `DbGeography`, these should treat as primitive types rather than POCO. - TypeAdapterConfig.NewConfig() - .MapWith(json => json); + ```csharp +TypeAdapterConfig.NewConfig() + .MapWith(json => json); +``` In case you would like to combine `MapWith` with other settings, for example, `PreserveReference`, `Include`, or `AfterMapping`, you can pass `applySettings` to true. - TypeAdapterConfig.NewConfig() - .PreserveReference(true) - .MapWith(poco => poco.ToDto(), applySettings: true); +```csharp +TypeAdapterConfig.NewConfig() + .PreserveReference(true) + .MapWith(poco => poco.ToDto(), applySettings: true); +``` ### Custom mapping data to existing object You can control mapping to existing object logic by `MapToTargetWith`. For example, you can copy data to existing array. - TypeAdapterConfig.NewConfig() - .MapToTargetWith((src, dest) => Array.Copy(src, dest, src.Length)); +```csharp +TypeAdapterConfig.NewConfig() + .MapToTargetWith((src, dest) => Array.Copy(src, dest, src.Length)); +``` NOTE: if you set `MapWith` setting but no `MapToTargetWith` setting, Mapster will use logic from `MapWith` setting. @@ -30,5 +38,7 @@ NOTE: if you set `MapWith` setting but no `MapToTargetWith` setting, Mapster wil You might not need to specify custom mapping logic completely. You can let Mapster do the mapping, and you do logic where Mapster cannot cover by using `AfterMapping`. - TypeAdapterConfig.NewConfig() - .AfterMapping((src, dest) => SpecialSetFn(src, dest)); +```csharp +TypeAdapterConfig.NewConfig() + .AfterMapping((src, dest) => SpecialSetFn(src, dest)); +``` diff --git a/Custom-mapping.md b/Custom-mapping.md index 6cf1dd57..fdb34a0c 100644 --- a/Custom-mapping.md +++ b/Custom-mapping.md @@ -2,28 +2,34 @@ You can customize how Mapster maps values to a property. - TypeAdapterConfig - .NewConfig() - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); +```csharp +TypeAdapterConfig + .NewConfig() + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); +``` You can even map when source and destination property types are different. - TypeAdapterConfig - .NewConfig() - .Map(dest => dest.Gender, //Genders.Male or Genders.Female - src => src.GenderString); //"Male" or "Female" +```csharp +TypeAdapterConfig + .NewConfig() + .Map(dest => dest.Gender, //Genders.Male or Genders.Female + src => src.GenderString); //"Male" or "Female" +``` ### Mapping with condition The Map configuration can accept a third parameter that provides a condition based on the source. If the condition is not met, Mapster will retry with next conditions. Default condition should be added at the end without specifying condition. If you do not specify default condition, null or default value will be assigned. - TypeAdapterConfig - .NewConfig() - .Map(dest => dest.FullName, src => "Sig. " + src.FullName, srcCond => srcCond.Country == "Italy") - .Map(dest => dest.FullName, src => "Sr. " + src.FullName, srcCond => srcCond.Country == "Spain") - .Map(dest => dest.FullName, src => "Mr. " + src.FullName); +```csharp +TypeAdapterConfig + .NewConfig() + .Map(dest => dest.FullName, src => "Sig. " + src.FullName, srcCond => srcCond.Country == "Italy") + .Map(dest => dest.FullName, src => "Sr. " + src.FullName, srcCond => srcCond.Country == "Spain") + .Map(dest => dest.FullName, src => "Mr. " + src.FullName); +``` NOTE: if you would like to skip mapping, when condition is met, you can use `IgnoreIf` (https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members#ignore-conditionally). @@ -31,8 +37,10 @@ NOTE: if you would like to skip mapping, when condition is met, you can use `Ign `Map` command can map to private member by specify name of the members. - TypeAdapterConfig - .NewConfig() - .Map("PrivateDestName", "PrivateSrcName"); +```csharp +TypeAdapterConfig + .NewConfig() + .Map("PrivateDestName", "PrivateSrcName"); +``` For more information about mapping non-public members, please see https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members. diff --git a/Data-types.md b/Data-types.md index 4d926953..d7517739 100644 --- a/Data-types.md +++ b/Data-types.md @@ -2,7 +2,7 @@ Converting between primitive types (ie. int, bool, double, decimal) is supported, including when those types are nullable. For all other types, if you can cast types in c#, you can also cast in Mapster. ```csharp - decimal i = 123.Adapt(); //equal to (decimal)123; +decimal i = 123.Adapt(); //equal to (decimal)123; ``` ### Enums @@ -11,27 +11,27 @@ The default Enum.ToString() in .Net is quite slow. The implementation in Mapster In Mapster, flagged enums are also supported. ```csharp - var e = "Read, Write, Delete".Adapt(); - //FileShare.Read | FileShare.Write | FileShare.Delete +var e = "Read, Write, Delete".Adapt(); +//FileShare.Read | FileShare.Write | FileShare.Delete ``` For enum to enum with different type, by default, Mapster will map enum by value. You can override to map enum by name by: ```csharp - TypeAdapterConfig.GlobalSettings.Default - .EnumMappingStrategy(EnumMappingStrategy.ByName); +TypeAdapterConfig.GlobalSettings.Default + .EnumMappingStrategy(EnumMappingStrategy.ByName); ``` ### Strings When Mapster maps other types to string, Mapster will use `ToString` method. And when ever Mapster maps string to the other types, Mapster will use `Parse` method. ```csharp - var s = 123.Adapt(); //equal to 123.ToString(); - var i = "123".Adapt(); //equal to int.Parse("123"); +var s = 123.Adapt(); //equal to 123.ToString(); +var i = "123".Adapt(); //equal to int.Parse("123"); ``` ### Collections This includes mapping among lists, arrays, collections, dictionary including various interfaces: IList, ICollection, IEnumerable etc... ```csharp - var list = db.Pocos.ToList(); - var target = list.Adapt>(); +var list = db.Pocos.ToList(); +var target = list.Adapt>(); ``` ### Mappable Objects @@ -42,34 +42,29 @@ Mapster can map 2 different objects using the following rules Example: ```csharp - class Staff { - public string Name { get; set; } - public int GetAge() { return (DateTime.Now - this.BirthDate).TotalDays / 365.25; } - public Staff Supervisor { get; set; } - ... +class Staff { + public string Name { get; set; } + public int GetAge() { + return (DateTime.Now - this.BirthDate).TotalDays / 365.25; } + public Staff Supervisor { get; set; } + ... +} - struct StaffDto { - public string Name { get; set; } - public int Age { get; set; } - public string SupervisorName { get; set; } - } +struct StaffDto { + public string Name { get; set; } + public int Age { get; set; } + public string SupervisorName { get; set; } +} - var dto = staff.Adapt(); - //dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name +var dto = staff.Adapt(); +//dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name ``` -You can make custom mapping logic by -- Setting custom mapping (https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) -- Setting custom naming convention (https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) -- Setting by attributes (https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) -- Ignoring members (https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) -- Setting rule based mapping (https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) - Mappable Object types are included: - POCO classes - POCO structs -- Dictionary type implement IDictionary +- Dictionary type implement `IDictionary` - Record types Example for object to dictionary diff --git a/Debugging.md b/Debugging.md index 5b1cbd1c..b357a893 100644 --- a/Debugging.md +++ b/Debugging.md @@ -8,12 +8,16 @@ This plugin allow you to perform step-into debugging! Then add following code on start up (or anywhere before mapping is compiled) - var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached }; - TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); +```csharp +var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached }; +TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); +``` Now on your mapping code (only in `DEBUG` mode). - var dto = poco.Adapt(); <--- you can step-into this function!! +```csharp +var dto = poco.Adapt(); //<--- you can step-into this function!! +``` ![image](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) @@ -33,7 +37,7 @@ var script = poco.BuildAdapter() ### Visual Studio for Mac To step-into debugging, you might need to emit file -```CSharp +```csharp var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached, EmitFile = true }; TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); ... diff --git a/EF6.md b/EF6.md index 6680f9ba..9bcf8fa9 100644 --- a/EF6.md +++ b/EF6.md @@ -4,20 +4,21 @@ In EntityFramework, when we got data from dto, and we would like to map data to Sample -``` +```csharp var poco = dto.BuildAdapter() - .CreateEntityFromContext(db) - .AdaptToType(); + .CreateEntityFromContext(db) + .AdaptToType(); ``` NOTE: If your data contains complex graph, it would be better to dump related records in single command to store first. This is to avoid plugin from downloading records one-by-one, which should be slower. -``` +```csharp //just make the query, objects will be cached in store -db.DomainPoco.Include("Children").Where(item => item.Id == dto.Id).FirstOrDefault(); +db.DomainPoco.Include("Children") + .Where(item => item.Id == dto.Id).FirstOrDefault(); //when mapping, Mapster will get object from cache rather than lookup in db var poco = dto.BuildAdapter() - .CreateEntityFromContext(db) - .AdaptToType(); + .CreateEntityFromContext(db) + .AdaptToType(); ``` diff --git a/FastExpressionCompiler.md b/FastExpressionCompiler.md index f607c7ad..d39c9fb5 100644 --- a/FastExpressionCompiler.md +++ b/FastExpressionCompiler.md @@ -6,6 +6,8 @@ Getting the package Then add following code on start up - TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileFast(); +```csharp +TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileFast(); +``` That's it. Now your code will enjoy performance boost 1.3 to 4.0 times faster! \ No newline at end of file diff --git a/Home.md b/Home.md index 65bc377c..4d6af7a3 100644 --- a/Home.md +++ b/Home.md @@ -1,34 +1,108 @@ -### Getting Started - -##### Mapping -* [Basic usages](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) -* [3 kind of mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) -* [Data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) - -##### Configuration -* [Configuration](https://github.com/MapsterMapper/Mapster/wiki/Configuration) -* [Config inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) -* [Config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) -* [Config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) -* [Config validation & compilation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) -* [Config for nested classes](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes) - -##### Settings -* Custom member matching logic - * [Custom mapping](https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) - * [Custom naming convention](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) - * [Setting by attributes](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) - * [Ignoring members](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) - * [Rule-based member matching](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) - * [Mapping non-public members](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) -* [Object creation & validation](https://github.com/MapsterMapper/Mapster/wiki/Object-creation-and-validation) -* [Setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) -* [Working with object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) -* [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) -* [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) - -##### Plugins -* [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) -* [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) -* [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) -* [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) \ No newline at end of file +# References + +### Basic + +| Method | Description | Link | +| ------------- |-----------------------| ----- | +| `src.Adapt()` | Mapping to new type | [basic](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) | +| `src.Adapt(dest)` | Mapping to existing object | [basic](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) | +| `query.ProjectToType()` | Mapping from queryable | [basic](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) | +| | Convention & Data type support | [data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) | + +### Mapper instance +| Method | Description | Link | +| ------------- |-----------------------| ----- | +| `IAdapter adapter = new Adapter()` | Create mapper instance | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | +| `adapter.Adapt(src)` | Mapping to new type | | +| `adapter.Adapt(src, dest)` | Mapping to existing object | | + +#### Builder +| Method | Description | Link | +| ------------- |-----------------------| ----- | +| `src.BuildAdapter()` | Create builder | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | +| `.ForkConfig(config => ...)` | Inline configuration | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | +| `.AddParameters(name, value)` | Passing runtime value | [setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) | +| `.AdaptToType()` | Mapping to new type | | +| `.AdaptTo(dest)` | Mapping to existing object | | +| `.CreateMapExpression()` | Get mapping expression | | +| `.CreateMapToTargetExpression()` | Get mapping to existing object expression | | +| `.CreateProjectionExpression()` | Get mapping from queryable expression | | + +#### Config +| Method | Description | Link | +| ------------- |-----------------------| ----- | +| `TypeAdapterConfig.GlobalSettings` | Global config | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `var config = new TypeAdapterConfig()` | Create new config instance | [config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) | +| `src.Adapt(config)` | Passing config to mapping | | +| `new Adapter(config)` | Passing config to mapper instance | | +| `src.BuildAdapter(config)` | Passing config to builder | | +| `config.RequireDestinationMemberSource` | Validate all properties are mapped | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | +| `config.RequireExplicitMapping` | Validate all type pairs are defined | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | +| `config.AllowImplicitDestinationInheritance` | Use config from destination based class | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | +| `config.AllowImplicitSourceInheritance` | Use config from source based class | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | +| `config.SelfContainedCodeGeneration` | Generate all nested mapping in 1 method | [code gen](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) | +| `config.Compile()` | Validate mapping instruction & cache | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | +| `config.CompileProjection()` | Validate mapping instruction & cache for queryable | | +| `config.Clone()` | Copy config | [config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) | +| `config.Fork(forked => ...)` | Inline configuration | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | + +#### Config scanning + +| Method | Description | Link | +| ------------- |-----------------------| ----- | +| `IRegister` | Interface for config scanning | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | +| `config.Scan(...assemblies)` | Scan for config in assemblies | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | +| `config.Apply(...registers)` | Apply registers directly | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | + +#### Declare settings + +| Method | Description | Link | +| ------------- |-----------------------| ----- | +| `config.Default` | Get setting applied to all type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `TypeAdapterConfig.NewConfig()` | Create setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `config.NewConfig()` | Create setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `TypeAdapterConfig.ForType()` | Get setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `config.ForType()` | Get setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `config.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))` | Get setting applied to generic type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `config.When((src, dest, mapType) => ...)` | Get setting that applied conditionally | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `config.ForDestinationType()` | Get setting that applied to specific destination type | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| | Configuration for nested mapping | [nested mapping](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-mapping) + +#### Settings +| Method | Description | Apply to queryable | Link | +| ------------- |-----------------------| ------------ | ----- | +| `AddDestinationTransform` | Clean up data for a specific type | x | [setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) | +| `AfterMapping` | Add steps after mapping done | | [before-after](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) | +| `AvoidInlineMapping` | Skip inline process for large type mapping | | [object reference](https://github.com/MapsterMapper/Mapster/wiki/Object-references) | +| `BeforeMapping` | Add steps before mapping start | | [before-after](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) | +| `ConstructUsing` | Define how to create object | x | [constructor](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) | +| `EnableNonPublicMembers` | Mapping non-public properties | | [non-public](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) | +| `EnumMappingStrategy` | Choose whether mapping enum by value or by name | | [data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) | +| `GetMemberName` | Define how to resolve property name | x | [custom naming](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) | +| `Ignore` | Ignore specific properties | x | [ignore](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) | +| `IgnoreAttribute` | Ignore specific attributes annotated on properties | x | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | +| `IgnoreIf` | Ignore conditionally | x | [ignore](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) | +| `IgnoreMember` | Setup rules to ignore | x | [rule based](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) | +| `IgnoreNonMapped` | Ignore all properties not defined in `Map` | x | [ignore](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) | +| `IgnoreNullValues` | Not map if src property is null | | [shallow & merge](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) | +| `Include` | Include derived types on mapping | | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | +| `IncludeAttribute` | Include specific attributes annotated on properties | x | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | +| `IncludeMember` | Setup rules to include | x | [rule based](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) | +| `Inherits` | Copy setting from based type | x | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | +| `Map` | Define property pairs | x | [custom mapping](https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) | +| `MapToConstructor` | Mapping to constructor | x | [constructor](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) | +| `MapToTargetWith` | Define how to map to existing object between type pair | | [custom conversion](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) | +| `MapWith` | Define how to map between type pair | x | [custom conversion](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) | +| `MaxDepth` | Limit depth of nested mapping | x | [object reference](https://github.com/MapsterMapper/Mapster/wiki/Object-references) | +| `NameMatchingStrategy` | Define how to resolve property's name | x | [custom naming](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) | +| `PreserveReference` | Tracking reference when mapping | | [object reference](https://github.com/MapsterMapper/Mapster/wiki/Object-references) | +| `ShallowCopyForSameType` | Direct assign rather than deep clone if type pairs are the same | | [shallow & merge](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) | +| `TwoWays` | Define type mapping are 2 ways | | [2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | +| `Unflattening` | Allow unflatten mapping | |[2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | + +#### Attributes + +| Annotation | Description | Link | +| ------------- |-----------------------| ----- | +| `[AdaptMember(name)]` | Mapping property to different name | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | +| `[AdaptIgnore]` | Ignore property from mapping | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | diff --git a/Ignoring-members.md b/Ignoring-members.md index 1e843f84..18ab465b 100644 --- a/Ignoring-members.md +++ b/Ignoring-members.md @@ -2,32 +2,38 @@ Mapster will automatically map properties with the same names. You can ignore members by using the `Ignore` method. - TypeAdapterConfig - .NewConfig() - .Ignore(dest => dest.Id); +```csharp +TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.Id); +``` ### Rule based ignore You can ignore based on member information by `IgnoreMember` command. Please see https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping for more info. - TypeAdapterConfig.GlobalSettings.Default - .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); +```csharp +TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); +``` ### IgnoreNonMapped You can ignore all non-mapped members by IgnoreNonMapped command. For example, we would like to map only Id and Name. - TypeAdapterConfig - .NewConfig() - .Map(dest => dest.Id, src => src.Id) - .Map(dest => dest.Name, src => src.Name) - .IgnoreNonMapped(true); +```csharp +TypeAdapterConfig + .NewConfig() + .Map(dest => dest.Id, src => src.Id) + .Map(dest => dest.Name, src => src.Name) + .IgnoreNonMapped(true); +``` ### Ignore by attribute You can ignore member by decorate with `[AdaptIgnore]`, and you can ignore custom attributes by `IgnoreAttribute` command. Please see https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes for more info. -``` +```csharp public class Product { public string Id { get; set; } public string Name { get; set; } @@ -41,14 +47,18 @@ public class Product { You can ignore members conditionally, with condition based on source or target. When the condition is met, mapping of the property will be skipped altogether. This is the difference from custom `Map` with condition, where destination is set to `null` when condition is met. - TypeAdapterConfig - .NewConfig() - .IgnoreIf((src, dest) => !string.IsNullOrEmpty(dest.Name), dest => dest.Name); +```csharp +TypeAdapterConfig + .NewConfig() + .IgnoreIf((src, dest) => !string.IsNullOrEmpty(dest.Name), dest => dest.Name); +``` ### IgnoreNullValues You might would like to merge from input object, By default, Mapster will map all properties, even source properties containing null values. You can copy only properties that have values by using `IgnoreNullValues` method. - TypeAdapterConfig - .NewConfig() - .IgnoreNullValues(true); \ No newline at end of file +```csharp +TypeAdapterConfig + .NewConfig() + .IgnoreNullValues(true); +``` diff --git a/Json.net.md b/Json.net.md index 15fd8ce3..0a344524 100644 --- a/Json.net.md +++ b/Json.net.md @@ -9,7 +9,9 @@ Json.net conversion supports Call `EnableJsonMapping` from your `TypeAdapterConfig` to enable Json.Net mapping. - TypeAdapterConfig.GlobalSettings.EnableJsonMapping(); +```csharp +TypeAdapterConfig.GlobalSettings.EnableJsonMapping(); +``` This will allow diff --git a/Mappers.md b/Mappers.md index 8b6ebf4c..b60927b0 100644 --- a/Mappers.md +++ b/Mappers.md @@ -2,11 +2,15 @@ You can simply call `Adapt` method from anywhere. - var destObject = sourceObject.Adapt(); +```csharp +var destObject = sourceObject.Adapt(); +``` or just - var destObject = sourceObject.Adapt(); +```csharp +var destObject = sourceObject.Adapt(); +``` You might notice that there are 2 extension methods doing the same thing. In fact, `sourceObject.Adapt` is a bit better in term of performance (different is just casting from object type). If your application doesn't require high performance, you can just use `sourceObject.Adapt` signature. @@ -14,7 +18,7 @@ You might notice that there are 2 extension methods doing the same thing. In fac In most case `Adapt` method is enough, but sometimes we need builder to support fancy scenario. Basic example, is to pass run-time value. -``` +```csharp var dto = poco.BuildAdapter() .AddParameters("user", this.User.Identity.Name) .AdaptToType(); @@ -22,7 +26,7 @@ var dto = poco.BuildAdapter() Or we can see how Mapster generate mapping logic with Debugger plugin (https://github.com/MapsterMapper/Mapster/wiki/Debugging). -``` +```csharp var script = poco.BuildAdapter() .CreateMapExpression() .ToScript(); @@ -30,7 +34,7 @@ var script = poco.BuildAdapter() Or we can map to EF6 object context (https://github.com/MapsterMapper/Mapster/wiki/EF6). -``` +```csharp var poco = dto.BuildAdapter() .CreateEntityFromContext(db) .AdaptToType(); @@ -41,8 +45,12 @@ var poco = dto.BuildAdapter() In some cases, you need an instance of a mapper (or a factory function) to pass into a DI container. Mapster has the IAdapter and Adapter to fill this need: - IAdapter adapter = new Adapter(); +```csharp +IAdapter adapter = new Adapter(); +``` And usage is the same as with the static methods. - var result = adapter.Adapt(source); +```csharp +var result = adapter.Adapt(source); +``` diff --git a/Mapping-non-public-members.md b/Mapping-non-public-members.md index 59a71c41..32e8ef61 100644 --- a/Mapping-non-public-members.md +++ b/Mapping-non-public-members.md @@ -1,18 +1,20 @@ ### EnableNonPublicMember -This will allow Mapster to set to all non-public members. You can turn on per type pair. +This will allow Mapster to set to all non-public members. - TypeAdapterConfig.NewConfig().EnableNonPublicMember(true); +```csharp +//type pair +TypeAdapterConfig.NewConfig().EnableNonPublicMember(true); -Or global! - - TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); +//global +TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); +``` ### AdaptMember attribute You can also map non-public members with `AdaptMember` attribute. -``` +```csharp public class Product { [AdaptMember] @@ -25,22 +27,27 @@ public class Product `Map` command can map to private member by specify name of the members. - TypeAdapterConfig - .NewConfig() - .Map("PrivateDestName", "PrivateSrcName"); +```csharp +TypeAdapterConfig + .NewConfig() + .Map("PrivateDestName", "PrivateSrcName"); +``` ### IncludeMember With `IncludeMember`, you can select which access modifier to allow. - TypeAdapterConfig.GlobalSettings.Default - .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal - || member.AccessModifier == AccessModifier.ProtectedInternal); +```csharp +TypeAdapterConfig.GlobalSettings.Default + .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal + || member.AccessModifier == AccessModifier.ProtectedInternal); +``` ### Note for non-public member mapping If type doesn't contain public properties, Mapster will treat type as primitive, you must also declare type pair to ensure Mapster will apply non-public member mapping. - TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); - TypeAdapterConfig.NewConfig(); - +```csharp +TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); +TypeAdapterConfig.NewConfig(); +``` diff --git a/Naming-convention.md b/Naming-convention.md index b4e21ef4..dcf1a7d7 100644 --- a/Naming-convention.md +++ b/Naming-convention.md @@ -2,15 +2,11 @@ By default, Mapster will map property with case sensitive name. You can adjust to flexible name mapping by setting `NameMatchingStrategy.Flexible` to `NameMatchingStrategy` method. This setting will allow matching between `PascalCase`, `camelCase`, `lower_case`, and `UPPER_CASE`. -This setting will apply flexible naming globally. - -``` +```csharp +//global TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.Flexible); -``` -or by specific type mapping. - -``` +//type pair TypeAdapterConfig.NewConfig().NameMatchingStrategy(NameMatchingStrategy.Flexible); ``` @@ -18,7 +14,7 @@ TypeAdapterConfig.NewConfig().NameMatchingStrategy(NameMatchingStrateg Flexible name could not map between `MiXcAsE` and `MixCase`, because flexible name will break `MiXcAsE` into `Mi-Xc-As-E` rather than just `Mix-Case`. In this case, we need to use `IgnoreCase` to perform case insensitive matching. -``` +```csharp TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.IgnoreCase); ``` @@ -26,22 +22,28 @@ TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrate For custom rules, you can use either `ConvertSourceMemberName` or `ConvertDestinationMemberName` up to which side you would like to convert. For example, you might would like to add `m_` to all properties. - TypeAdapterConfig.NewConfig() - .NameMatchingStrategy(NameMatchingStrategy.ConvertSourceMemberName(name => "m_" + name)); +```csharp +TypeAdapterConfig.NewConfig() + .NameMatchingStrategy(NameMatchingStrategy.ConvertSourceMemberName(name => "m_" + name)); +``` This example is to replace foreign letter from name. - TypeAdapterConfig.NewConfig() - .NameMatchingStrategy(NameMatchingStrategy.ConvertSourceMemberName(name => name.Replace("Ä", "A")); +```csharp +TypeAdapterConfig.NewConfig() + .NameMatchingStrategy(NameMatchingStrategy.ConvertSourceMemberName(name => name.Replace("Ä", "A")); +``` ### Naming Convention with IDictionary If you would like to change case from POCO to `IDictionary` to camelCase, you can use `ToCamelCase`. Another way around, if you would like to map `IDictionary` back to POCO, you can use `FromCamelCase`. - TypeAdapterConfig>.NewConfig() - .NameMatchingStrategy(NameMatchingStrategy.ToCamelCase); - TypeAdapterConfig, Poco>.NewConfig() - .NameMatchingStrategy(NameMatchingStrategy.FromCamelCase); +```csharp +TypeAdapterConfig>.NewConfig() + .NameMatchingStrategy(NameMatchingStrategy.ToCamelCase); +TypeAdapterConfig, Poco>.NewConfig() + .NameMatchingStrategy(NameMatchingStrategy.FromCamelCase); +``` NOTE: mapping from `IDictionary` to POCO, you can also use `Flexible` or `IgnoreCase`, but both will be slower since it will scan through dictionary entries rather than lookup. @@ -49,19 +51,23 @@ NOTE: mapping from `IDictionary` to POCO, you can also use `Flexible` You can change name based on rule by `GetMemberName` method. For example, if we would like to rename property based on `JsonProperty` attribute. - TypeAdapterConfig.GlobalSettings.Default - .GetMemberName(member => member.GetCustomAttributes(true) - .OfType() - .FirstOrDefault()?.PropertyName); //if return null, property will not be renamed +```csharp +TypeAdapterConfig.GlobalSettings.Default + .GetMemberName(member => member.GetCustomAttributes(true) + .OfType() + .FirstOrDefault()?.PropertyName); //if return null, property will not be renamed +``` Then in your class - public class Poco - { - [JsonProperty("code")] - public string Id { get; set; } +```csharp +public class Poco +{ + [JsonProperty("code")] + public string Id { get; set; } - ... - } + ... +} +``` With above config, `Id` will be mapped to `code`. \ No newline at end of file diff --git a/Object-creation-and-validation.md b/Object-creation-and-validation.md deleted file mode 100644 index 09680d67..00000000 --- a/Object-creation-and-validation.md +++ /dev/null @@ -1,29 +0,0 @@ -### Custom Destination Object Creation - -You can provide a function call to create your destination objects instead of using the default object creation -(which expects an empty constructor). To do so, use the `ConstructUsing` method when configuring. This method expects -a function that will provide the destination instance. You can call your own constructor, a factory method, -or anything else that provides an object of the expected type. -```csharp - //Example using a non-default constructor - TypeAdapterConfig.NewConfig() - .ConstructUsing(src => new TDestination(src.Id, src.Name)); - - //Example using an object initializer - TypeAdapterConfig.NewConfig() - .ConstructUsing(src => new TDestination{Unmapped = "unmapped"}); -``` -### After mapping action - -You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. - -```C# - TypeAdapterConfig.ForType().AfterMapping((src, dest) => dest.Validate()); -``` - -Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. - -```C# - TypeAdapterConfig.GlobalSettings.ForDestinationType() - .AfterMapping(dest => dest.Validate()); -``` diff --git a/Object-references.md b/Object-references.md index 5c979939..11fcabc5 100644 --- a/Object-references.md +++ b/Object-references.md @@ -2,24 +2,37 @@ When mapping objects with circular references, a stackoverflow exception will result. This is because Mapster will get stuck in a loop trying to recursively map the circular reference. If you would like to map circular references or preserve references (such as 2 properties pointing to the same object), you can do it by setting `PreserveReference` to `true` - TypeAdapterConfig - .NewConfig() - .PreserveReference(true); +```csharp +TypeAdapterConfig + .NewConfig() + .PreserveReference(true); +``` -NOTE: Projection doesn't support circular reference. To overcome, you might use `Adapt` instead of `ProjectToType`. +NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. - TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); - var students = context.Student.Include(p => p.Schools).Adapt>(); +### MaxDepth -NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. +Rather than `PreserveReference`, you could also try `MaxDepth`. `MaxDepth` will map until it reaches the defined limit. Unlike `PreserveReference`, `MaxDepth` also works with queryable projection. + +```csharp +TypeAdapterConfig + .NewConfig() + .MaxDepth(3); +``` + +NOTE 1: `MaxDepth` starts with 1, means you will copy only primitives. POCO class and collection of POCO each count as a depth of 1. + +NOTE 2: even `MaxDepth` has no maximum value, you shouldn't input large number. Each depth will generate a mapping logic, otherwise it will consume a lot of memory. ### Shallow copy By default, Mapster will recursively map nested objects. You can do shallow copying by setting `ShallowCopyForSameType` to `true`. - TypeAdapterConfig - .NewConfig() - .ShallowCopyForSameType(true); +```csharp +TypeAdapterConfig + .NewConfig() + .ShallowCopyForSameType(true); +``` ### Mapping very large objects @@ -29,6 +42,7 @@ For performance optimization, Mapster tried to inline class mapping. This proces You can skip inlining process by: - TypeAdapterConfig.GlobalSettings.Default - .AvoidInlineMapping(true); - +```csharp +TypeAdapterConfig.GlobalSettings.Default + .AvoidInlineMapping(true); +``` diff --git a/Rule-based-member-mapping.md b/Rule-based-member-mapping.md index 2c3d835e..86fb2d1a 100644 --- a/Rule-based-member-mapping.md +++ b/Rule-based-member-mapping.md @@ -1,39 +1,43 @@ By default, Mapster will include public fields and properties, but we can change this behavior by `IncludeMember` and `IgnoreMember` method. The methods require predicate, and input types of predicate are: - public interface IMemberModel - { - Type Type { get; } - string Name { get; } - object Info { get; } - AccessModifier SetterModifier { get; } - AccessModifier AccessModifier { get; } - IEnumerable GetCustomAttributes(bool inherit); - } - - public enum MemberSide - { - Source, - Destination, - } +```csharp +public interface IMemberModel +{ + Type Type { get; } + string Name { get; } + object Info { get; } + AccessModifier SetterModifier { get; } + AccessModifier AccessModifier { get; } + IEnumerable GetCustomAttributes(bool inherit); +} + +public enum MemberSide +{ + Source, + Destination, +} +``` ### Not allow fields If you would like to allow only properties not public field to be mapped, you can check from `Info`. Possible values could be `PropertyInfo`, `FieldInfo`, or `ParameterInfo`. In this case, we will reject member of type `FieldInfo`. - TypeAdapterConfig.GlobalSettings.Default - .IgnoreMember((member, side) => member.Info is FieldInfo); +```csharp +TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => member.Info is FieldInfo); +``` ### Allow only some list of types to be mapped Suppose you are working with EF, and you would like to skip all navigation properties. Then we will allow only short list of types. **Allow by types** -``` +```csharp TypeAdapterConfig.GlobalSettings.Default .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); ``` **Allow by Namespaces** -``` +```csharp TypeAdapterConfig.GlobalSettings.Default .IgnoreMember((member, side) => !member.Type.Namespace.StartsWith("System")); ``` @@ -42,23 +46,29 @@ TypeAdapterConfig.GlobalSettings.Default If you would like to map members marked as internal, you can do it by: +```csharp TypeAdapterConfig.GlobalSettings.Default .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal || member.AccessModifier == AccessModifier.ProtectedInternal); +``` ### Allow only DataMember attribute If you would like to include all members decorated with DataMember attribute, and ignore all members with no DataMember attribute, you can set up by: - TypeAdapterConfig.GlobalSettings.Default - .IncludeMember((member, side) => member.GetCustomAttributes(true).OfType().Any()); - TypeAdapterConfig.GlobalSettings.Default - .IgnoreMember((member, side) => !member.GetCustomAttributes(true).OfType().Any()); +```csharp +TypeAdapterConfig.GlobalSettings.Default + .IncludeMember((member, side) => member.GetCustomAttributes(true).OfType().Any()); +TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => !member.GetCustomAttributes(true).OfType().Any()); +``` ### Turn-off non-public setters Mapster always allows non-public setters. But you can override by: - TypeAdapterConfig.GlobalSettings.Default - .IgnoreMember((member, side) => side == MemberSide.Destination - && member.SetterModifier != AccessModifier.Public); +```csharp +TypeAdapterConfig.GlobalSettings.Default + .IgnoreMember((member, side) => side == MemberSide.Destination + && member.SetterModifier != AccessModifier.Public); +``` diff --git a/Setting-by-attributes.md b/Setting-by-attributes.md index 50dbf3d2..987f0771 100644 --- a/Setting-by-attributes.md +++ b/Setting-by-attributes.md @@ -2,7 +2,7 @@ When a property decorated with `[AdaptIgnore]`, that property will be excluded from Mapping. For example, if we would like to exclude price to be mapped. -``` +```csharp public class Product { public string Id { get; set; } public string Name { get; set; } @@ -23,7 +23,7 @@ You can ignore members annotated with any attributes by using the `IgnoreAttribu **Map to different name** With `AdaptMember` attribute, you can specify name of source or target to be mapped. For example, if we would like to map `Id` to `Code`. -``` +```csharp public class Product { [AdaptMember("Code")] public string Id { get; set; } @@ -33,7 +33,7 @@ public class Product { **Map to non-public members** You can also map non-public members with `AdaptMember` attribute. -``` +```csharp public class Product { [AdaptMember] private string HiddenId { get; set; } @@ -45,14 +45,18 @@ public class Product { You can rename member to be matched by `GetMemberName`. For example, if we would like to rename property based on `JsonProperty` attribute. - TypeAdapterConfig.GlobalSettings.Default - .GetMemberName(member => member.GetCustomAttributes(true) - .OfType() - .FirstOrDefault()?.PropertyName); //if return null, property will not be renamed +```csharp +TypeAdapterConfig.GlobalSettings.Default + .GetMemberName(member => member.GetCustomAttributes(true) + .OfType() + .FirstOrDefault()?.PropertyName); //if return null, property will not be renamed +``` ### Include custom attributes And if we would like to include non-public members decorated with `JsonProperty` attribute, we can do it by `IncludeAttribute`. - TypeAdapterConfig.GlobalSettings.Default - .IncludeAttribute(typeof(JsonPropertyAttribute)); +```csharp +TypeAdapterConfig.GlobalSettings.Default + .IncludeAttribute(typeof(JsonPropertyAttribute)); +``` diff --git a/Shallow-merge.md b/Shallow-merge.md new file mode 100644 index 00000000..7408973f --- /dev/null +++ b/Shallow-merge.md @@ -0,0 +1,18 @@ +### Deep copy vs shallow copy + +By default, Mapster will recursively map nested objects (deep copy). You can do shallow copying by setting `ShallowCopyForSameType` to `true`. + +```csharp +TypeAdapterConfig + .NewConfig() + .ShallowCopyForSameType(true); +``` +### Copy vs Merge + +By default, Mapster will map all properties, even source properties containing null values. You can copy only properties that have values (merge) by using `IgnoreNullValues` method. + +```csharp +TypeAdapterConfig + .NewConfig() + .IgnoreNullValues(true); +``` diff --git a/Two-ways.md b/Two-ways.md new file mode 100644 index 00000000..9c0144d9 --- /dev/null +++ b/Two-ways.md @@ -0,0 +1,55 @@ +### 2-ways mapping + +If you need to map object from POCO to DTO, and map back from DTO to POCO. You can define the setting once by using `TwoWays`. + +```csharp +TypeAdapterConfig + .NewConfig() + .TwoWays() + .Map(dto => dto.Code, poco => poco.Id); //<-- this setting will apply dto.Code = poco.Id & poco.Id = dto.Code for reverse mapping +``` + +NOTE: `TwoWays` command need to call before setting to take effect. + +```csharp +TypeAdapterConfig + .NewConfig() + .Map(dto => dto.Foo, poco => poco.Bar) //<-- this map only apply to Poco->Dto + .TwoWays() + .Map(dto => dto.Foz, poco => poco.Baz); //<-- this map will apply both side +``` + +### Unflattening + +By default, Mapster will perform flattening. + +```csharp +class Staff { + public string Name { get; set; } + public Staff Supervisor { get; set; } + ... +} + +struct StaffDto { + public string SupervisorName { get; set; } +} +``` + +Above example, without any setup, you can map from POCO to DTO and you will get `SupervisorName` from `Supervisor.Name` (flattening). + +However, unflattening process needed to be defined. You can map to `Supervisor.Name` from `SupervisorName` by + +#### using `Unflattening`. + +```csharp +TypeAdapterConfig.NewConfig() + .Unflattening(true); +``` + +#### using `TwoWays` + +```csharp +TypeAdapterConfig + .NewConfig() + .TwoWays(); //<-- this will also map poco.Supervisor.Name = dto.SupervisorName for reverse mapping +``` diff --git a/_Sidebar.md b/_Sidebar.md index 65bc377c..b897aefd 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -1,8 +1,9 @@ ### Getting Started +##### [References](https://github.com/MapsterMapper/Mapster/wiki) ##### Mapping * [Basic usages](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) -* [3 kind of mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) +* [Mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) * [Data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) ##### Configuration @@ -11,7 +12,7 @@ * [Config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) * [Config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) * [Config validation & compilation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) -* [Config for nested classes](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes) +* [Config for nested mapping](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-mapping) ##### Settings * Custom member matching logic @@ -21,14 +22,18 @@ * [Ignoring members](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) * [Rule-based member matching](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) * [Mapping non-public members](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) -* [Object creation & validation](https://github.com/MapsterMapper/Mapster/wiki/Object-creation-and-validation) + * [Two ways & unflattening mapping](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) +* [Constructor mapping](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) +* [Before & after mapping](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) * [Setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) -* [Working with object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) +* [Shallow & merge mapping](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) +* [Recursive & object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) * [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) * [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) ##### Plugins +* [Code Generation](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) * [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) +* [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) * [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) -* [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) \ No newline at end of file From 6ca671b8bc35722082d0c4455f6fa0b32af7fcbc Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 13 Apr 2019 16:49:21 +0700 Subject: [PATCH 081/363] Updated Home (markdown) --- Home.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Home.md b/Home.md index 4d6af7a3..5c75838d 100644 --- a/Home.md +++ b/Home.md @@ -97,8 +97,8 @@ | `NameMatchingStrategy` | Define how to resolve property's name | x | [custom naming](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) | | `PreserveReference` | Tracking reference when mapping | | [object reference](https://github.com/MapsterMapper/Mapster/wiki/Object-references) | | `ShallowCopyForSameType` | Direct assign rather than deep clone if type pairs are the same | | [shallow & merge](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) | -| `TwoWays` | Define type mapping are 2 ways | | [2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | -| `Unflattening` | Allow unflatten mapping | |[2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | +| `TwoWays` | Define type mapping are 2 ways | x | [2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | +| `Unflattening` | Allow unflatten mapping | x |[2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | #### Attributes From b4702bd79583ef1d5c29af9ac218bce27bd2f3f5 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Mon, 20 Jan 2020 07:33:00 +0700 Subject: [PATCH 082/363] Updated Setting by attributes (markdown) --- Setting-by-attributes.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Setting-by-attributes.md b/Setting-by-attributes.md index 987f0771..dfd3483f 100644 --- a/Setting-by-attributes.md +++ b/Setting-by-attributes.md @@ -16,8 +16,16 @@ public class Product { You can ignore members annotated with any attributes by using the `IgnoreAttribute` method. - TypeAdapterConfig.GlobalSettings.Default - .IgnoreAttribute(typeof(JsonIgnoreAttribute)); +```csharp +TypeAdapterConfig.GlobalSettings.Default + .IgnoreAttribute(typeof(JsonIgnoreAttribute)); +``` + +However `IgnoreAttribute` will ignore both source and destination. If you would like to ignore only one side, you can use `IgnoreMember`. + +```csharp +config.IgnoreMember((member, side) => member.HasCustomAttribute(typeof(NotMapAttribute)) && side == MemberSide.Source); +``` ### AdaptMember attribute **Map to different name** From eb44638e84d7fcfea48a6056fd67bf1d0f679fbb Mon Sep 17 00:00:00 2001 From: chaowlert Date: Tue, 21 Jan 2020 22:59:16 +0700 Subject: [PATCH 083/363] Updated Debugging (markdown) --- Debugging.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Debugging.md b/Debugging.md index b357a893..63a7be38 100644 --- a/Debugging.md +++ b/Debugging.md @@ -2,15 +2,14 @@ PM> Install-Package ExpressionDebugger -This plugin allow you to perform step-into debugging! +This plugin allow you to perform step-into debugging using Roslyn! ##### Usage Then add following code on start up (or anywhere before mapping is compiled) ```csharp -var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached }; -TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); +TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(); ``` Now on your mapping code (only in `DEBUG` mode). @@ -38,8 +37,17 @@ var script = poco.BuildAdapter() ### Visual Studio for Mac To step-into debugging, you might need to emit file ```csharp -var opt = new ExpressionCompilationOptions { IsRelease = !Debugger.IsAttached, EmitFile = true }; +var opt = new ExpressionCompilationOptions { EmitFile = true }; TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); ... var dto = poco.Adapt(); //<-- you can step-into this function!! ``` + +### Do not worry about performance +In `RELEASE` mode, Roslyn compiler actually faster than default dynamic compilation by 2x. +Here is the result. + +| Method | Mean | StdDev | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | +|-------------------------- |---------------:|-------------:|-------------:|------------:|------:|------:|-----------:| +| 'Mapster 4.1.1' | 115.31 ms | 0.849 ms | 1.426 ms | 31000.0000 | - | - | 124.36 MB | +| 'Mapster 4.1.1 (Roslyn)' | 53.55 ms | 0.342 ms | 0.654 ms | 31100.0000 | - | - | 124.36 MB | From aca54eb05466f08f1d4c4828df42c756e9684c44 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 09:22:33 +0700 Subject: [PATCH 084/363] Created Async (markdown) --- Async.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Async.md diff --git a/Async.md b/Async.md new file mode 100644 index 00000000..f96b1d2f --- /dev/null +++ b/Async.md @@ -0,0 +1,36 @@ +### Async support + + PM> Install-Package Mapster.Async + +This plugin allows you to perform async operation for mapping + +##### Setup + +Use `AfterMappingAsync` to setup async operation + +```csharp +config.NewConfig() + .AfterMappingAsync(async (poco, dto) => + { + var userManager = MapContext.Current.GetService(); + var user = await userManager.FindByIdAsync(poco.UserId); + dto.UserName = user.Name; + }); +``` + +##### Mapping + +Then map asynchronously with `AdaptToTypeAsync`. + +```csharp +var dto = await poco.BuildAdapter() + .AdaptToTypeAsync(); +``` + + +Or like this, if you use mapper instance. + +```csharp +var dto = await _mapper.From(poco) + .AdaptToTypeAsync(); +``` From 35be3840bcab7ad2aad6d4f963c8ed05b0f964dc Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 09:23:09 +0700 Subject: [PATCH 085/363] Updated Debugging (markdown) --- Debugging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Debugging.md b/Debugging.md index 63a7be38..f9f3d5c2 100644 --- a/Debugging.md +++ b/Debugging.md @@ -2,7 +2,7 @@ PM> Install-Package ExpressionDebugger -This plugin allow you to perform step-into debugging using Roslyn! +This plugin allows you to perform step-into debugging using Roslyn! ##### Usage From 2314c34e2095f595528de508b336f32a2d460573 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 09:24:54 +0700 Subject: [PATCH 086/363] Updated _Sidebar (markdown) --- _Sidebar.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index b897aefd..4838f2bd 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -32,8 +32,10 @@ * [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) ##### Plugins +* [Async Support](https://github.com/MapsterMapper/Mapster/wiki/Async) * [Code Generation](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) -* [EF6](https://github.com/MapsterMapper/Mapster/wiki/EF6) +* [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/DI) +* [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF6) * [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) * [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) From e57e0ef642e413a097abbf03305cd6af3e0853f1 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 10:00:14 +0700 Subject: [PATCH 087/363] Created Dependency Injection (markdown) --- Dependency-Injection.md | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Dependency-Injection.md diff --git a/Dependency-Injection.md b/Dependency-Injection.md new file mode 100644 index 00000000..a83896dc --- /dev/null +++ b/Dependency-Injection.md @@ -0,0 +1,49 @@ +### Dependency injection support + + PM> Install-Package Mapster.DependencyInjection + +This plugin allows you to inject service into mapping configuration. + +#### Usage + +On startup, register `TypeAdapterConfig`, and `ServiceMapper`. + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + ... + services.AddSingleton(config); + services.AddScoped(); + ... +} +``` + +NOTE: lifetime of `ServiceMapper` is up to services you would like to inject. It can be singleton, if you inject only singleton services. Or it can be transient, if any injected services is transient. + +##### Mapping configuration + +You can get service by `MapContext.Current.GetService<>()`, for example + +```csharp +config.NewConfig() + .Map(dest => dest.Name, src => MapContext.Current.GetService(src.Name)); +``` + +##### Mapping + +If you setup service injection, you need to use mapper instance to map object. + +```csharp +public class FooService { + private readonly IMapper _mapper; + + public FooService(IMapper mapper) { + _mapper = mapper; + } + + public void DoSomething(Poco poco) { + var dto = _mapper.Map(poco); + ... + } +} +``` From bb6f632d0c4647c07a31e2aabb20bdd29b9426f2 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 10:01:05 +0700 Subject: [PATCH 088/363] Updated _Sidebar (markdown) --- _Sidebar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 4838f2bd..7bd31922 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -35,7 +35,7 @@ * [Async Support](https://github.com/MapsterMapper/Mapster/wiki/Async) * [Code Generation](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) -* [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/DI) +* [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) * [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF6) * [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) * [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) From a82af9f30a025fd5f6f69a48f81c1a586d19cbdd Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 10:03:09 +0700 Subject: [PATCH 089/363] Updated Json.net (markdown) --- Json.net.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Json.net.md b/Json.net.md index 0a344524..cbba8c09 100644 --- a/Json.net.md +++ b/Json.net.md @@ -12,6 +12,12 @@ Call `EnableJsonMapping` from your `TypeAdapterConfig` to enable Json.Net mappin ```csharp TypeAdapterConfig.GlobalSettings.EnableJsonMapping(); ``` + +or + +```csharp +config.EnableJsonMapping(); +``` This will allow From 5972aa000b5b54765b4f3528928a77b5de1cda39 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 10:04:15 +0700 Subject: [PATCH 090/363] Updated FastExpressionCompiler (markdown) --- FastExpressionCompiler.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/FastExpressionCompiler.md b/FastExpressionCompiler.md index d39c9fb5..c312d73a 100644 --- a/FastExpressionCompiler.md +++ b/FastExpressionCompiler.md @@ -10,4 +10,9 @@ Then add following code on start up TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileFast(); ``` -That's it. Now your code will enjoy performance boost 1.3 to 4.0 times faster! \ No newline at end of file +That's it. Now your code will enjoy performance boost. Here is result. + +| Method | Mean | StdDev | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | +|-------------------------- |---------------:|-------------:|-------------:|------------:|------:|------:|-----------:| +| 'Mapster 4.1.1' | 115.31 ms | 0.849 ms | 1.426 ms | 31000.0000 | - | - | 124.36 MB | +| 'Mapster 4.1.1 (FEC)' | 54.70 ms | 1.023 ms | 1.546 ms | 29600.0000 | - | - | 118.26 MB | From 5f5fc4ec61c6215d145e526f5239760d47a64533 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 10:22:54 +0700 Subject: [PATCH 091/363] Updated EF6 (markdown) --- EF-6-&-EF-Core.md | 32 ++++++++++++++++++++++++++++++++ EF6.md | 24 ------------------------ 2 files changed, 32 insertions(+), 24 deletions(-) create mode 100644 EF-6-&-EF-Core.md delete mode 100644 EF6.md diff --git a/EF-6-&-EF-Core.md b/EF-6-&-EF-Core.md new file mode 100644 index 00000000..92023cf7 --- /dev/null +++ b/EF-6-&-EF-Core.md @@ -0,0 +1,32 @@ +### EF 6 & EF Core support + +For EF 6 + + PM> Install-Package Mapster.EF6 + +For EF Core + + PM> Install-Package Mapster.EFCore + +In EF, objects are tracked, when you copy data from dto to entity containing navigation properties, this plugin will help finding entity object in navigation properties automatically. + +#### Usage + +Use `EntityFromContext` method to define data context. + +```csharp +var poco = db.DomainPoco.Include("Children") + .Where(item => item.Id == dto.Id).FirstOrDefault(); + +dto.BuildAdapter() + .EntityFromContext(db) + .AdaptTo(poco); +``` + +Or like this, if you use mapper instance + +```csharp +_mapper.From(dto) + .EntityFromContext(db) + .AdaptTo(poco); +``` diff --git a/EF6.md b/EF6.md deleted file mode 100644 index 9bcf8fa9..00000000 --- a/EF6.md +++ /dev/null @@ -1,24 +0,0 @@ -In EntityFramework, when we got data from dto, and we would like to map data to entities. We need to get objects from store first and then mapping data to it. Problem is, when the object contains complex graph, it is very difficult to map data correctly. Because some child property might reference to another object in store, and some children collection may or may not exist in store. This plugin will help you to copy data to object in store correctly. If data doesn't found, new object will be created. - - PM> Install-Package Mapster.EF6 - -Sample - -```csharp -var poco = dto.BuildAdapter() - .CreateEntityFromContext(db) - .AdaptToType(); -``` - -NOTE: If your data contains complex graph, it would be better to dump related records in single command to store first. This is to avoid plugin from downloading records one-by-one, which should be slower. - -```csharp -//just make the query, objects will be cached in store -db.DomainPoco.Include("Children") - .Where(item => item.Id == dto.Id).FirstOrDefault(); - -//when mapping, Mapster will get object from cache rather than lookup in db -var poco = dto.BuildAdapter() - .CreateEntityFromContext(db) - .AdaptToType(); -``` From da36000db45e2b6d1ce98c91ce6ef79bb3365cfd Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 10:23:43 +0700 Subject: [PATCH 092/363] Updated _Sidebar (markdown) --- _Sidebar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 7bd31922..3ef12180 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -36,6 +36,6 @@ * [Code Generation](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) * [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) -* [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF6) +* [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) * [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) * [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) From be09364ffab37d043804cf565d162444e175ff4c Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 10:37:05 +0700 Subject: [PATCH 093/363] Updated Mappers (markdown) --- Mappers.md | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/Mappers.md b/Mappers.md index b60927b0..c4583713 100644 --- a/Mappers.md +++ b/Mappers.md @@ -3,54 +3,46 @@ You can simply call `Adapt` method from anywhere. ```csharp -var destObject = sourceObject.Adapt(); +var dest = src.Adapt(); ``` or just ```csharp -var destObject = sourceObject.Adapt(); +var dest = src.Adapt(); ``` -You might notice that there are 2 extension methods doing the same thing. In fact, `sourceObject.Adapt` is a bit better in term of performance (different is just casting from object type). If your application doesn't require high performance, you can just use `sourceObject.Adapt` signature. +2 extension methods are doing the same thing. `src.Adapt` will cast `src` to object. Therefore, if you map value type, please use `src.Adapt` to avoid boxing and unboxing. -### Builder - -In most case `Adapt` method is enough, but sometimes we need builder to support fancy scenario. Basic example, is to pass run-time value. - -```csharp -var dto = poco.BuildAdapter() - .AddParameters("user", this.User.Identity.Name) - .AdaptToType(); -``` +### Mapper instance -Or we can see how Mapster generate mapping logic with Debugger plugin (https://github.com/MapsterMapper/Mapster/wiki/Debugging). +In some cases, you need an instance of a mapper (or a factory function) to pass into a DI container. Mapster has +the `IMapper` and `Mapper` to fill this need: ```csharp -var script = poco.BuildAdapter() - .CreateMapExpression() - .ToScript(); +IMapper mapper = new Mapper(); ``` -Or we can map to EF6 object context (https://github.com/MapsterMapper/Mapster/wiki/EF6). +And usage `Map` method to perform mapping. ```csharp -var poco = dto.BuildAdapter() - .CreateEntityFromContext(db) - .AdaptToType(); +var result = mapper.Map(source); ``` -### Mapper instance +### Builder -In some cases, you need an instance of a mapper (or a factory function) to pass into a DI container. Mapster has -the IAdapter and Adapter to fill this need: +In most case `Adapt` method is enough, but sometimes we need builder to support fancy scenario. Basic example, is to pass run-time value. ```csharp -IAdapter adapter = new Adapter(); +var dto = poco.BuildAdapter() + .AddParameters("user", this.User.Identity.Name) + .AdaptToType(); ``` -And usage is the same as with the static methods. +Or if you use mapper instance, you can create builder by method `From`. ```csharp -var result = adapter.Adapt(source); +var dto = mapper.From(poco) + .AddParameters("user", this.User.Identity.Name) + .AdaptToType(); ``` From 66a93b0cf028bdf98868ffbc441c8848d753d388 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 11:02:05 +0700 Subject: [PATCH 094/363] Updated Setting by attributes (markdown) --- Setting-by-attributes.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Setting-by-attributes.md b/Setting-by-attributes.md index dfd3483f..ca7c7339 100644 --- a/Setting-by-attributes.md +++ b/Setting-by-attributes.md @@ -12,6 +12,20 @@ public class Product { } ``` +`[AdaptIgnore]` will both ignore when type are used as source or destination. You can ignore only one side by passing `MemberSide`. + +```csharp +public class Product { + public string Id { get; set; } + public string Name { get; set; } + + [AdaptIgnore(MemberSide.Source)] + public decimal Price { get; set; } +} +``` + +Above example, `Price` will be ignored only when `Product` is used as source. + ### Ignore custom attributes You can ignore members annotated with any attributes by using the `IgnoreAttribute` method. @@ -68,3 +82,18 @@ And if we would like to include non-public members decorated with `JsonProperty` TypeAdapterConfig.GlobalSettings.Default .IncludeAttribute(typeof(JsonPropertyAttribute)); ``` + +### Use destination value + +You can tell Mapster to use existing property object to map data rather than create new object. + +```csharp +public class Order { + public string Id { get; set; } + + [UseDestinationValue] + public ICollection Items { get; } = new List(); +} +``` + +See more detail at https://github.com/MapsterMapper/Mapster/wiki/Use-existing-prop \ No newline at end of file From c25b1328e7aaa06e02c83a443ba6b3065e34c6d9 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 11:09:09 +0700 Subject: [PATCH 095/363] Updated Setting by attributes (markdown) --- Setting-by-attributes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Setting-by-attributes.md b/Setting-by-attributes.md index ca7c7339..470828c5 100644 --- a/Setting-by-attributes.md +++ b/Setting-by-attributes.md @@ -96,4 +96,4 @@ public class Order { } ``` -See more detail at https://github.com/MapsterMapper/Mapster/wiki/Use-existing-prop \ No newline at end of file +See more detail at https://github.com/MapsterMapper/Mapster/wiki/Use-existing-value \ No newline at end of file From cbea8f1c32eefcf4e300b75020cda8f79a317bd3 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 11:10:18 +0700 Subject: [PATCH 096/363] Updated Setting by attributes (markdown) --- Setting-by-attributes.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/Setting-by-attributes.md b/Setting-by-attributes.md index 470828c5..08d3b303 100644 --- a/Setting-by-attributes.md +++ b/Setting-by-attributes.md @@ -95,5 +95,3 @@ public class Order { public ICollection Items { get; } = new List(); } ``` - -See more detail at https://github.com/MapsterMapper/Mapster/wiki/Use-existing-value \ No newline at end of file From 596eba4e9a68f359a776db7c4c21954cc04fd80e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 11:22:23 +0700 Subject: [PATCH 097/363] Created Mapping readonly prop (markdown) --- Mapping-readonly-prop.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Mapping-readonly-prop.md diff --git a/Mapping-readonly-prop.md b/Mapping-readonly-prop.md new file mode 100644 index 00000000..c43106bc --- /dev/null +++ b/Mapping-readonly-prop.md @@ -0,0 +1,34 @@ +### Non public setter + +Mapster can map to non public setter automatically. + +```csharp +public class Order { + public string Id { get; set; } + public ICollection Items { get; private set; } +} +``` + +### Using UseDestinationValue attribute + +You can make your type pure readonly and annotate with [UseDestinationValue]. + +```csharp +public class Order { + public string Id { get; set; } + + [UseDestinationValue] + public ICollection Items { get; } = new List(); +} +``` + +### Convention based setup + +Or you can apply without annotate each type, for example, if you would like all readonly `ICollection<>` to use destination value. + +```csharp +TypeAdapterConfig.GlobalSettings.Default + .UseDestinationValue(member => member.SetterModifier == AccessModifier.None && + member.Type.IsGenericType && + member.Type.GetGenericTypeDefinition() == typeof(ICollection<>)); +``` From 806f6ae6534a4572d0025b1ff17a9b06fdf57599 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 11:23:19 +0700 Subject: [PATCH 098/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_Sidebar.md b/_Sidebar.md index 3ef12180..95cf84e7 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -21,6 +21,7 @@ * [Setting by attributes](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) * [Ignoring members](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) * [Rule-based member matching](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) + * [Mapping readonly prop](https://github.com/MapsterMapper/Mapster/wiki/Mapping-readonly-prop) * [Mapping non-public members](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) * [Two ways & unflattening mapping](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) * [Constructor mapping](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) From 78756c15676f12650d9507e7a9e309793a900c39 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 11:26:12 +0700 Subject: [PATCH 099/363] Updated Mappers (markdown) From 1edf456da8154c35877d7e51a7ebe288a9602799 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 11:40:35 +0700 Subject: [PATCH 100/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_Sidebar.md b/_Sidebar.md index 95cf84e7..59d3eff1 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -31,6 +31,7 @@ * [Recursive & object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) * [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) * [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) +* [Multiple sources](https://github.com/MapsterMapper/Mapster/wiki/Multiple-sources) ##### Plugins * [Async Support](https://github.com/MapsterMapper/Mapster/wiki/Async) From 25faa8181a6627eb40e74edad583b2d97245c27c Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 11:56:29 +0700 Subject: [PATCH 101/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 - 1 file changed, 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 59d3eff1..95cf84e7 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -31,7 +31,6 @@ * [Recursive & object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) * [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) * [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) -* [Multiple sources](https://github.com/MapsterMapper/Mapster/wiki/Multiple-sources) ##### Plugins * [Async Support](https://github.com/MapsterMapper/Mapster/wiki/Async) From 791b009df1260f6e2dd3f2d7ae4299aa9cb62b73 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 12:10:56 +0700 Subject: [PATCH 102/363] Updated Custom mapping (markdown) --- Custom-mapping.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Custom-mapping.md b/Custom-mapping.md index fdb34a0c..5ce7a529 100644 --- a/Custom-mapping.md +++ b/Custom-mapping.md @@ -44,3 +44,61 @@ TypeAdapterConfig ``` For more information about mapping non-public members, please see https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members. + +### Deep destination property + +`Map` can be defined to map deep destination property. + +```csharp +TypeAdapterConfig.NewConfig() + .Map(dest => dest.Child.Name, src => src.Name); +``` + +### Null propagation + +If `Map` contains only property path, null propagation will be applied. + +```csharp +TypeAdapterConfig.NewConfig() + .Map(dest => dest.Name, src => src.Child.Name); +``` + +From above example, if `src.Child` is null, mapping will return null instead of throw `NullPointerException`. + +### Multiple sources + +**Example 1**: Mapping Dto+SubDto to Poco + +```csharp +public class SubDto +{ + public string Extras { get; set; } +} +public class Dto +{ + public string Name { get; set; } + public SubDto Details { get; set; } +} +public class Poco +{ + public string Name { get; set; } + public string Extras { get; set; } +} +``` + +To configure Dto+SubDto to poco + +```csharp +TypeAdapterConfig.NewConfig() + .Map(dest => dest, src => src.Details); +``` + +**Example 2**: 2 different dto to poco + +You can use tuple to do this + +```csharp +TypeAdapterConfig<(Dto1, Dto2), Poco>.NewConfig() + .Map(dest => dest, src => src.Item1) + .Map(dest => dest, src => src.Item2); +``` From 31f3424c5a726f3c3223584600a27523413cbe0a Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 12:19:18 +0700 Subject: [PATCH 103/363] Updated Dependency Injection (markdown) --- Dependency-Injection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dependency-Injection.md b/Dependency-Injection.md index a83896dc..056f5156 100644 --- a/Dependency-Injection.md +++ b/Dependency-Injection.md @@ -26,7 +26,7 @@ You can get service by `MapContext.Current.GetService<>()`, for example ```csharp config.NewConfig() - .Map(dest => dest.Name, src => MapContext.Current.GetService(src.Name)); + .Map(dest => dest.Name, src => MapContext.Current.GetService().Format(src.Name)); ``` ##### Mapping From 5997ef27b8a1cea64172622e1690aa5dfe06540e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 12:32:53 +0700 Subject: [PATCH 104/363] Updated Home (markdown) --- Home.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/Home.md b/Home.md index 5c75838d..fc634a8c 100644 --- a/Home.md +++ b/Home.md @@ -9,17 +9,17 @@ | `query.ProjectToType()` | Mapping from queryable | [basic](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) | | | Convention & Data type support | [data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) | -### Mapper instance +#### Mapper instance | Method | Description | Link | | ------------- |-----------------------| ----- | -| `IAdapter adapter = new Adapter()` | Create mapper instance | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | -| `adapter.Adapt(src)` | Mapping to new type | | -| `adapter.Adapt(src, dest)` | Mapping to existing object | | +| `IMapper mapper = new Mapper()` | Create mapper instance | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | +| `mapper.Map(src)` | Mapping to new type | | +| `mapper.Map(src, dest)` | Mapping to existing object | | #### Builder | Method | Description | Link | | ------------- |-----------------------| ----- | -| `src.BuildAdapter()` | Create builder | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | +| `src.BuildAdapter()`
`mapper.From(src)` | Create builder | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | | `.ForkConfig(config => ...)` | Inline configuration | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | | `.AddParameters(name, value)` | Passing runtime value | [setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) | | `.AdaptToType()` | Mapping to new type | | @@ -34,7 +34,7 @@ | `TypeAdapterConfig.GlobalSettings` | Global config | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | | `var config = new TypeAdapterConfig()` | Create new config instance | [config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) | | `src.Adapt(config)` | Passing config to mapping | | -| `new Adapter(config)` | Passing config to mapper instance | | +| `new Mapper(config)` | Passing config to mapper instance | | | `src.BuildAdapter(config)` | Passing config to builder | | | `config.RequireDestinationMemberSource` | Validate all properties are mapped | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | | `config.RequireExplicitMapping` | Validate all type pairs are defined | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | @@ -59,11 +59,9 @@ | Method | Description | Link | | ------------- |-----------------------| ----- | | `config.Default` | Get setting applied to all type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `TypeAdapterConfig.NewConfig()` | Create setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `config.NewConfig()` | Create setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `TypeAdapterConfig.ForType()` | Get setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `config.ForType()` | Get setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `config.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))` | Get setting applied to generic type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `TypeAdapterConfig.NewConfig()`
`config.NewConfig()` | Create setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `TypeAdapterConfig.ForType()`
`config.ForType()` | Get setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | +| `config.ForType(typeof(GenericPoco<>),typeof(GenericDto<>))` | Get setting applied to generic type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | | `config.When((src, dest, mapType) => ...)` | Get setting that applied conditionally | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | | `config.ForDestinationType()` | Get setting that applied to specific destination type | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | | | Configuration for nested mapping | [nested mapping](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-mapping) @@ -99,10 +97,24 @@ | `ShallowCopyForSameType` | Direct assign rather than deep clone if type pairs are the same | | [shallow & merge](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) | | `TwoWays` | Define type mapping are 2 ways | x | [2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | | `Unflattening` | Allow unflatten mapping | x |[2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | +| `UseDestinationValue` | Use existing property object to map data | |[readonly-prop](https://github.com/MapsterMapper/Mapster/wiki/Mapping-readonly-prop) | #### Attributes | Annotation | Description | Link | | ------------- |-----------------------| ----- | | `[AdaptMember(name)]` | Mapping property to different name | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | -| `[AdaptIgnore]` | Ignore property from mapping | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | +| `[AdaptIgnore(side)]` | Ignore property from mapping | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | +| `[UseDestinationValue]` | Use existing property object to map data | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | + +#### Plugins + +| Plugin | Method | Description | Link | +| ------ | ------------- |-----------------------| ----- | +| Async | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | [async](https://github.com/MapsterMapper/Mapster/wiki/Async) | +| Codegen | `builder.CreateMapExpression().ToScript()` | generate mapping code | [codegen](https://github.com/MapsterMapper/Mapster/wiki/CodeGen), [debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) | +| Debugging | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | [debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) | +| Dependency Injection | `MapContext.Current.GetService()` | Inject service into mapping logic | [injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) | +| EF 6 & EF Core | `builder.EntityFromContext` | Copy data to tracked EF entity | [ef](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) | +| FastExpressionCompiler | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | [fec](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) | +| Json.net | `config.EnableJsonMapping()` | map json from/to poco and string | [json](https://github.com/MapsterMapper/Mapster/wiki/Json.net) | \ No newline at end of file From 39c6549abde2dd096de078f0a242e96499a93e10 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 12:35:44 +0700 Subject: [PATCH 105/363] Updated Home (markdown) --- Home.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Home.md b/Home.md index fc634a8c..d8c03fe2 100644 --- a/Home.md +++ b/Home.md @@ -109,12 +109,12 @@ #### Plugins -| Plugin | Method | Description | Link | -| ------ | ------------- |-----------------------| ----- | -| Async | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | [async](https://github.com/MapsterMapper/Mapster/wiki/Async) | -| Codegen | `builder.CreateMapExpression().ToScript()` | generate mapping code | [codegen](https://github.com/MapsterMapper/Mapster/wiki/CodeGen), [debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) | -| Debugging | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | [debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) | -| Dependency Injection | `MapContext.Current.GetService()` | Inject service into mapping logic | [injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) | -| EF 6 & EF Core | `builder.EntityFromContext` | Copy data to tracked EF entity | [ef](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) | -| FastExpressionCompiler | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | [fec](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) | -| Json.net | `config.EnableJsonMapping()` | map json from/to poco and string | [json](https://github.com/MapsterMapper/Mapster/wiki/Json.net) | \ No newline at end of file +| Plugin | Method | Description | +| ------ | ------------- |-----------------------| +| [Async](https://github.com/MapsterMapper/Mapster/wiki/Async) | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | +| [Codegen](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) | `builder.CreateMapExpression().ToScript()` | generate mapping code | +| [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | +| [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) | `MapContext.Current.GetService()` | Inject service into mapping logic | +| [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) | `builder.EntityFromContext` | Copy data to tracked EF entity | +| [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | +| [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) | `config.EnableJsonMapping()` | map json from/to poco and string | \ No newline at end of file From 74cf6fe5a3d38977a5180f96b6ff66120c4c4018 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 12:36:14 +0700 Subject: [PATCH 106/363] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index d8c03fe2..9f9b7495 100644 --- a/Home.md +++ b/Home.md @@ -116,5 +116,5 @@ | [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | | [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) | `MapContext.Current.GetService()` | Inject service into mapping logic | | [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) | `builder.EntityFromContext` | Copy data to tracked EF entity | -| [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | +| [FEC](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | | [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) | `config.EnableJsonMapping()` | map json from/to poco and string | \ No newline at end of file From 713e03c961236bc45b24519a4d920521fa985eb5 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Feb 2020 12:50:20 +0700 Subject: [PATCH 107/363] Updated Data types (markdown) --- Data-types.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Data-types.md b/Data-types.md index d7517739..50226b0b 100644 --- a/Data-types.md +++ b/Data-types.md @@ -64,8 +64,9 @@ var dto = staff.Adapt(); Mappable Object types are included: - POCO classes - POCO structs +- POCO interfaces - Dictionary type implement `IDictionary` -- Record types +- Record types (either class, struct, and interface) Example for object to dictionary From f9e2dc1ceda50a13d66baa445c382dcb087343b7 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Tue, 4 Feb 2020 06:24:43 +0700 Subject: [PATCH 108/363] Updated Custom mapping (markdown) --- Custom-mapping.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Custom-mapping.md b/Custom-mapping.md index 5ce7a529..26126ca0 100644 --- a/Custom-mapping.md +++ b/Custom-mapping.md @@ -67,35 +67,35 @@ From above example, if `src.Child` is null, mapping will return null instead of ### Multiple sources -**Example 1**: Mapping Dto+SubDto to Poco +**Example 1**: Include property to Poco ```csharp public class SubDto { - public string Extras { get; set; } + public string Extra { get; set; } } public class Dto { public string Name { get; set; } - public SubDto Details { get; set; } + public SubDto SubDto { get; set; } } public class Poco { public string Name { get; set; } - public string Extras { get; set; } + public string Extra { get; set; } } ``` -To configure Dto+SubDto to poco +In this case, you would like to map all properties from `Dto` to `Poco`, and also include all properties from `Dto.SubDto` to `Poco`. You can do this by just mapping `dto.SubDto` to `poco` in configuration. ```csharp TypeAdapterConfig.NewConfig() - .Map(dest => dest, src => src.Details); + .Map(poco => poco, dto => dto.SubDto); ``` -**Example 2**: 2 different dto to poco +**Example 2**: Mapping 2 objects to poco -You can use tuple to do this +In this example, you have `Dto1` and `Dto2`, and you would like to map both objects to a `Poco`. You can do this by wrap `Dto1` and `Dto2` into tuple. And then mapping `tuple.Item1` and `tuple.Item2` to `Poco`. ```csharp TypeAdapterConfig<(Dto1, Dto2), Poco>.NewConfig() From bad83cf1ffdac3d6c906fc4e03918297c99971ef Mon Sep 17 00:00:00 2001 From: chaowlert Date: Tue, 4 Feb 2020 06:25:52 +0700 Subject: [PATCH 109/363] Updated Custom mapping (markdown) --- Custom-mapping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Custom-mapping.md b/Custom-mapping.md index 26126ca0..f0810c47 100644 --- a/Custom-mapping.md +++ b/Custom-mapping.md @@ -95,7 +95,7 @@ TypeAdapterConfig.NewConfig() **Example 2**: Mapping 2 objects to poco -In this example, you have `Dto1` and `Dto2`, and you would like to map both objects to a `Poco`. You can do this by wrap `Dto1` and `Dto2` into tuple. And then mapping `tuple.Item1` and `tuple.Item2` to `Poco`. +In this example, you have `Dto1` and `Dto2`, and you would like to map both objects to a `Poco`. You can do this by wrapping `Dto1` and `Dto2` into a tuple. And then mapping `tuple.Item1` and `tuple.Item2` to `Poco`. ```csharp TypeAdapterConfig<(Dto1, Dto2), Poco>.NewConfig() From c21bc0c4c3cabf0764173f30ff6a63ed6d674f35 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Tue, 4 Feb 2020 21:25:52 +0700 Subject: [PATCH 110/363] Updated Config instance (markdown) --- Config-instance.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Config-instance.md b/Config-instance.md index 4b45ebdd..ec1feb05 100644 --- a/Config-instance.md +++ b/Config-instance.md @@ -27,11 +27,11 @@ You can apply a specific config instance by passing it to the `Adapt` method. (N var result = src.Adapt(config); ``` -Or to an Adapter instance. +Or to an Mapper instance. ```csharp -var adapter = new Adapter(config); -var result = adapter.Adapt(src); +var mapper = new Mapper(config); +var result = mapper.Map(src); ``` ### Clone From 734c39acff67987771ec34c44370036663b10c7c Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 14 Mar 2020 19:40:36 +0700 Subject: [PATCH 111/363] Updated Setting values (markdown) --- Setting-values.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Setting-values.md b/Setting-values.md index 45790025..232ffd2a 100644 --- a/Setting-values.md +++ b/Setting-values.md @@ -23,6 +23,11 @@ TypeAdapterConfig.NewConfig() .AddDestinationTransforms((string x) => x ?? ""); ``` +**Return empty collection if null** +```csharp +config.Default.AddDestinationTransform(DestinationTransform.EmptyCollectionIfNull); +``` + ### Passing run-time value In some cases, you might would like to pass runtime values (ie, current user). On configuration, we can receive run-time value by `MapContext.Current.Parameters`. From 74925ec44fdd0ed9d75cf2dac89c3597d150e5c1 Mon Sep 17 00:00:00 2001 From: qiqigou <48587985+qiqigou@users.noreply.github.com> Date: Tue, 17 Mar 2020 14:06:57 +0800 Subject: [PATCH 112/363] Created _Footer (markdown) --- _Footer.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 _Footer.md diff --git a/_Footer.md b/_Footer.md new file mode 100644 index 00000000..de423499 --- /dev/null +++ b/_Footer.md @@ -0,0 +1 @@ +00000000000 \ No newline at end of file From db87e1e9f28aa3e32922d488778e8299bd485209 Mon Sep 17 00:00:00 2001 From: qiqigou <48587985+qiqigou@users.noreply.github.com> Date: Tue, 17 Mar 2020 14:07:31 +0800 Subject: [PATCH 113/363] Destroyed _Footer (markdown) --- _Footer.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 _Footer.md diff --git a/_Footer.md b/_Footer.md deleted file mode 100644 index de423499..00000000 --- a/_Footer.md +++ /dev/null @@ -1 +0,0 @@ -00000000000 \ No newline at end of file From 35ad1e7c7ab39a7fdf77d3b66360436ee488857d Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 10 Apr 2020 08:39:50 +0700 Subject: [PATCH 114/363] Updated Setting values (markdown) --- Setting-values.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Setting-values.md b/Setting-values.md index 232ffd2a..81ccd1c2 100644 --- a/Setting-values.md +++ b/Setting-values.md @@ -14,13 +14,13 @@ While `Map` method specify logic for single property, `AddDestinationTransform` **Trim string** ```csharp TypeAdapterConfig.NewConfig() - .AddDestinationTransforms((string x) => x.Trim()); + .AddDestinationTransform((string x) => x.Trim()); ``` **Null replacement** ```csharp TypeAdapterConfig.NewConfig() - .AddDestinationTransforms((string x) => x ?? ""); + .AddDestinationTransform((string x) => x ?? ""); ``` **Return empty collection if null** From a577d4b31b852a99e96bc4d77c8e041ec09f5a6a Mon Sep 17 00:00:00 2001 From: gt4dev Date: Wed, 27 May 2020 13:51:54 +0200 Subject: [PATCH 115/363] there was misspelling --- Mapping-non-public-members.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mapping-non-public-members.md b/Mapping-non-public-members.md index 32e8ef61..2047729f 100644 --- a/Mapping-non-public-members.md +++ b/Mapping-non-public-members.md @@ -1,13 +1,13 @@ -### EnableNonPublicMember +### EnableNonPublicMembers This will allow Mapster to set to all non-public members. ```csharp //type pair -TypeAdapterConfig.NewConfig().EnableNonPublicMember(true); +TypeAdapterConfig.NewConfig().EnableNonPublicMembers(true); //global -TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); +TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMembers(true); ``` ### AdaptMember attribute @@ -48,6 +48,6 @@ TypeAdapterConfig.GlobalSettings.Default If type doesn't contain public properties, Mapster will treat type as primitive, you must also declare type pair to ensure Mapster will apply non-public member mapping. ```csharp -TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMember(true); +TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMembers(true); TypeAdapterConfig.NewConfig(); ``` From 3865ad6bf2c5809ca88b926a2588d8cf22da3c00 Mon Sep 17 00:00:00 2001 From: Yaroslav <17689846+oobarbazanoo@users.noreply.github.com> Date: Thu, 4 Jun 2020 22:27:27 +0300 Subject: [PATCH 116/363] Updated _Sidebar (markdown) --- _Sidebar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 95cf84e7..bbda6cf9 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -1,4 +1,4 @@ -### Getting Started +### Getting Started foo ##### [References](https://github.com/MapsterMapper/Mapster/wiki) ##### Mapping From ddb0bf42fb5905c6609b47b7b8f9bec7d66ef864 Mon Sep 17 00:00:00 2001 From: Yaroslav <17689846+oobarbazanoo@users.noreply.github.com> Date: Thu, 4 Jun 2020 22:29:18 +0300 Subject: [PATCH 117/363] Updated _Sidebar (markdown) --- _Sidebar.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index bbda6cf9..d71f3636 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -1,4 +1,5 @@ -### Getting Started foo +### Getting Started +### FWIW YOU SHOULD RESTRICT THE ACCESS TO THE MODIFICATION OF THE CONTENT. OTHERWISE IT MAY NOT BE CONSIDERED CREDIBLE. ##### [References](https://github.com/MapsterMapper/Mapster/wiki) ##### Mapping From 3009fba8dd2abc88509e85926701879326e079bc Mon Sep 17 00:00:00 2001 From: Yaroslav <17689846+oobarbazanoo@users.noreply.github.com> Date: Thu, 4 Jun 2020 23:02:50 +0300 Subject: [PATCH 118/363] Updated Dependency Injection (markdown) --- Dependency-Injection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dependency-Injection.md b/Dependency-Injection.md index 056f5156..6f39f07a 100644 --- a/Dependency-Injection.md +++ b/Dependency-Injection.md @@ -12,7 +12,7 @@ On startup, register `TypeAdapterConfig`, and `ServiceMapper`. public void ConfigureServices(IServiceCollection services) { ... - services.AddSingleton(config); + services.AddSingleton(config); //what does it mean??? services.AddScoped(); ... } From a5cd9eb6bdfd2076067d4d13519a6b3cbe5aa669 Mon Sep 17 00:00:00 2001 From: Yaroslav <17689846+oobarbazanoo@users.noreply.github.com> Date: Thu, 4 Jun 2020 23:03:03 +0300 Subject: [PATCH 119/363] Updated _Sidebar (markdown) --- _Sidebar.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/_Sidebar.md b/_Sidebar.md index d71f3636..95cf84e7 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -1,5 +1,4 @@ -### Getting Started -### FWIW YOU SHOULD RESTRICT THE ACCESS TO THE MODIFICATION OF THE CONTENT. OTHERWISE IT MAY NOT BE CONSIDERED CREDIBLE. +### Getting Started ##### [References](https://github.com/MapsterMapper/Mapster/wiki) ##### Mapping From f03aff933f3c60b08285f8e714a91d85a5c61fd6 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 5 Jun 2020 06:14:08 +0700 Subject: [PATCH 120/363] Revert 3009fba^...3009fba on Dependency Injection --- Dependency-Injection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dependency-Injection.md b/Dependency-Injection.md index 6f39f07a..056f5156 100644 --- a/Dependency-Injection.md +++ b/Dependency-Injection.md @@ -12,7 +12,7 @@ On startup, register `TypeAdapterConfig`, and `ServiceMapper`. public void ConfigureServices(IServiceCollection services) { ... - services.AddSingleton(config); //what does it mean??? + services.AddSingleton(config); services.AddScoped(); ... } From a47fa4cd3faacf68dfafe2c43dcdedf0aaa5857d Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 5 Jun 2020 08:48:11 +0700 Subject: [PATCH 121/363] Updated Dependency Injection (markdown) --- Dependency-Injection.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dependency-Injection.md b/Dependency-Injection.md index 056f5156..d7a50be0 100644 --- a/Dependency-Injection.md +++ b/Dependency-Injection.md @@ -12,6 +12,9 @@ On startup, register `TypeAdapterConfig`, and `ServiceMapper`. public void ConfigureServices(IServiceCollection services) { ... + var config = new TypeAdapterConfig(); + // Or + // var config = TypeAdapterConfig.GlobalSettings; services.AddSingleton(config); services.AddScoped(); ... From 34586df6829c551107aeae605ef29be9e125dbaf Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 30 Jul 2020 07:29:48 +0700 Subject: [PATCH 122/363] Updated Home (markdown) --- Home.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Home.md b/Home.md index 9f9b7495..3ff094ed 100644 --- a/Home.md +++ b/Home.md @@ -9,14 +9,14 @@ | `query.ProjectToType()` | Mapping from queryable | [basic](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) | | | Convention & Data type support | [data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) | -#### Mapper instance +#### Mapper instance (for dependency injection) | Method | Description | Link | | ------------- |-----------------------| ----- | | `IMapper mapper = new Mapper()` | Create mapper instance | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | | `mapper.Map(src)` | Mapping to new type | | | `mapper.Map(src, dest)` | Mapping to existing object | | -#### Builder +#### Builder (for complex mapping) | Method | Description | Link | | ------------- |-----------------------| ----- | | `src.BuildAdapter()`
`mapper.From(src)` | Create builder | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | @@ -76,6 +76,7 @@ | `ConstructUsing` | Define how to create object | x | [constructor](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) | | `EnableNonPublicMembers` | Mapping non-public properties | | [non-public](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) | | `EnumMappingStrategy` | Choose whether mapping enum by value or by name | | [data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) | +| `Fork` | Add new settings without side effect on main config | x | [nested mapping](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-mapping) | | `GetMemberName` | Define how to resolve property name | x | [custom naming](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) | | `Ignore` | Ignore specific properties | x | [ignore](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) | | `IgnoreAttribute` | Ignore specific attributes annotated on properties | x | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | @@ -117,4 +118,5 @@ | [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) | `MapContext.Current.GetService()` | Inject service into mapping logic | | [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) | `builder.EntityFromContext` | Copy data to tracked EF entity | | [FEC](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | +| [Immutable](https://github.com/MapsterMapper/Mapster/wiki/Immutable) | `config.EnableImmutableMapping()` | mapping to immutable collection | | [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) | `config.EnableJsonMapping()` | map json from/to poco and string | \ No newline at end of file From dc6cd1dfb428933b3cdafee9f5c1bcefbed62765 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 30 Jul 2020 07:42:47 +0700 Subject: [PATCH 123/363] Updated Config for nested mapping (markdown) --- Config-for-nested-mapping.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Config-for-nested-mapping.md b/Config-for-nested-mapping.md index eed2c801..49abc1d1 100644 --- a/Config-for-nested-mapping.md +++ b/Config-for-nested-mapping.md @@ -1,10 +1,11 @@ -Configuration is per type pair, not per type hierarchy. For example if you have parent and child classes. +For example if you have parent and child classes. ```csharp class ParentPoco { public string Id { get; set; } public List Children { get; set; } + public string Name { get; set; } } class ChildPoco { @@ -24,7 +25,7 @@ TypeAdapterConfig.NewConfig() .PreserveReference(true); ``` -When mapping, child type will not get effect from `PreserveReference`. +By default, children types will not get effect from `PreserveReference`. To do so, you must specify all type pairs inside `ParentPoco`. @@ -43,10 +44,22 @@ Or you can set `PreserveReference` in global setting. TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); ``` -If you don't want to set config in global setting, you can also use `Fork`. +### Fork + +You can use `Fork` command to define config that applies only specified mapping down to nested mapping without polluting global setting. ```csharp -var forked = TypeAdapterConfig.GlobalSettings.Fork(config => - config.Default.PreserveReference(true)); -var parentDto = parentPoco.Adapt(forked); +TypeAdapterConfig.NewConfig() + .Fork(config => config.Default.PreserveReference(true)); ``` + +**Ignore if string is null or empty** + +Another example, Mapster only can ignore null value ([IgnoreNullValues](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge#copy-vs-merge)), however, you use `Fork` to ignore null or empty. + +```csharp +TypeAdapterConfig.NewConfig() + .Fork(config => config.ForType() + .MapToTargetWith((src, dest) => string.IsNullOrEmpty(src) ? dest : src) + ); +``` \ No newline at end of file From a53433906006a9c3915ec6c9f037c83dc4ea0bda Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 30 Jul 2020 07:47:58 +0700 Subject: [PATCH 124/363] Created Immutable (markdown) --- Immutable.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Immutable.md diff --git a/Immutable.md b/Immutable.md new file mode 100644 index 00000000..5e643281 --- /dev/null +++ b/Immutable.md @@ -0,0 +1,35 @@ +# Mapster.Immutable +Immutable collection supports + +### Install + + PM> Install-Package Mapster.Immutable + +### Usage + +Call `EnableImmutableMapping` from your `TypeAdapterConfig` to enable Immutable collection. + +```csharp +TypeAdapterConfig.GlobalSettings.EnableImmutableMapping(); +``` + +or + +```csharp +config.EnableImmutableMapping(); +``` + +This will allow mapping to +- `IImmutableDictionary<,>` +- `IImmutableList<>` +- `IImmutableQueue<>` +- `IImmutableSet<>` +- `IImmutableStack<>` +- `ImmutableArray<>` +- `ImmutableDictionary<,>` +- `ImmutableHashSet<>` +- `ImmutableList<>` +- `ImmutableQueue<>` +- `ImmutableSortedDictionary<,>` +- `ImmutableSortedSet<>` +- `ImmutableStack<>` From 6b21c6d4e4bb84d1b31e36da4e93d6e6f281cd0f Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 30 Jul 2020 07:48:58 +0700 Subject: [PATCH 125/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_Sidebar.md b/_Sidebar.md index 95cf84e7..4099c519 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -39,4 +39,5 @@ * [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) * [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) * [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) +* [Immutable](https://github.com/MapsterMapper/Mapster/wiki/Immutable) * [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) From 4f471fd1c61edcb8ac7499b7b45ff950263d77c6 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 30 Jul 2020 08:16:25 +0700 Subject: [PATCH 126/363] Updated Data types (markdown) --- Data-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data-types.md b/Data-types.md index 50226b0b..612aa180 100644 --- a/Data-types.md +++ b/Data-types.md @@ -28,7 +28,7 @@ var i = "123".Adapt(); //equal to int.Parse("123"); ``` ### Collections -This includes mapping among lists, arrays, collections, dictionary including various interfaces: IList, ICollection, IEnumerable etc... +This includes mapping among lists, arrays, collections, dictionary including various interfaces: `IList`, `ICollection`, `IEnumerable`, `ISet`, `IDictionary` etc... ```csharp var list = db.Pocos.ToList(); var target = list.Adapt>(); From 13c187005654b298a7fc811189b035880654f161 Mon Sep 17 00:00:00 2001 From: Vinicius Rocha Date: Fri, 31 Jul 2020 15:15:27 -0400 Subject: [PATCH 127/363] Fix variable name on code example. --- Data-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data-types.md b/Data-types.md index 612aa180..6b4e1958 100644 --- a/Data-types.md +++ b/Data-types.md @@ -72,7 +72,7 @@ Example for object to dictionary ```csharp var point = new { X = 2, Y = 3 }; -var dict = src.Adapt>(); +var dict = point.Adapt>(); dict["Y"].ShouldBe(3); ``` From d7c02b4a3c11d5fcf2cdfcbfdefdd8a5086f6c5e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 00:25:18 +0700 Subject: [PATCH 128/363] Updated CodeGen (markdown) --- CodeGen.md => TextTamplate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename CodeGen.md => TextTamplate.md (99%) diff --git a/CodeGen.md b/TextTamplate.md similarity index 99% rename from CodeGen.md rename to TextTamplate.md index be2c11a1..bdf13c54 100644 --- a/CodeGen.md +++ b/TextTamplate.md @@ -1,4 +1,4 @@ -### Code generation +### TextTamplate ``` PM> Install-Package ExpressionTranslator ``` From ba0a81eee7e94e0bbd8b979327710bf326c29dc0 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 00:25:42 +0700 Subject: [PATCH 129/363] Updated Home (markdown) --- Home.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Home.md b/Home.md index 3ff094ed..f44d5d03 100644 --- a/Home.md +++ b/Home.md @@ -113,10 +113,16 @@ | Plugin | Method | Description | | ------ | ------------- |-----------------------| | [Async](https://github.com/MapsterMapper/Mapster/wiki/Async) | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | -| [Codegen](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) | `builder.CreateMapExpression().ToScript()` | generate mapping code | | [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | | [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) | `MapContext.Current.GetService()` | Inject service into mapping logic | | [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) | `builder.EntityFromContext` | Copy data to tracked EF entity | | [FEC](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | | [Immutable](https://github.com/MapsterMapper/Mapster/wiki/Immutable) | `config.EnableImmutableMapping()` | mapping to immutable collection | -| [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) | `config.EnableJsonMapping()` | map json from/to poco and string | \ No newline at end of file +| [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) | `config.EnableJsonMapping()` | map json from/to poco and string | + +#### Code Generation Tools + +| Plugin | Tool | Description | +| ------ | ------------- |-----------------------| +| [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | `dotnet mapster` | automatically generate mapping codes on build | +| [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTamplate) | `t4` | generate mapping codes using t4 | From bcfcc46d573668082bebb6b98549cac0630ffcca Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 00:26:51 +0700 Subject: [PATCH 130/363] Updated _Sidebar (markdown) --- _Sidebar.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 4099c519..5d3be0a8 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -34,10 +34,13 @@ ##### Plugins * [Async Support](https://github.com/MapsterMapper/Mapster/wiki/Async) -* [Code Generation](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) * [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) * [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) * [FastExpressionCompiler](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) * [Immutable](https://github.com/MapsterMapper/Mapster/wiki/Immutable) * [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) + +##### Tools +* [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) +* [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) From 81cb3201a865029f42d978c4a0cf2af76888019d Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 00:28:05 +0700 Subject: [PATCH 131/363] Updated TextTamplate (markdown) --- TextTamplate.md => TextTemplate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename TextTamplate.md => TextTemplate.md (99%) diff --git a/TextTamplate.md b/TextTemplate.md similarity index 99% rename from TextTamplate.md rename to TextTemplate.md index bdf13c54..dcce5783 100644 --- a/TextTamplate.md +++ b/TextTemplate.md @@ -1,4 +1,4 @@ -### TextTamplate +### TextTemplate ``` PM> Install-Package ExpressionTranslator ``` From fa24735ca89724f838783095320be7256e1600d6 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 00:28:21 +0700 Subject: [PATCH 132/363] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index f44d5d03..19ffa32c 100644 --- a/Home.md +++ b/Home.md @@ -125,4 +125,4 @@ | Plugin | Tool | Description | | ------ | ------------- |-----------------------| | [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | `dotnet mapster` | automatically generate mapping codes on build | -| [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTamplate) | `t4` | generate mapping codes using t4 | +| [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) | `t4` | generate mapping codes using t4 | From 35fe9141071f4fcda9d7febca2b5926afe536ab2 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 01:25:26 +0700 Subject: [PATCH 133/363] Created Mapster.Tool (markdown) --- Mapster.Tool.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Mapster.Tool.md diff --git a/Mapster.Tool.md b/Mapster.Tool.md new file mode 100644 index 00000000..20399317 --- /dev/null +++ b/Mapster.Tool.md @@ -0,0 +1,70 @@ +### Mapster.Tool + +Here are steps to add code generation. + +1. Define interface to generate code. Your interface must annotate with `[Mapper]` in order for tool to pickup for generation. + +This is example interface. +```csharp +[Mapper] +public interface IProductMapper +{ + ProductDTO Map(Product customer); +} +``` + +You can add multiple members as you want. All member names are flexible. +```csharp +[Mapper] +public interface ICustomerMapper +{ + //for queryable + Expression> ProjectToDto { get; } + + //map from POCO to DTO + CustomerDTO MapToDto(Customer customer); + + //map back from DTO to POCO + Customer MapToPoco(CustomerDTO dto); + + //map to existing object + Customer MapToExisting(CustomerDTO dto, Customer customer); +} +``` + +At this point, you can use your interface in your code. + +```csharp +var dtos = context.Customers.Select(mapper.ProjectToDto); +var dto = mapper.MapToDto(poco); +mapper.MapToExisting(dto, poco); +``` + +2. Install `Mapster.Tool` + +You can install per repo + +```bash +#skip this step if you already have dotnet-tools.json +dotnet new tool-manifest + +dotnet tool install Mapster.Tool +``` + +Or you can install globally on your machine + +```bash +dotnet tool install -g Mapster.Tool +``` + +NOTE: the tool required .NET Core 2.1 or .NET Core 3.1 on your machine. + +3. Add setting to you `csproj` + +```xml + + + +``` + +That's it, now Mapster will automatically generate codes after build. \ No newline at end of file From 28b991549e222f7bdaa7e97ca44a9f00684fa80e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 01:31:14 +0700 Subject: [PATCH 134/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 20399317..0d70ff41 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -32,7 +32,19 @@ public interface ICustomerMapper } ``` -At this point, you can use your interface in your code. +If you have configuration, it must be in `IRegister` + +```csharp +public class MyRegister : IRegister +{ + public void Register(TypeAdapterConfig config) + { + config.NewConfig(); + } +} +``` + +At this point, you can use your interface in your code to perform mapping. ```csharp var dtos = context.Customers.Select(mapper.ProjectToDto); @@ -67,4 +79,9 @@ NOTE: the tool required .NET Core 2.1 or .NET Core 3.1 on your machine. ``` -That's it, now Mapster will automatically generate codes after build. \ No newline at end of file +That's it, now Mapster will automatically generate codes after build. + +#### Example + +You can find example in Benchmark project +- https://github.com/MapsterMapper/Mapster/tree/master/src/Benchmark/Mappers \ No newline at end of file From a10a1a46f8d67f6333b46d47f14da3aec7a3dc57 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 02:02:19 +0700 Subject: [PATCH 135/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 0d70ff41..6d46732e 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -54,8 +54,6 @@ mapper.MapToExisting(dto, poco); 2. Install `Mapster.Tool` -You can install per repo - ```bash #skip this step if you already have dotnet-tools.json dotnet new tool-manifest @@ -63,18 +61,13 @@ dotnet new tool-manifest dotnet tool install Mapster.Tool ``` -Or you can install globally on your machine - -```bash -dotnet tool install -g Mapster.Tool -``` - NOTE: the tool required .NET Core 2.1 or .NET Core 3.1 on your machine. 3. Add setting to you `csproj` ```xml + ``` From 0e6d48924b001f7fbbbe9a8dc5168671d5bf7eae Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 2 Aug 2020 08:12:14 +0700 Subject: [PATCH 136/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 6d46732e..ea4df634 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -52,7 +52,7 @@ var dto = mapper.MapToDto(poco); mapper.MapToExisting(dto, poco); ``` -2. Install `Mapster.Tool` +2. Install `Mapster.Tool` from your project root ```bash #skip this step if you already have dotnet-tools.json From e781d196b92d54b8f48a4b1325ae7414999d1c19 Mon Sep 17 00:00:00 2001 From: Atilla Lonny Date: Thu, 13 Aug 2020 08:15:31 -0400 Subject: [PATCH 137/363] A bit of grammar corrections. --- Debugging.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Debugging.md b/Debugging.md index f9f3d5c2..88b2bc6f 100644 --- a/Debugging.md +++ b/Debugging.md @@ -12,7 +12,7 @@ Then add following code on start up (or anywhere before mapping is compiled) TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(); ``` -Now on your mapping code (only in `DEBUG` mode). +Now in your mapping code (only in `DEBUG` mode). ```csharp var dto = poco.Adapt(); //<--- you can step-into this function!! @@ -22,11 +22,11 @@ var dto = poco.Adapt(); //<--- you can step-into this fun ##### Using internal classes or members -`private`, `protected` and `internal` don't allow in debug mode. +`private`, `protected` and `internal` aren't allowed in debug mode. ### Get mapping script -We can also see how Mapster generate mapping logic with `ToScript` method. +We can also see how Mapster generates mapping logic with `ToScript` method. ``` var script = poco.BuildAdapter() @@ -44,8 +44,8 @@ var dto = poco.Adapt(); //<-- you can step-into this func ``` ### Do not worry about performance -In `RELEASE` mode, Roslyn compiler actually faster than default dynamic compilation by 2x. -Here is the result. +In `RELEASE` mode, Roslyn compiler is actually faster than default dynamic compilation by 2x. +Here is the result: | Method | Mean | StdDev | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | |-------------------------- |---------------:|-------------:|-------------:|------------:|------:|------:|-----------:| From d20913cb45c7aa90c80f9794a97a875125c449e5 Mon Sep 17 00:00:00 2001 From: Atilla Lonny Date: Thu, 13 Aug 2020 08:18:34 -0400 Subject: [PATCH 138/363] A bit of grammar corrections. --- Data-types.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Data-types.md b/Data-types.md index 6b4e1958..296fdab8 100644 --- a/Data-types.md +++ b/Data-types.md @@ -7,7 +7,7 @@ decimal i = 123.Adapt(); //equal to (decimal)123; ### Enums Mapster maps enums to numerics automatically, but it also maps strings to and from enums automatically in a fast manner. -The default Enum.ToString() in .Net is quite slow. The implementation in Mapster is double the speed. Likewise, a fast conversion from strings to enums is also included. If the string is null or empty, the enum will initialize to the first enum value. +The default Enum.ToString() in .NET is quite slow. The implementation in Mapster is double the speed. Likewise, a fast conversion from strings to enums is also included. If the string is null or empty, the enum will initialize to the first enum value. In Mapster, flagged enums are also supported. ```csharp @@ -21,7 +21,7 @@ TypeAdapterConfig.GlobalSettings.Default ``` ### Strings -When Mapster maps other types to string, Mapster will use `ToString` method. And when ever Mapster maps string to the other types, Mapster will use `Parse` method. +When Mapster maps other types to string, Mapster will use `ToString` method. And whenever Mapster maps string to the other types, Mapster will use `Parse` method. ```csharp var s = 123.Adapt(); //equal to 123.ToString(); var i = "123".Adapt(); //equal to int.Parse("123"); @@ -35,7 +35,7 @@ var target = list.Adapt>(); ``` ### Mappable Objects -Mapster can map 2 different objects using the following rules +Mapster can map two different objects using the following rules - Source and destination property names are the same. Ex: `dest.Name = src.Name` - Source has get method. Ex: `dest.Name = src.GetName()` - Source property has child object which can flatten to destination. Ex: `dest.ContactName = src.Contact.Name` or `dest.Contact_Name = src.Contact.Name` @@ -68,7 +68,7 @@ Mappable Object types are included: - Dictionary type implement `IDictionary` - Record types (either class, struct, and interface) -Example for object to dictionary +Example for object to dictionary: ```csharp var point = new { X = 2, Y = 3 }; @@ -93,4 +93,4 @@ var src = new { Name = "Mapster", Age = 3 }; var target = src.Adapt(); ``` -There is limitation on record type mapping. Record type must not have setter and have only one non-empty constructor. And all parameter names must match with properties. +There are limitations on record type mapping. Record type must not have a setter and have only one non-empty constructor, and all parameter names must match with properties. From 6e59064b3470774d5979670ffed45e94c6b261f0 Mon Sep 17 00:00:00 2001 From: Atilla Lonny Date: Thu, 13 Aug 2020 08:29:14 -0400 Subject: [PATCH 139/363] A bit of grammar corrections. --- Configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Configuration.md b/Configuration.md index 8234c0e5..7cf0c487 100644 --- a/Configuration.md +++ b/Configuration.md @@ -57,7 +57,7 @@ TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == ### Destination type only -Setting also be able to done without knowing the source type, by using `ForDestinationType`. For example, you can do `AfterMapping` setting to validate after mapping. +A setting can also be created without knowing the source type, by using `ForDestinationType`. For example, you can do `AfterMapping` setting to validate after mapping. ```csharp TypeAdapterConfig.GlobalSettings.ForDestinationType() @@ -68,7 +68,7 @@ NOTE: `ForDestinationType` above will always apply to all types assignable to `I ### Open generics -If mapping type is generic, you can create setting by passing generic type definition to `ForType`. +If the mapping type is generic, you can create a setting by passing generic type definition to `ForType`. ```csharp TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) From 51b1b6fc4ea3a5c621efff490f4444a323783a7e Mon Sep 17 00:00:00 2001 From: Atilla Lonny Date: Thu, 13 Aug 2020 08:31:42 -0400 Subject: [PATCH 140/363] A bit of grammar corrections. --- Config-inheritance.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Config-inheritance.md b/Config-inheritance.md index 21ce8365..c6589e2f 100644 --- a/Config-inheritance.md +++ b/Config-inheritance.md @@ -14,13 +14,13 @@ var dest = TypeAdapter.Adapt(src); //dest.Name = src.Name + "_Suffix" ``` -If you don't wish a derived type to use the base mapping, you can turn off by `AllowImplicitSourceInheritance` +If you don't wish for a derived type to use the base mapping, you can turn it off by using `AllowImplicitSourceInheritance` ```csharp TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = false; ``` -And by default, Mapster will not inherit destination type mappings. You can turn on by `AllowImplicitDestinationInheritance`. +And by default, Mapster will not inherit destination type mappings. You can turn it on by `AllowImplicitDestinationInheritance`. ```csharp TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; @@ -47,5 +47,5 @@ Vehicle vehicle = new Car { Id = 1, Name = "Car", Make = "Toyota" }; var dto = vehicle.Adapt(); dto.ShouldBeOfType(); -((CarDto)dto).Make.ShouldBe("Toyota"); //Make property doesn't exist in Vehicle +((CarDto)dto).Make.ShouldBe("Toyota"); //The 'Make' property doesn't exist in Vehicle ``` From a79385b54bc0bcd0bd557d6d4088771317da36c9 Mon Sep 17 00:00:00 2001 From: Atilla Lonny Date: Thu, 13 Aug 2020 08:34:02 -0400 Subject: [PATCH 141/363] A bit of grammar corrections. --- Config-location.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Config-location.md b/Config-location.md index 8f17fd22..a176e920 100644 --- a/Config-location.md +++ b/Config-location.md @@ -25,7 +25,7 @@ var dto2 = poco2.Adapt(config); ### Keep together with mapping -Problem with separating configuration and mapping is, coding will be separated into 2 locations. You might remove or alter mapping, and you can forget to update the configuration. `Fork` method allow you to keep config and mapping inline. +A potential problem with separating configuration and mapping is that the code will be separated into 2 locations. You might remove or alter mapping, and you can forget to update the configuration. `Fork` method allow you to keep config and mapping inline. ```csharp var dto = poco.Adapt( @@ -94,7 +94,7 @@ var registers = container.GetExports(); config.Apply(registers); ``` -`Apply` method also allow you to selectively pick from one or more `IRegister` rather than every `IRegister` in assembly. +`Apply` method also allows you to selectively pick from one or more `IRegister` rather than every `IRegister` in assembly. ```csharp var register = new MockingRegister(); From b09060a80d00f6fd2b5620a73a7276d5a658e7b1 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 20 Aug 2020 05:44:12 +0700 Subject: [PATCH 142/363] Updated Data types (markdown) --- Data-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data-types.md b/Data-types.md index 296fdab8..a9429f87 100644 --- a/Data-types.md +++ b/Data-types.md @@ -93,4 +93,4 @@ var src = new { Name = "Mapster", Age = 3 }; var target = src.Adapt(); ``` -There are limitations on record type mapping. Record type must not have a setter and have only one non-empty constructor, and all parameter names must match with properties. +There are limitations to map Record type automatically. Record type must not have a setter and have only one non-empty constructor, and all parameter names must match with properties. Otherwise you need to add [`MapToConstructor`](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping#map-to-constructor) config. From 95a71bc8cdbce9d396216860dec8f25cf75a689a Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 20 Aug 2020 05:47:07 +0700 Subject: [PATCH 143/363] Updated Mappers (markdown) --- Mappers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mappers.md b/Mappers.md index c4583713..62f49aeb 100644 --- a/Mappers.md +++ b/Mappers.md @@ -46,3 +46,7 @@ var dto = mapper.From(poco) .AddParameters("user", this.User.Identity.Name) .AdaptToType(); ``` + +### Code generation + +See [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) for generating your specific mapper class, rather than using the provided mappers. \ No newline at end of file From a659b6612ec1a3a98a40a8c69488d9089cdc610f Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 20 Aug 2020 05:50:16 +0700 Subject: [PATCH 144/363] Updated Config location (markdown) --- Config-location.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Config-location.md b/Config-location.md index a176e920..cc20241e 100644 --- a/Config-location.md +++ b/Config-location.md @@ -100,3 +100,14 @@ config.Apply(registers); var register = new MockingRegister(); config.Apply(register); ``` + +### Attributes + +You can also set config together with your POCO classes. For example: + +```csharp +[AdaptTo(typeof(StudentDto), PreserveReference = true)] +public class Student { + ... +} +``` \ No newline at end of file From 185df8fc238045b76e947aa226b4523aea9285f2 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 20 Aug 2020 05:55:23 +0700 Subject: [PATCH 145/363] Updated Config validation & compilation (markdown) --- Config-validation-&-compilation.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Config-validation-&-compilation.md b/Config-validation-&-compilation.md index c3fa9d9e..6eaa7e0d 100644 --- a/Config-validation-&-compilation.md +++ b/Config-validation-&-compilation.md @@ -50,8 +50,6 @@ var config = new TypeAdapterConfig(); config.Compile(); ``` -After compile, when you change setting in config, it will generate errors. Therefore, make sure you finish configuration before calling `Compile`. +Calling `Compile` method on start up helps you validate mapping and detect problem on start-up time, not on run-time. -Calling `Compile` method on start up has following benefits. -1. **Validate mapping**: as describe in previous section, `Compile` method helps you validate mapping. Calling on start up, help you detect problem on start-up time, not on run-time. -2. **Prevent compilation error on runtime**: Mapster is thread-safe for `Adapt` method after compilation. But not before compilation. In some corner case, you might got compilation error when calling `Adapt` method concurrently with `TypeAdapterConfig` setup. +NOTE: After compile, when you change setting in config, it will generate errors. Therefore, make sure you finish configuration before calling `Compile`. \ No newline at end of file From bea5a90cfbcc045ca5999d00e456b49d4759fddb Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 20 Aug 2020 06:03:40 +0700 Subject: [PATCH 146/363] Updated Home (markdown) --- Home.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 19ffa32c..82075610 100644 --- a/Home.md +++ b/Home.md @@ -40,7 +40,7 @@ | `config.RequireExplicitMapping` | Validate all type pairs are defined | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | | `config.AllowImplicitDestinationInheritance` | Use config from destination based class | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | | `config.AllowImplicitSourceInheritance` | Use config from source based class | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | -| `config.SelfContainedCodeGeneration` | Generate all nested mapping in 1 method | [code gen](https://github.com/MapsterMapper/Mapster/wiki/CodeGen) | +| `config.SelfContainedCodeGeneration` | Generate all nested mapping in 1 method | [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) | | `config.Compile()` | Validate mapping instruction & cache | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | | `config.CompileProjection()` | Validate mapping instruction & cache for queryable | | | `config.Clone()` | Copy config | [config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) | @@ -107,6 +107,8 @@ | `[AdaptMember(name)]` | Mapping property to different name | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | | `[AdaptIgnore(side)]` | Ignore property from mapping | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | | `[UseDestinationValue]` | Use existing property object to map data | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | +| `[AdaptTo]` `[AdaptFrom]` `[AdaptTwoWays]` | Add setting on POCO class | [location](https://github.com/MapsterMapper/Mapster/wiki/Config-location#attributes) | +| `[Mapper]` `[GeneratMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | #### Plugins From 104c34d2d7e03313ace592f3ab74b1ffd8a27261 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 20 Aug 2020 07:35:27 +0700 Subject: [PATCH 147/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 239 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 199 insertions(+), 40 deletions(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index ea4df634..df8b3eb1 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -1,8 +1,201 @@ -### Mapster.Tool +## Mapster.Tool -Here are steps to add code generation. +```bash +#skip this step if you already have dotnet-tools.json +dotnet new tool-manifest + +dotnet tool install Mapster.Tool +``` + +### Commands +Mapster.Tool provides 3 commands +- **model**: generate models from entities +- **extensions**: generate extension methods from entities +- **mapper**: generate mappers from interfaces + +And Mapster.Tool provides following options +- -a: define input assembly +- -n: define namespace of generated classes +- -o: define output directory + +### csproj integration + +#### Generate manually +add following code to your `csproj` file. +```xml + + + + + + + +``` +to generate run following command: +```bash +dotnet msbuild -t:Mapster +``` + +#### Generate automatically on build +add following code to your `csproj` file. +```xml + + + + + + +``` + +#### Clean up +add following code to your `csproj` file. +```xml + + + + + + +``` +to clean up run following command: +```bash +dotnet msbuild -t:CleanGenerated +``` + +### Generate models + +Annotate your class with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`. + +Example: +```csharp +[AdaptTo("[name]Dto")] +public class Student { + ... +} +``` + +Then Mapster will generate: +```csharp +public class StudentDto { + ... +} +``` + +#### Ignore some properties on generation + +By default, code generation will ignore properties that annotated `[AdaptIgnore]` attribute. But you can add more settings which include `IgnoreAttributes`, `IgnoreNoAttributes`, `IgnoreNamespaces`. + +Example: +```csharp +[AdaptTo("[name]Dto", IgnoreNoAttributes = new[] { typeof(DataMemberAttribute) })] +public class Student { + + [DataMember] + public string Name { get; set; } //this property will be generated + + public string LastName { get; set; } //this will not be generated +} +``` + +#### Change property types + +By default, if property type annotated with the same adapt attribute, code generation will forward to that type. (For example, `Student` has `ICollection`, after code generation `StudentDto` will has `ICollection`). + +You can override this by `[PropertyType(typeof(Target))]` attribute. This annotation can be annotated to either on property or on class. + +For example: +```csharp +[AdaptTo("[name]Dto")] +public class Student { + public ICollection Enrollments { get; set; } +} + +[AdaptTo("[name]Dto"), PropertyType(typeof(DataItem))] +public class Enrollment { + [PropertyType(typeof(string))] + public Grade? Grade { get; set; } +} +``` + +This will generate: +```csharp +public class StudentDto { + public ICollection Enrollments { get; set; } +} +public class EnrollmentDto { + public string Grade { get; set; } +} +``` + +#### Generate readonly properties + +For `[AdaptTo]` and `[AdaptTwoWays]`, you can generate readonly properties with `MapToConstructor` setting. + +For example: +```csharp +[AdaptTo("[name]Dto", MapToConstructor = true)] +public class Student { + public string Name { get; set; } +} +``` + +This will generate: +```csharp +public class StudentDto { + public string Name { get; } + + public StudentDto(string name) { + this.Name = name; + } +} +``` + +#### Generate nullable properties -1. Define interface to generate code. Your interface must annotate with `[Mapper]` in order for tool to pickup for generation. +For `[AdaptFrom]`, you can generate nullable properties with `IgnoreNullValues` setting. + +For example: +```csharp +[AdaptFrom("[name]Merge", IgnoreNullValues = true)] +public class Student { + public int Age { get; set; } +} +``` + +This will generate: +```csharp +public class StudentMerge { + public int? Age { get; set; } +} +``` + +### Generate extension methods + +For any POCOs annotate with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`, you can add `[GenerateMapper]` in order to generate extension methods. + +Example: +```csharp +[AdaptTo("[name]Dto"), GenerateMapper] +public class Student { + ... +} +``` + +Then Mapster will generate: +```csharp +public class StudentDto { + ... +} +public static class StudentMapper { + public static StudentDto AdaptToDto(this Student poco) { ... } + public static StudentDto AdaptTo(this Student poco, StudentDto dto) { ... } + public static Expression> ProjectToDto => ... +} +``` + +### Generate mapper from interface + +Annotate your interface with `[Mapper]` in order for tool to pickup for generation. This is example interface. ```csharp @@ -13,7 +206,7 @@ public interface IProductMapper } ``` -You can add multiple members as you want. All member names are flexible. +You can add multiple members as you want. All member names are flexible, but signature must be in following patterns: ```csharp [Mapper] public interface ICustomerMapper @@ -24,9 +217,6 @@ public interface ICustomerMapper //map from POCO to DTO CustomerDTO MapToDto(Customer customer); - //map back from DTO to POCO - Customer MapToPoco(CustomerDTO dto); - //map to existing object Customer MapToExisting(CustomerDTO dto, Customer customer); } @@ -44,37 +234,6 @@ public class MyRegister : IRegister } ``` -At this point, you can use your interface in your code to perform mapping. - -```csharp -var dtos = context.Customers.Select(mapper.ProjectToDto); -var dto = mapper.MapToDto(poco); -mapper.MapToExisting(dto, poco); -``` - -2. Install `Mapster.Tool` from your project root - -```bash -#skip this step if you already have dotnet-tools.json -dotnet new tool-manifest - -dotnet tool install Mapster.Tool -``` - -NOTE: the tool required .NET Core 2.1 or .NET Core 3.1 on your machine. - -3. Add setting to you `csproj` - -```xml - - - - -``` - -That's it, now Mapster will automatically generate codes after build. - -#### Example +### Sample -You can find example in Benchmark project -- https://github.com/MapsterMapper/Mapster/tree/master/src/Benchmark/Mappers \ No newline at end of file +- https://github.com/MapsterMapper/Mapster/tree/master/src/Sample.CodeGen \ No newline at end of file From f07fbd272924edbf3db18378a85999939f0b1e1e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 23 Aug 2020 08:10:51 +0700 Subject: [PATCH 148/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index df8b3eb1..fd609b22 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -10,7 +10,7 @@ dotnet tool install Mapster.Tool ### Commands Mapster.Tool provides 3 commands - **model**: generate models from entities -- **extensions**: generate extension methods from entities +- **extension**: generate extension methods from entities - **mapper**: generate mappers from interfaces And Mapster.Tool provides following options From 8114c5ff56c3549feec4745fe877898c9e7f107f Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 23 Oct 2020 08:34:03 +0700 Subject: [PATCH 149/363] Updated EF 6 & EF Core (markdown) --- EF-6-&-EF-Core.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/EF-6-&-EF-Core.md b/EF-6-&-EF-Core.md index 92023cf7..a0af43f2 100644 --- a/EF-6-&-EF-Core.md +++ b/EF-6-&-EF-Core.md @@ -30,3 +30,12 @@ _mapper.From(dto) .EntityFromContext(db) .AdaptTo(poco); ``` + +#### EF Core ProjectToType +`Mapster.EfCore` also allows `ProjectToType` from mapper instance. + +```csharp +var query = db.Customers.Where(...); +_mapper.From(query) + .ProjectToType(); +``` \ No newline at end of file From 1046d3563e59f7465095f355b7c7f971da6a671e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 23 Oct 2020 08:34:27 +0700 Subject: [PATCH 150/363] Updated EF 6 & EF Core (markdown) --- EF-6-&-EF-Core.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EF-6-&-EF-Core.md b/EF-6-&-EF-Core.md index a0af43f2..84df4bb2 100644 --- a/EF-6-&-EF-Core.md +++ b/EF-6-&-EF-Core.md @@ -32,7 +32,7 @@ _mapper.From(dto) ``` #### EF Core ProjectToType -`Mapster.EfCore` also allows `ProjectToType` from mapper instance. +`Mapster.EFCore` also allows `ProjectToType` from mapper instance. ```csharp var query = db.Customers.Where(...); From 3174a5efeea37704881c78614052bc410446de58 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 5 Nov 2020 22:58:31 +0700 Subject: [PATCH 151/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index fd609b22..43a73ba1 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -31,7 +31,7 @@ add following code to your `csproj` file. ``` -to generate run following command: +to generate run following command on `csproj` file directory: ```bash dotnet msbuild -t:Mapster ``` From 148a31f810a38703bc520f4616922c3e2b8b6313 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 12 Nov 2020 19:23:45 +0700 Subject: [PATCH 152/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 43a73ba1..a78934e4 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -171,6 +171,7 @@ public class StudentMerge { ### Generate extension methods +#### Generate using `[GenerateMapper]` attribute For any POCOs annotate with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`, you can add `[GenerateMapper]` in order to generate extension methods. Example: @@ -193,6 +194,22 @@ public static class StudentMapper { } ``` +#### Generate using configuration + +You can also generate extension methods and add extra settings from configuration. + +```csharp +public class MyRegister : IRegister +{ + public void Register(TypeAdapterConfig config) + { + config.NewConfig() + .GenerateMapper(MapType.Map | MapType.MapToTarget); + } +} +``` + + ### Generate mapper from interface Annotate your interface with `[Mapper]` in order for tool to pickup for generation. From 8c407d83a20f8a8402cb6e41175dc324a8b5b37f Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 10 Dec 2020 18:32:38 +0700 Subject: [PATCH 153/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index a78934e4..2a323f59 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -62,6 +62,19 @@ to clean up run following command: dotnet msbuild -t:CleanGenerated ``` +### Generate full type name + +If your POCOs and DTOs have the same name, you might need to generate using full type name, by add `-p` flag. +```xml + + + + + + + +``` + ### Generate models Annotate your class with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`. From 262774eec891fb8cf321f67765192b2e3741a15e Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 10 Dec 2020 18:33:06 +0700 Subject: [PATCH 154/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 2a323f59..c2b308a6 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -62,7 +62,7 @@ to clean up run following command: dotnet msbuild -t:CleanGenerated ``` -### Generate full type name +#### Generate full type name If your POCOs and DTOs have the same name, you might need to generate using full type name, by add `-p` flag. ```xml From b3f89a5be560b5061d868854e0c758b34fdcc310 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Fri, 11 Dec 2020 07:48:54 +0700 Subject: [PATCH 155/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index c2b308a6..70cf1eb4 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -26,9 +26,9 @@ add following code to your `csproj` file. - - - + + + ``` to generate run following command on `csproj` file directory: @@ -41,9 +41,9 @@ add following code to your `csproj` file. ```xml - - - + + + ``` @@ -69,9 +69,9 @@ If your POCOs and DTOs have the same name, you might need to generate using full - - - + + + ``` From f010e76ab1f543ffac276310f8f2e275220bf9b2 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 20 Dec 2020 08:58:44 +0700 Subject: [PATCH 156/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 70cf1eb4..cd07693a 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -17,6 +17,7 @@ And Mapster.Tool provides following options - -a: define input assembly - -n: define namespace of generated classes - -o: define output directory +- -p: print full type name (if your DTOs/POCOs having the same name) ### csproj integration @@ -64,7 +65,7 @@ dotnet msbuild -t:CleanGenerated #### Generate full type name -If your POCOs and DTOs have the same name, you might need to generate using full type name, by add `-p` flag. +If your POCOs and DTOs have the same name, you might need to generate using full type name, by adding `-p` flag. ```xml From 659f3879a146f1cb95b9c137070b4c0600cc4563 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 22:52:21 +0700 Subject: [PATCH 157/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_Sidebar.md b/_Sidebar.md index 5d3be0a8..ca6931cc 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -43,4 +43,5 @@ ##### Tools * [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) + * [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation) * [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) From fc4e0f24e841308d53ae3207e5b1952bc5564369 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 22:54:39 +0700 Subject: [PATCH 158/363] Updated Home (markdown) --- Home.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 82075610..7aa90c7f 100644 --- a/Home.md +++ b/Home.md @@ -126,5 +126,6 @@ | Plugin | Tool | Description | | ------ | ------------- |-----------------------| -| [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | `dotnet mapster` | automatically generate mapping codes on build | +| [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | `dotnet mapster` | generate DTOs and mapping codes (using attributes) | +| [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation) | `dotnet mapster` | generate DTOs and mapping codes (using fluent API) | | [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) | `t4` | generate mapping codes using t4 | From 4099f82d83e2dc30b456d1341b84d0f4a7785759 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 22:58:37 +0700 Subject: [PATCH 159/363] Updated _Sidebar (markdown) --- _Sidebar.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_Sidebar.md b/_Sidebar.md index ca6931cc..427535b3 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -44,4 +44,6 @@ ##### Tools * [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) * [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation) + * [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation) + * [Interface](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation) * [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) From e292801fede3e9791fef826dc1fbef7994bd309f Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 22:59:46 +0700 Subject: [PATCH 160/363] Updated Home (markdown) --- Home.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Home.md b/Home.md index 7aa90c7f..d7ae8725 100644 --- a/Home.md +++ b/Home.md @@ -126,6 +126,5 @@ | Plugin | Tool | Description | | ------ | ------------- |-----------------------| -| [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | `dotnet mapster` | generate DTOs and mapping codes (using attributes) | -| [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation) | `dotnet mapster` | generate DTOs and mapping codes (using fluent API) | +| [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | `dotnet mapster` | generate DTOs and mapping codes on build | | [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) | `t4` | generate mapping codes using t4 | From 453ad2419e979393d222ae3af6f6f3d070d51a2a Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 23:10:06 +0700 Subject: [PATCH 161/363] Updated _Sidebar (markdown) --- _Sidebar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 427535b3..cf7d4204 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -45,5 +45,5 @@ * [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) * [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation) * [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation) - * [Interface](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation) + * [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation) * [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) From 65fd86a61f8497445437573df95d20f519c5a3e0 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 23:11:44 +0700 Subject: [PATCH 162/363] Created Attribute base Code generation (markdown) --- Attribute-base-Code-generation.md | 147 ++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 Attribute-base-Code-generation.md diff --git a/Attribute-base-Code-generation.md b/Attribute-base-Code-generation.md new file mode 100644 index 00000000..fee43680 --- /dev/null +++ b/Attribute-base-Code-generation.md @@ -0,0 +1,147 @@ +### Generate models + +Annotate your class with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`. + +Example: +```csharp +[AdaptTo("[name]Dto")] +public class Student { + ... +} +``` + +Then Mapster will generate: +```csharp +public class StudentDto { + ... +} +``` + +#### Ignore some properties on generation + +By default, code generation will ignore properties that annotated `[AdaptIgnore]` attribute. But you can add more settings which include `IgnoreAttributes`, `IgnoreNoAttributes`, `IgnoreNamespaces`. + +Example: +```csharp +[AdaptTo("[name]Dto", IgnoreNoAttributes = new[] { typeof(DataMemberAttribute) })] +public class Student { + + [DataMember] + public string Name { get; set; } //this property will be generated + + public string LastName { get; set; } //this will not be generated +} +``` + +#### Change property types + +By default, if property type annotated with the same adapt attribute, code generation will forward to that type. (For example, `Student` has `ICollection`, after code generation `StudentDto` will has `ICollection`). + +You can override this by `[PropertyType(typeof(Target))]` attribute. This annotation can be annotated to either on property or on class. + +For example: +```csharp +[AdaptTo("[name]Dto")] +public class Student { + public ICollection Enrollments { get; set; } +} + +[AdaptTo("[name]Dto"), PropertyType(typeof(DataItem))] +public class Enrollment { + [PropertyType(typeof(string))] + public Grade? Grade { get; set; } +} +``` + +This will generate: +```csharp +public class StudentDto { + public ICollection Enrollments { get; set; } +} +public class EnrollmentDto { + public string Grade { get; set; } +} +``` + +#### Generate readonly properties + +For `[AdaptTo]` and `[AdaptTwoWays]`, you can generate readonly properties with `MapToConstructor` setting. + +For example: +```csharp +[AdaptTo("[name]Dto", MapToConstructor = true)] +public class Student { + public string Name { get; set; } +} +``` + +This will generate: +```csharp +public class StudentDto { + public string Name { get; } + + public StudentDto(string name) { + this.Name = name; + } +} +``` + +#### Generate nullable properties + +For `[AdaptFrom]`, you can generate nullable properties with `IgnoreNullValues` setting. + +For example: +```csharp +[AdaptFrom("[name]Merge", IgnoreNullValues = true)] +public class Student { + public int Age { get; set; } +} +``` + +This will generate: +```csharp +public class StudentMerge { + public int? Age { get; set; } +} +``` + +### Generate extension methods + +#### Generate using `[GenerateMapper]` attribute +For any POCOs annotate with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`, you can add `[GenerateMapper]` in order to generate extension methods. + +Example: +```csharp +[AdaptTo("[name]Dto"), GenerateMapper] +public class Student { + ... +} +``` + +Then Mapster will generate: +```csharp +public class StudentDto { + ... +} +public static class StudentMapper { + public static StudentDto AdaptToDto(this Student poco) { ... } + public static StudentDto AdaptTo(this Student poco, StudentDto dto) { ... } + public static Expression> ProjectToDto => ... +} +``` + +#### Generate using configuration + +You can also generate extension methods and add extra settings from configuration. + +```csharp +public class MyRegister : IRegister +{ + public void Register(TypeAdapterConfig config) + { + config.NewConfig() + .GenerateMapper(MapType.Map | MapType.MapToTarget); + } +} +``` + From 4a8949497f967737ab7d386383eb920068c129cf Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 23:12:21 +0700 Subject: [PATCH 163/363] Created Interface base Code generation (markdown) --- Interface-base-Code-generation.md | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Interface-base-Code-generation.md diff --git a/Interface-base-Code-generation.md b/Interface-base-Code-generation.md new file mode 100644 index 00000000..7ffdb215 --- /dev/null +++ b/Interface-base-Code-generation.md @@ -0,0 +1,40 @@ +### Generate mapper from interface + +Annotate your interface with `[Mapper]` in order for tool to pickup for generation. + +This is example interface. +```csharp +[Mapper] +public interface IProductMapper +{ + ProductDTO Map(Product customer); +} +``` + +You can add multiple members as you want. All member names are flexible, but signature must be in following patterns: +```csharp +[Mapper] +public interface ICustomerMapper +{ + //for queryable + Expression> ProjectToDto { get; } + + //map from POCO to DTO + CustomerDTO MapToDto(Customer customer); + + //map to existing object + Customer MapToExisting(CustomerDTO dto, Customer customer); +} +``` + +If you have configuration, it must be in `IRegister` + +```csharp +public class MyRegister : IRegister +{ + public void Register(TypeAdapterConfig config) + { + config.NewConfig(); + } +} +``` From 9364b69ba36ee01d2936434becace3ade8791b7f Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 23:12:37 +0700 Subject: [PATCH 164/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 204 ++++-------------------------------------------- 1 file changed, 17 insertions(+), 187 deletions(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index cd07693a..b40d8475 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -1,5 +1,6 @@ ## Mapster.Tool +### Install Mapster.Tool ```bash #skip this step if you already have dotnet-tools.json dotnet new tool-manifest @@ -7,6 +8,17 @@ dotnet new tool-manifest dotnet tool install Mapster.Tool ``` +### Install Mapster +For lightweight dependency, you can just install `Mapster.Core`. +``` +PM> Install-Package Mapster.Core +``` + +However, if you need `TypeAdapterConfig` for advance configuration, you still need `Mapster`. +``` +PM> Install-Package Mapster +``` + ### Commands Mapster.Tool provides 3 commands - **model**: generate models from entities @@ -76,194 +88,12 @@ If your POCOs and DTOs have the same name, you might need to generate using full ``` -### Generate models - -Annotate your class with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`. - -Example: -```csharp -[AdaptTo("[name]Dto")] -public class Student { - ... -} -``` - -Then Mapster will generate: -```csharp -public class StudentDto { - ... -} -``` - -#### Ignore some properties on generation - -By default, code generation will ignore properties that annotated `[AdaptIgnore]` attribute. But you can add more settings which include `IgnoreAttributes`, `IgnoreNoAttributes`, `IgnoreNamespaces`. - -Example: -```csharp -[AdaptTo("[name]Dto", IgnoreNoAttributes = new[] { typeof(DataMemberAttribute) })] -public class Student { - - [DataMember] - public string Name { get; set; } //this property will be generated - - public string LastName { get; set; } //this will not be generated -} -``` - -#### Change property types - -By default, if property type annotated with the same adapt attribute, code generation will forward to that type. (For example, `Student` has `ICollection`, after code generation `StudentDto` will has `ICollection`). - -You can override this by `[PropertyType(typeof(Target))]` attribute. This annotation can be annotated to either on property or on class. - -For example: -```csharp -[AdaptTo("[name]Dto")] -public class Student { - public ICollection Enrollments { get; set; } -} - -[AdaptTo("[name]Dto"), PropertyType(typeof(DataItem))] -public class Enrollment { - [PropertyType(typeof(string))] - public Grade? Grade { get; set; } -} -``` - -This will generate: -```csharp -public class StudentDto { - public ICollection Enrollments { get; set; } -} -public class EnrollmentDto { - public string Grade { get; set; } -} -``` - -#### Generate readonly properties - -For `[AdaptTo]` and `[AdaptTwoWays]`, you can generate readonly properties with `MapToConstructor` setting. - -For example: -```csharp -[AdaptTo("[name]Dto", MapToConstructor = true)] -public class Student { - public string Name { get; set; } -} -``` - -This will generate: -```csharp -public class StudentDto { - public string Name { get; } +### Generate DTOs and mapping codes - public StudentDto(string name) { - this.Name = name; - } -} -``` - -#### Generate nullable properties - -For `[AdaptFrom]`, you can generate nullable properties with `IgnoreNullValues` setting. - -For example: -```csharp -[AdaptFrom("[name]Merge", IgnoreNullValues = true)] -public class Student { - public int Age { get; set; } -} -``` - -This will generate: -```csharp -public class StudentMerge { - public int? Age { get; set; } -} -``` - -### Generate extension methods - -#### Generate using `[GenerateMapper]` attribute -For any POCOs annotate with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`, you can add `[GenerateMapper]` in order to generate extension methods. - -Example: -```csharp -[AdaptTo("[name]Dto"), GenerateMapper] -public class Student { - ... -} -``` - -Then Mapster will generate: -```csharp -public class StudentDto { - ... -} -public static class StudentMapper { - public static StudentDto AdaptToDto(this Student poco) { ... } - public static StudentDto AdaptTo(this Student poco, StudentDto dto) { ... } - public static Expression> ProjectToDto => ... -} -``` - -#### Generate using configuration - -You can also generate extension methods and add extra settings from configuration. - -```csharp -public class MyRegister : IRegister -{ - public void Register(TypeAdapterConfig config) - { - config.NewConfig() - .GenerateMapper(MapType.Map | MapType.MapToTarget); - } -} -``` - - -### Generate mapper from interface - -Annotate your interface with `[Mapper]` in order for tool to pickup for generation. - -This is example interface. -```csharp -[Mapper] -public interface IProductMapper -{ - ProductDTO Map(Product customer); -} -``` - -You can add multiple members as you want. All member names are flexible, but signature must be in following patterns: -```csharp -[Mapper] -public interface ICustomerMapper -{ - //for queryable - Expression> ProjectToDto { get; } - - //map from POCO to DTO - CustomerDTO MapToDto(Customer customer); - - //map to existing object - Customer MapToExisting(CustomerDTO dto, Customer customer); -} -``` - -If you have configuration, it must be in `IRegister` - -```csharp -public class MyRegister : IRegister -{ - public void Register(TypeAdapterConfig config) - { - config.NewConfig(); - } -} -``` +There are 3 flavors, to generate DTOs and mapping codes +- [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation): if you don't want to touch your domain classes +- [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation): if you would like to keep mapping declaration closed to your domain classes. +- [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation): if you already have DTOs, and you would like to define mapping through interfaces. ### Sample From eaaad71c15808849e4b8e228ec39d4748874bdd8 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sun, 24 Jan 2021 23:39:25 +0700 Subject: [PATCH 165/363] Created Fluent API Code generation (markdown) --- Fluent-API-Code-generation.md | 160 ++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 Fluent-API-Code-generation.md diff --git a/Fluent-API-Code-generation.md b/Fluent-API-Code-generation.md new file mode 100644 index 00000000..e5c2ea7f --- /dev/null +++ b/Fluent-API-Code-generation.md @@ -0,0 +1,160 @@ +### Configuration class + +Create a configuration class implement `ICodeGenerationRegister`. + +```csharp +public class MyRegister : ICodeGenerationRegister +{ + public void Register(CodeGenerationConfig config) + { + config.AdaptTo("[name]Dto") + .ForAllTypesInNamespace(Assembly.GetExecutingAssembly(), "Sample.CodeGen.Domains"); + + config.GenerateMapper("[name]Mapper") + .ForType() + .ForType(); + } +} +``` + +### Generate models + +Declare `AdaptFrom`, `AdaptTo`, or `AdaptTwoWays`. + +Example: +```csharp +config.AdaptTo("[name]Dto") + ForType(); +``` + +Then Mapster will generate: +```csharp +public class StudentDto { + ... +} +``` + +#### Add types to generate + +You can add types by `ForTypes`, `ForAllTypesInNamespace`, `ForType<>`, and you can remove added types using `ExcludeTypes`. +```csharp +config.AdaptTo("[name]Dto") + .ForAllTypesInNamespace(Assembly.GetExecutingAssembly(), "Sample.CodeGen.Domains") + .ExcludeTypes(typeof(SchoolContext)) + .ExcludeTypes(type => type.IsEnum) +``` + + +#### Ignore some properties on generation + +By default, code generation will ignore properties that annotated `[AdaptIgnore]` attribute. But you can add more settings which include `IgnoreAttributes`, `IgnoreNoAttributes`, `IgnoreNamespaces`. + +Example: +```csharp +config.AdaptTo("[name]Dto") + .ForType() + .IgnoreNoAttributes (typeof(DataMemberAttribute)); + +public class Student { + [DataMember] + public string Name { get; set; } //this property will be generated + public string LastName { get; set; } //this will not be generated +} +``` + +#### Ignore a property + +```csharp +config.AdaptTo("[name]Dto") + .ForType(cfg => { + cfg.Ignore(poco => poco.LastName); + }); +``` + +#### Change a property name, type + +```csharp +config.AdaptTo("[name]Dto") + .ForType(cfg => { + cfg.Map(poco => poco.LastName, "Surname"); //change property name + cfg.Map(poco => poco.Grade, typeof(string)); //change property type + }); +``` + +#### Forward property types + +By default, code generation will forward type on the same declaration. (For example, `Student` has `ICollection`, after code generation `StudentDto` will has `ICollection`). + +You can override this by `AlterType`. + +```csharp +config.AdaptTo("[name]Dto") + .ForAllTypesInNamespace(Assembly.GetExecutingAssembly(), "Sample.CodeGen.Domains") + .AlterType(); //forward all Student to Person +``` + +#### Generate readonly properties + +For `AdaptTo` and `AdaptTwoWays`, you can generate readonly properties with `MapToConstructor` setting. + +For example: +```csharp +config.AdaptTo("[name]Dto") + .ForType() + .MapToConstructor(true); +``` + +This will generate: +```csharp +public class StudentDto { + public string Name { get; } + + public StudentDto(string name) { + this.Name = name; + } +} +``` + +#### Generate nullable properties + +For `AdaptFrom`, you can generate nullable properties with `IgnoreNullValues` setting. + +For example: +```csharp +config.AdaptFrom("[name]Merge") + .ForType() + .IgnoreNullValues(true); +``` + +This will generate: +```csharp +public class StudentMerge { + public int? Age { get; set; } +} +``` + +### Generate extension methods + +#### Generate using `GenerateMapper`. +For any POCOs declared with `AdaptFrom`, `AdaptTo`, or `AdaptTwoWays`, you can declare `GenerateMapper` in order to generate extension methods. + +Example: +```csharp +config.AdaptTo("[name]Dto") + .ForType(); + +config.GenerateMapper("[name]Mapper") + .ForType(); +``` + +Then Mapster will generate: +```csharp +public class StudentDto { + ... +} +public static class StudentMapper { + public static StudentDto AdaptToDto(this Student poco) { ... } + public static StudentDto AdaptTo(this Student poco, StudentDto dto) { ... } + public static Expression> ProjectToDto => ... +} +``` From 8d84e00fb28acbd0464e183fa164800b3d845f79 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 6 Feb 2021 08:53:03 +0700 Subject: [PATCH 166/363] Updated EF 6 & EF Core (markdown) --- EF-6-&-EF-Core.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/EF-6-&-EF-Core.md b/EF-6-&-EF-Core.md index 84df4bb2..45818e07 100644 --- a/EF-6-&-EF-Core.md +++ b/EF-6-&-EF-Core.md @@ -10,6 +10,12 @@ For EF Core In EF, objects are tracked, when you copy data from dto to entity containing navigation properties, this plugin will help finding entity object in navigation properties automatically. +#### Mapster.EFCore & EFCore compatiability +- use Mapster.EFCore version 5.x for EFCore 5.x +- use Mapster.EFCore version 3.x for EFCore 3.x +- use Mapster.EFCore version 1.x for EFCore 2.x + + #### Usage Use `EntityFromContext` method to define data context. From 89f79f805f3fc4aba570e03fd4ab090519bc3115 Mon Sep 17 00:00:00 2001 From: Mohammad Hossein Amri Date: Sat, 6 Feb 2021 22:35:38 +0800 Subject: [PATCH 167/363] update what is commented in #302 --- Object-references.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Object-references.md b/Object-references.md index 11fcabc5..4e2ff85d 100644 --- a/Object-references.md +++ b/Object-references.md @@ -10,6 +10,8 @@ TypeAdapterConfig NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. +NOTE: you might need to use `MaxDepth`, `PreserveReference` doesn't support for EF Query + ### MaxDepth Rather than `PreserveReference`, you could also try `MaxDepth`. `MaxDepth` will map until it reaches the defined limit. Unlike `PreserveReference`, `MaxDepth` also works with queryable projection. From 0ab94d94d14590bd73f667ba5f49a600a81b3338 Mon Sep 17 00:00:00 2001 From: Mohammad Hossein Amri Date: Sat, 6 Feb 2021 22:36:27 +0800 Subject: [PATCH 168/363] Updated Object references (markdown) --- Object-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Object-references.md b/Object-references.md index 4e2ff85d..d2df4a2e 100644 --- a/Object-references.md +++ b/Object-references.md @@ -10,7 +10,7 @@ TypeAdapterConfig NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. -NOTE: you might need to use `MaxDepth`, `PreserveReference` doesn't support for EF Query +NOTE: you might need to use `MaxDepth`. `PreserveReference` doesn't support EF Query (`ProjectTo`) ### MaxDepth From d3f9fab09cfbfb5cbf4c6f81ca0b0f2a00929019 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Thu, 11 Feb 2021 23:37:35 +0700 Subject: [PATCH 169/363] Updated Attribute base Code generation (markdown) --- Attribute-base-Code-generation.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Attribute-base-Code-generation.md b/Attribute-base-Code-generation.md index fee43680..7c42614e 100644 --- a/Attribute-base-Code-generation.md +++ b/Attribute-base-Code-generation.md @@ -130,6 +130,19 @@ public static class StudentMapper { } ``` +#### Configuration +If you have configuration, it must be in `IRegister` + +```csharp +public class MyRegister : IRegister +{ + public void Register(TypeAdapterConfig config) + { + config.NewConfig(); + } +} +``` + #### Generate using configuration You can also generate extension methods and add extra settings from configuration. From 51b65cdb8c122dc0f632fd684cfceb41a689e54d Mon Sep 17 00:00:00 2001 From: chaowlert Date: Sat, 10 Apr 2021 17:06:44 +0700 Subject: [PATCH 170/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index b40d8475..6668502a 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -27,9 +27,11 @@ Mapster.Tool provides 3 commands And Mapster.Tool provides following options - -a: define input assembly +- -b: specify base namespace for generating dynamic outputs & namespaces - -n: define namespace of generated classes - -o: define output directory - -p: print full type name (if your DTOs/POCOs having the same name) +- -r: generate record types instead of POCO types ### csproj integration @@ -88,6 +90,35 @@ If your POCOs and DTOs have the same name, you might need to generate using full ``` +#### Dynamic outputs & namespaces +For example you have following structure. +``` +Sample.CodeGen +- Domains + - Sub1 + - Domain1 + - Sub2 + - Domain2 +``` + +And if you can specify base namespace as `Sample.CodeGen.Domains` +```xml + +``` + +Code will be generated to +``` +Sample.CodeGen +- Generated + - Sub1 + - Domain1 + - Sub2 + - Domain2 +``` + + + ### Generate DTOs and mapping codes There are 3 flavors, to generate DTOs and mapping codes From 9759d2a3ab5cf823be8d5010dc79722774289fcc Mon Sep 17 00:00:00 2001 From: chaowlert Date: Mon, 12 Apr 2021 23:38:00 +0700 Subject: [PATCH 171/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 6668502a..96d626a1 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -122,7 +122,7 @@ Sample.CodeGen ### Generate DTOs and mapping codes There are 3 flavors, to generate DTOs and mapping codes -- [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation): if you don't want to touch your domain classes +- [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation): if you don't want to touch your domain classes, or generate DTOs from domain types in different assembly. - [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation): if you would like to keep mapping declaration closed to your domain classes. - [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation): if you already have DTOs, and you would like to define mapping through interfaces. From c9543faa79d6b9212ede86634607fcc83b57e200 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Wed, 14 Apr 2021 13:44:46 +0700 Subject: [PATCH 172/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 96d626a1..2daaaabb 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -112,9 +112,9 @@ Code will be generated to Sample.CodeGen - Generated - Sub1 - - Domain1 + - Dto1 - Sub2 - - Domain2 + - Dto2 ``` From aaf627a5b41d4f919b90c7692878941653076de0 Mon Sep 17 00:00:00 2001 From: chaowlert Date: Mon, 21 Jun 2021 20:18:03 +0700 Subject: [PATCH 173/363] Updated _Sidebar (markdown) --- _Sidebar.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_Sidebar.md b/_Sidebar.md index cf7d4204..94859f26 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -1,5 +1,8 @@ ### Getting Started +##### Translation +* [中文文档](https://github.com/rivenfx/Mapster-docs) (sp thx to [@staneee](https://github.com/staneee)) + ##### [References](https://github.com/MapsterMapper/Mapster/wiki) ##### Mapping * [Basic usages](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) From 48db20edfeed6c30cda9300e245485ed453c7411 Mon Sep 17 00:00:00 2001 From: Bill Robertson Date: Sat, 6 Nov 2021 20:41:08 -0500 Subject: [PATCH 174/363] Updates String.Format to string interpolation --- Custom-mapping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Custom-mapping.md b/Custom-mapping.md index f0810c47..2f336609 100644 --- a/Custom-mapping.md +++ b/Custom-mapping.md @@ -6,7 +6,7 @@ You can customize how Mapster maps values to a property. TypeAdapterConfig .NewConfig() .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); + src => $"{src.FirstName} {src.LastName}"); ``` You can even map when source and destination property types are different. From dc8e3453c03807856b6b245d6474471cbc8707cd Mon Sep 17 00:00:00 2001 From: Nguyen Gia Hoang <35656480+popemkt@users.noreply.github.com> Date: Mon, 21 Feb 2022 22:48:00 +0700 Subject: [PATCH 175/363] Should be close instead of closed --- Mapster.Tool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 2daaaabb..7e04b4f9 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -123,7 +123,7 @@ Sample.CodeGen There are 3 flavors, to generate DTOs and mapping codes - [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation): if you don't want to touch your domain classes, or generate DTOs from domain types in different assembly. -- [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation): if you would like to keep mapping declaration closed to your domain classes. +- [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation): if you would like to keep mapping declaration close to your domain classes. - [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation): if you already have DTOs, and you would like to define mapping through interfaces. ### Sample From 932e15a5107b5be7de198682be3f26f2a791e95c Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Mon, 27 Jun 2022 23:53:09 +0200 Subject: [PATCH 176/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index 7e04b4f9..b1feca32 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -26,13 +26,13 @@ Mapster.Tool provides 3 commands - **mapper**: generate mappers from interfaces And Mapster.Tool provides following options -- -a: define input assembly -- -b: specify base namespace for generating dynamic outputs & namespaces -- -n: define namespace of generated classes -- -o: define output directory -- -p: print full type name (if your DTOs/POCOs having the same name) -- -r: generate record types instead of POCO types - +- -a: Define input assembly +- -b: Specify base namespace for generating dynamic outputs & namespaces +- -n: Define namespace of generated classes +- -o: Define output directory +- -p: Print full type name (if your DTOs/POCOs having the same name) +- -r: Generate record types instead of POCO types +- -s: Skip generating existing files ### csproj integration #### Generate manually From 3adbbd95f6601e97b3c8cfdb79e7a3c9aa3d515f Mon Sep 17 00:00:00 2001 From: devbased Date: Thu, 30 Jun 2022 13:46:19 +0300 Subject: [PATCH 177/363] Add documentation for new overloads with destination parameter --- Before-after-mapping.md | 58 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/Before-after-mapping.md b/Before-after-mapping.md index 24410ddd..8f7ea2b9 100644 --- a/Before-after-mapping.md +++ b/Before-after-mapping.md @@ -4,7 +4,7 @@ You can perform actions before mapping started by using `BeforeMapping` method. ```csharp TypeAdapterConfig.ForType() - .BeforeMapping((src, dest) => dest.Initialize()); + .BeforeMapping((src, result) => result.Initialize()); ``` ### After mapping action @@ -13,14 +13,14 @@ You can perform actions after each mapping by using `AfterMapping` method. For i ```csharp TypeAdapterConfig.ForType() - .AfterMapping((src, dest) => dest.Validate()); + .AfterMapping((src, result) => result.Validate()); ``` Or you can set for all mappings to types which implemented a specific interface by using `ForDestinationType` method. ```csharp TypeAdapterConfig.GlobalSettings.ForDestinationType() - .AfterMapping(dest => dest.Validate()); + .AfterMapping(result => result.Validate()); ``` ### Before & after mapping in code generation @@ -32,7 +32,7 @@ For single line statement, you can directly change from `BeforeMapping` and `Aft ```csharp TypeAdapterConfig.GlobalSettings.ForDestinationType() - .AfterMappingInline(dest => dest.Validate()); + .AfterMappingInline(result => result.Validate()); ``` #### Multiple statements @@ -51,5 +51,53 @@ Then you can reference the method to `BeforeMappingInline` and `AfterMappingInli ```csharp TypeAdapterConfig.GlobalSettings.ForDestinationType() - .AfterMappingInline(dest => PocoToDtoMapper.Validate(dest)); + .AfterMappingInline(result => PocoToDtoMapper.Validate(result)); ``` + +### Before and After mapping have overloads with `destination` parameter + +You can use `BeforeMapping` with `destination` to construct final (`result`) object. + +```csharp +TypeAdapterConfig, IEnumerable>.NewConfig() + .BeforeMapping((src, result, destination) => + { + if (!ReferenceEquals(result, destination) && destination != null && result is ICollection resultCollection) + { + foreach (var item in destination) + { + resultCollection.Add(item); + } + } +}); + +IEnumerable source = new List { 1, 2, 3, }; +IEnumerable destination = new List { 0, }; + +var result = source.Adapt(destination); + +destination.ShouldBe(new List { 0, }); +source.ShouldBe(new List { 1, 2, 3, }); +result.ShouldBe(new List { 0, 1, 2, 3, }); +``` + +Same with `AfterMapping`. + +```csharp +TypeAdapterConfig.NewConfig() + .ConstructUsing((simplePoco, dto) => new SimpleDto()) + .AfterMapping((src, result, destination) => result.Name += $"{destination.Name}xxx"); + +var poco = new SimplePoco +{ + Id = Guid.NewGuid(), + Name = "test", +}; + +var oldDto = new SimpleDto { Name = "zzz", }; +var result = poco.Adapt(oldDto); + +result.ShouldNotBeSameAs(oldDto); +result.Id.ShouldBe(poco.Id); +result.Name.ShouldBe(poco.Name + "zzzxxx"); +``` \ No newline at end of file From dcacbd1503eb2198fdedf73f3bc4f4a270793519 Mon Sep 17 00:00:00 2001 From: devbased Date: Thu, 30 Jun 2022 13:47:08 +0300 Subject: [PATCH 178/363] Add example for ConstructUsing overload with destination parameter --- Constructor-mapping.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Constructor-mapping.md b/Constructor-mapping.md index 71592f5a..ac22d2a7 100644 --- a/Constructor-mapping.md +++ b/Constructor-mapping.md @@ -12,6 +12,10 @@ TypeAdapterConfig.NewConfig() //Example using an object initializer TypeAdapterConfig.NewConfig() .ConstructUsing(src => new TDestination{Unmapped = "unmapped"}); + +//Example using an overload with `destination` parameter +TypeAdapterConfig.NewConfig() + .ConstructUsing((src, destination) => new TDestination(src.Id, destination?.Name ?? src.Name)); ``` ### Map to constructor From ce9de53239d42e91b347dbb526228d9f6cbedafb Mon Sep 17 00:00:00 2001 From: Arad Alvand Date: Wed, 10 Aug 2022 14:18:25 +0430 Subject: [PATCH 179/363] Fix a typo --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index d7ae8725..16899f3e 100644 --- a/Home.md +++ b/Home.md @@ -108,7 +108,7 @@ | `[AdaptIgnore(side)]` | Ignore property from mapping | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | | `[UseDestinationValue]` | Use existing property object to map data | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | | `[AdaptTo]` `[AdaptFrom]` `[AdaptTwoWays]` | Add setting on POCO class | [location](https://github.com/MapsterMapper/Mapster/wiki/Config-location#attributes) | -| `[Mapper]` `[GeneratMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | +| `[Mapper]` `[GenerateMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | #### Plugins From d44f78f4de888a475f04ea5613a2b37f160673b9 Mon Sep 17 00:00:00 2001 From: Max Wagner Date: Thu, 6 Oct 2022 13:03:00 +0200 Subject: [PATCH 180/363] Fix typo (missing method call period) --- Fluent-API-Code-generation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Fluent-API-Code-generation.md b/Fluent-API-Code-generation.md index e5c2ea7f..39ddcdf8 100644 --- a/Fluent-API-Code-generation.md +++ b/Fluent-API-Code-generation.md @@ -24,7 +24,7 @@ Declare `AdaptFrom`, `AdaptTo`, or `AdaptTwoWays`. Example: ```csharp config.AdaptTo("[name]Dto") - ForType(); + .ForType(); ``` Then Mapster will generate: From afdd321ac50fcc7bcdbe1ee1103ebd8022b85213 Mon Sep 17 00:00:00 2001 From: almika777 <47299790+almika777@users.noreply.github.com> Date: Thu, 9 Feb 2023 19:33:29 +0500 Subject: [PATCH 181/363] Created _Footer (markdown) --- _Footer.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 _Footer.md diff --git a/_Footer.md b/_Footer.md new file mode 100644 index 00000000..5975c2eb --- /dev/null +++ b/_Footer.md @@ -0,0 +1 @@ +wegf \ No newline at end of file From 54e6c53bfcdc67e8aef21320eb59c8435f38d6ca Mon Sep 17 00:00:00 2001 From: almika777 <47299790+almika777@users.noreply.github.com> Date: Thu, 9 Feb 2023 19:33:40 +0500 Subject: [PATCH 182/363] Destroyed _Footer (markdown) --- _Footer.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 _Footer.md diff --git a/_Footer.md b/_Footer.md deleted file mode 100644 index 5975c2eb..00000000 --- a/_Footer.md +++ /dev/null @@ -1 +0,0 @@ -wegf \ No newline at end of file From fc7fb16d57e630e35f477c087a81c8abae9c6117 Mon Sep 17 00:00:00 2001 From: Aref Azizian Date: Thu, 9 Feb 2023 21:58:08 +0330 Subject: [PATCH 183/363] Created Mapping Configuration With "IMapFrom" Interface (markdown) --- ...figuration-With-\"IMapFrom\"-Interface.md" | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 "Mapping-Configuration-With-\"IMapFrom\"-Interface.md" diff --git "a/Mapping-Configuration-With-\"IMapFrom\"-Interface.md" "b/Mapping-Configuration-With-\"IMapFrom\"-Interface.md" new file mode 100644 index 00000000..812aede3 --- /dev/null +++ "b/Mapping-Configuration-With-\"IMapFrom\"-Interface.md" @@ -0,0 +1,30 @@ +Before using this feature you have to add this line: +```csharp +TypeAdapterConfig.GlobalSettings.ScanInheritedTypes(Assembly.GetExecutingAssembly()); +``` +With adding above line to your Startup.cs or Program.cs or any other way to run at startup, you can write mapping configs in the destination class that implements IMapFrom interface + +Example: +```csharp +public class InheritedDestinationModel : IMapFrom +{ + public string Type { get; set; } + public int Value { get; set; } + + public void ConfigureMapping(TypeAdapterConfig config) + { + config.NewConfig() + .Map(dest => dest.Value, _ => DesireValues.Number); + } +} +``` + +Even if your destination model doesn't have a specific configuration (you don't want to customize anything), you can just inherit from IMapFrom interface + +Example: +```csharp +public class DestinationModel : IMapFrom +{ + public string Type { get; set; } +} +``` \ No newline at end of file From 2aa41488b85847e7615f75e80b97312c7d1d568d Mon Sep 17 00:00:00 2001 From: Aref Azizian Date: Thu, 9 Feb 2023 21:59:52 +0330 Subject: [PATCH 184/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_Sidebar.md b/_Sidebar.md index 94859f26..b03ce1ce 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -8,6 +8,7 @@ * [Basic usages](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) * [Mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) * [Data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) +* [Mapping with interface](https://github.com/MapsterMapper/Mapster/wiki/Mapping-Configuration-With-%22IMapFrom%22-Interface) ##### Configuration * [Configuration](https://github.com/MapsterMapper/Mapster/wiki/Configuration) From ec6f17811fa3d80f41732cb178f9aa4f581e8e05 Mon Sep 17 00:00:00 2001 From: Aref Azizian Date: Thu, 9 Feb 2023 22:01:30 +0330 Subject: [PATCH 185/363] Updated Mapping Configuration With "IMapFrom" Interface (markdown) --- "Mapping-Configuration-With-\"IMapFrom\"-Interface.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/Mapping-Configuration-With-\"IMapFrom\"-Interface.md" "b/Mapping-Configuration-With-\"IMapFrom\"-Interface.md" index 812aede3..7c59698a 100644 --- "a/Mapping-Configuration-With-\"IMapFrom\"-Interface.md" +++ "b/Mapping-Configuration-With-\"IMapFrom\"-Interface.md" @@ -14,7 +14,7 @@ public class InheritedDestinationModel : IMapFrom public void ConfigureMapping(TypeAdapterConfig config) { config.NewConfig() - .Map(dest => dest.Value, _ => DesireValues.Number); + .Map(dest => dest.Value, source => int.Parse(source.Value)); } } ``` From 5d8d34275b8bd42d54a3989389495c81b0cfb071 Mon Sep 17 00:00:00 2001 From: dialloi659 <44133766+dialloi659@users.noreply.github.com> Date: Wed, 3 May 2023 15:40:51 +0200 Subject: [PATCH 186/363] Updated Custom mapping (markdown) --- Custom-mapping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Custom-mapping.md b/Custom-mapping.md index 2f336609..7344265f 100644 --- a/Custom-mapping.md +++ b/Custom-mapping.md @@ -1,4 +1,4 @@ -### Custom member mapping +### Custom member mapping hihi You can customize how Mapster maps values to a property. From 971c5a1036cb2c4792c735e5c9832365aa873f66 Mon Sep 17 00:00:00 2001 From: joshslark Date: Sat, 20 May 2023 00:36:42 -0500 Subject: [PATCH 187/363] Updated Custom mapping (markdown) --- Custom-mapping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Custom-mapping.md b/Custom-mapping.md index 7344265f..2f336609 100644 --- a/Custom-mapping.md +++ b/Custom-mapping.md @@ -1,4 +1,4 @@ -### Custom member mapping hihi +### Custom member mapping You can customize how Mapster maps values to a property. From a83925e49afc00269582b7e7ea72f5b223b55171 Mon Sep 17 00:00:00 2001 From: PedramShateri <39373637+PedramShateri@users.noreply.github.com> Date: Sun, 2 Jul 2023 15:09:33 +0330 Subject: [PATCH 188/363] Updated _Sidebar (markdown) From 35bb741b2af902743e14e7be01b8cc5086a5ab5e Mon Sep 17 00:00:00 2001 From: PedramShateri <39373637+PedramShateri@users.noreply.github.com> Date: Sun, 2 Jul 2023 15:09:43 +0330 Subject: [PATCH 189/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_Sidebar.md b/_Sidebar.md index b03ce1ce..916d8aa1 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -51,3 +51,4 @@ * [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation) * [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation) * [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) +test From 4c8f00e446e1cfc19dfef2ca79cc600b8f16c388 Mon Sep 17 00:00:00 2001 From: PedramShateri <39373637+PedramShateri@users.noreply.github.com> Date: Sun, 2 Jul 2023 15:09:55 +0330 Subject: [PATCH 190/363] Updated _Sidebar (markdown) --- _Sidebar.md | 1 - 1 file changed, 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index 916d8aa1..b03ce1ce 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -51,4 +51,3 @@ * [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation) * [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation) * [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) -test From 9f4881b866a1b727e5ba07b71b87162a1ef3c2dd Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Thu, 21 Sep 2023 23:36:05 +0200 Subject: [PATCH 191/363] Add net8.0 framework moniker and update test-related packages --- src/ExpressionDebugger/ExpressionDebugger.csproj | 2 +- src/ExpressionTranslator/ExpressionTranslator.csproj | 2 +- src/Mapster.Async.Tests/Mapster.Async.Tests.csproj | 10 +++++----- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.Tests.csproj | 10 +++++----- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj | 10 +++++----- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- .../Mapster.Immutable.Tests.csproj | 10 +++++----- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj | 10 +++++----- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- .../Mapster.SourceGenerator.csproj | 2 +- src/Mapster.Tests/Mapster.Tests.csproj | 8 ++++---- src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj | 4 ++-- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- src/Sample.AspNetCore/Sample.AspNetCore.csproj | 2 +- src/Sample.CodeGen/Sample.CodeGen.csproj | 2 +- src/TemplateTest/TemplateTest.csproj | 10 +++++----- 22 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index 530de3d6..8b5e81f6 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0 + net6.0;net7.0;net8.0; True Chaowlert Chaisrichalermpol Step into debugging from linq expressions diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index 8d4f5de6..0bad5ffc 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0 + net6.0;net7.0;net8.0; True Chaowlert Chaisrichalermpol Translate from linq expressions to C# code diff --git a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj index fea91e85..835d8547 100644 --- a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj +++ b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj @@ -1,15 +1,15 @@ - + - net7.0;net6.0 + net8.0;net7.0;net6.0 false - - - + + + diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index dc1b1d76..ea7f0594 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 Async supports for Mapster true Mapster;Async diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index df7ee33b..63602c75 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -1,7 +1,7 @@  Lightweight library for Mapster and Mapster CodeGen - net7.0;net6.0 + net8.0;net7.0;net6.0 Mapster.Core mapster 1.2.1 diff --git a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj index e0b62dae..4f02a26a 100644 --- a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj +++ b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj @@ -1,16 +1,16 @@ - + - net7.0;net6.0 + net8.0;net7.0;net6.0 false - - - + + + diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 6a9be860..c6ce1228 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 Dependency Injection supports for Mapster true Mapster;DependencyInjection diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index d73d7e87..df5936c1 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 EF6 plugin for Mapster true Mapster;EF6 diff --git a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj index ca407d5b..e0a55450 100644 --- a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj +++ b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj @@ -1,16 +1,16 @@ - + - net7.0;net6.0 + net8.0;net7.0;net6.0 false - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index e4e75b39..e33a9436 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 EFCore plugin for Mapster true Mapster;EFCore diff --git a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj index 4b52ed69..bbb9f594 100644 --- a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj +++ b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj @@ -1,15 +1,15 @@ - + - net7.0;net6.0 + net8.0;net7.0;net6.0 false - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index e072691b..b035613b 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 Immutable collection supports for Mapster true Mapster;Immutable diff --git a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj index b7173dfe..1841f5c6 100644 --- a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj +++ b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj @@ -1,15 +1,15 @@ - + - net7.0;net6.0 + net8.0;net7.0;net6.0 false - - - + + + diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index a08db26e..8c2f8dd3 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 Json.net conversion supports for Mapster true Mapster;Json.net diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index f436bada..74dbf23e 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 Source generator to generate mapping using Mapster source-generator;mapster true diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index 1ea56308..4121ac36 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -1,7 +1,7 @@ - net7.0;net6.0 + net8.0;net7.0;net6.0 false Mapster.Tests Mapster.Tests.snk @@ -9,9 +9,9 @@ true - - - + + + diff --git a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj index 9ddf4c8d..c42d2964 100644 --- a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj +++ b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -10,7 +10,7 @@ - + diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 17e614a6..6798003f 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -2,7 +2,7 @@ Exe - net7.0;net6.0 + net8.0;net7.0;net6.0 true true dotnet-mapster diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index 6526c895..f67277cc 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -4,7 +4,7 @@ A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Copyright (c) 2016 Chaowlert Chaisrichalermpol, Eric Swann chaowlert;eric_swann - net7.0;net6.0 + net8.0;net7.0;net6.0 Mapster A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Mapster diff --git a/src/Sample.AspNetCore/Sample.AspNetCore.csproj b/src/Sample.AspNetCore/Sample.AspNetCore.csproj index c5761156..7cf57a89 100644 --- a/src/Sample.AspNetCore/Sample.AspNetCore.csproj +++ b/src/Sample.AspNetCore/Sample.AspNetCore.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 diff --git a/src/Sample.CodeGen/Sample.CodeGen.csproj b/src/Sample.CodeGen/Sample.CodeGen.csproj index 27485d31..f288e135 100644 --- a/src/Sample.CodeGen/Sample.CodeGen.csproj +++ b/src/Sample.CodeGen/Sample.CodeGen.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net8.0;net7.0;net6.0 enable diff --git a/src/TemplateTest/TemplateTest.csproj b/src/TemplateTest/TemplateTest.csproj index 2f6524f3..8a3bc1b3 100644 --- a/src/TemplateTest/TemplateTest.csproj +++ b/src/TemplateTest/TemplateTest.csproj @@ -1,16 +1,16 @@ - + - net7.0;net6.0 + net8.0;net7.0;net6.0 enable false - - - + + + From 5026e650dbe6dbdb04e07b7bb097a55d6960a29e Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Thu, 21 Sep 2023 23:42:34 +0200 Subject: [PATCH 192/363] Add .net7.0 moniker to Mapster.Tool.Tests.csproj and upgrade the Mapster.Tool install to 8.4.0. --- src/Mapster.Tool.Tests/.config/dotnet-tools.json | 2 +- src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster.Tool.Tests/.config/dotnet-tools.json b/src/Mapster.Tool.Tests/.config/dotnet-tools.json index 0c61f2d3..db4df7b6 100644 --- a/src/Mapster.Tool.Tests/.config/dotnet-tools.json +++ b/src/Mapster.Tool.Tests/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "mapster.tool": { - "version": "8.3.0", + "version": "8.4.0", "commands": [ "dotnet-mapster" ] diff --git a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj index c42d2964..f5797195 100644 --- a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj +++ b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0;net6.0 enable enable From 7f1935cf375ca2b2259d59a289f5c86e6544575d Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Thu, 21 Sep 2023 23:50:02 +0200 Subject: [PATCH 193/363] Upgrade build pipeline to .NET 8. This still won't build until 8.0.x is available on GitHub. --- .github/workflows/dotnet-buildandtest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet-buildandtest.yml b/.github/workflows/dotnet-buildandtest.yml index efad3f06..7e30a04a 100644 --- a/.github/workflows/dotnet-buildandtest.yml +++ b/.github/workflows/dotnet-buildandtest.yml @@ -16,12 +16,12 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Show dotnet version run: | dotnet --list-sdks dotnet --list-runtimes - name: Build with dotnet run: dotnet build ./src/Mapster.sln - - name: Run tests on .NET 7.0 + - name: Run tests on .NET 8.0 run: dotnet test --verbosity normal ./src/Mapster.sln From 2f7034d33fd0f68f4dacf2bf81ce9faaa3c4c406 Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Fri, 22 Sep 2023 23:42:24 +0200 Subject: [PATCH 194/363] Update version numbers --- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index ea7f0594..9ec82a66 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -7,7 +7,7 @@ Mapster;Async true Mapster.Async.snk - 2.0.1 + 2.0.2-pre01 diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index 63602c75..5bfd0a54 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -4,7 +4,7 @@ net8.0;net7.0;net6.0 Mapster.Core mapster - 1.2.1 + 1.2.2-pre01 enable true true diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index c6ce1228..f806c551 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -7,7 +7,7 @@ Mapster;DependencyInjection true Mapster.DependencyInjection.snk - 1.0.1 + 1.0.2-pre01 diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index df5936c1..2f4b1b25 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -8,7 +8,7 @@ True true Mapster.EF6.snk - 2.0.1 + 2.0.2-pre01 diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index e33a9436..db82d4b6 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -8,7 +8,7 @@ True true Mapster.EFCore.snk - 5.1.1 + 5.1.2-pre01 diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index b035613b..6bfa1057 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -7,7 +7,7 @@ Mapster;Immutable true Mapster.Immutable.snk - 1.0.1 + 1.0.2-pre01 enable diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 8c2f8dd3..0101057d 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -7,7 +7,7 @@ Mapster;Json.net true Mapster.JsonNet.snk - 1.1.1 + 1.1.2-pre01 diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 6798003f..d7921c64 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -10,7 +10,7 @@ Mapster;Tool true Mapster.Tool.snk - 8.4.0 + 8.4.1-pre01 enable diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index f67277cc..60ff3663 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -16,7 +16,7 @@ true Mapster - 7.4.0 + 7.4.1-pre01 enable 1701;1702;8618 From db8aa28d0bc286fa6379f5cc56ab769dcbdf320c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 24 Sep 2023 22:11:57 +0500 Subject: [PATCH 195/363] partial fix issue #537 not fix - collection issue #430 #546 - object to ToDistination #524 --- .../WhenIgnoringConditionally.cs | 3 +- src/Mapster.Tests/WhenRecordRegration.cs | 415 ++++++++++++++++++ src/Mapster/Adapters/ClassAdapter.cs | 23 +- src/Mapster/Adapters/RecordTypeAdapter.cs | 6 +- src/Mapster/Utils/ReflectionUtils.cs | 61 ++- 5 files changed, 488 insertions(+), 20 deletions(-) create mode 100644 src/Mapster.Tests/WhenRecordRegration.cs diff --git a/src/Mapster.Tests/WhenIgnoringConditionally.cs b/src/Mapster.Tests/WhenIgnoringConditionally.cs index a92106f0..d7377a37 100644 --- a/src/Mapster.Tests/WhenIgnoringConditionally.cs +++ b/src/Mapster.Tests/WhenIgnoringConditionally.cs @@ -160,6 +160,7 @@ public void IgnoreIf_Can_Be_Combined() public void IgnoreIf_Apply_To_RecordType() { TypeAdapterConfig.NewConfig() + .EnableNonPublicMembers(true) // add or .IgnoreIf((src, dest) => src.Name == "TestName", dest => dest.Name) .Compile(); @@ -187,7 +188,7 @@ public class SimpleDto public string Name { get; set; } } - public class SimpleRecord + public class SimpleRecord // or Replace on record { public int Id { get; } public string Name { get; } diff --git a/src/Mapster.Tests/WhenRecordRegration.cs b/src/Mapster.Tests/WhenRecordRegration.cs new file mode 100644 index 00000000..c6d99b51 --- /dev/null +++ b/src/Mapster.Tests/WhenRecordRegration.cs @@ -0,0 +1,415 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System; +using System.Collections.Generic; + +namespace Mapster.Tests +{ + /// + /// Tests for https://github.com/MapsterMapper/Mapster/issues/537 + /// + [TestClass] + + public class WhenRecordRegress + { + + + [TestMethod] + public void AdaptRecord() + { + var _sourse = new TestREcord() { X = 700 }; + + var _destination = new TestREcord() { X = 500 }; + + var _result = _sourse.Adapt(_destination); + + object.ReferenceEquals(_result, _destination).ShouldBeFalse(); + + + var _sourcePositional = new TestRecordPositional(600); + + var _destinationPositional = new TestRecordPositional(900); + + var _positionalResult = _sourcePositional.Adapt(_destinationPositional); + + object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); + + + + + var _sourceStruct = new TestRecordStruct() { X = 1000 }; + + var _destinationStruct = new TestRecordStruct() { X = 800 }; + + var _structResult = _sourceStruct.Adapt(_destinationStruct); + + object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); + + + _result.X.ShouldBe(700); + + _positionalResult.X.ShouldBe(600); + + _structResult.X.ShouldBe(1000); + } + + + [TestMethod] + public void AdaptRecordToClass() + { + var _sourse = new TestRecordPositional(200); + + var _destination = new TestClassProtectedCtr(400); + + var _result = _sourse.Adapt(_destination); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + + _destination.ShouldBeOfType(); + + _destination.X.ShouldBe(200); + } + + [TestMethod] + public void AdaptClassToRecord() + { + var _sourse = new TestClassProtectedCtr(200); + + var _destination = new TestRecordPositional(400); + + var _result = _sourse.Adapt(_destination); + + + object.ReferenceEquals(_destination, _result).ShouldBeFalse(); + + _destination.ShouldBeOfType(); + + _result.X.ShouldBe(200); + } + + + + + + [TestMethod] + public void AdaptClassIsNotNewInstanse() + { + var _sourse = new TestClassPublicCtr(200); + + var _destination = new TestClassProtectedCtr(400); + + var _result = _sourse.Adapt(_destination); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + + _destination.ShouldBeOfType(); + + _destination.X.ShouldBe(200); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/615 + /// + [TestMethod] + public void AdaptClassIncludeStruct() + { + TypeAdapterConfig + .ForType() + .Map(x => x.TestStruct, x => x.SourceWithStruct.TestStruct); + + var source = new SourceWithClass + { + SourceWithStruct = new SourceWithStruct + { + TestStruct = new TestStruct("A") + } + }; + + var destination = source.Adapt(); + + destination.TestStruct.Property.ShouldBe("A"); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/482 + /// + [TestMethod] + public void AdaptClassIsPrivateProperty() + { + var _sourse = new TestClassPublicCtr(200); + + var _destination = new TestClassProtectedCtrPrivateProperty(400, "Me"); + + var _result = _sourse.Adapt(_destination); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + + _destination.ShouldBeOfType(); + + _destination.X.ShouldBe(200); + + _destination.Name.ShouldBe("Me"); + } + + + /// + /// https://github.com/MapsterMapper/Mapster/issues/427 + /// + [TestMethod] + public void UpdateNullable() + { + var _sourse = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); + + var _update = new UpdateUser + { + Id = "123", + + }; + + var configDate = new TypeAdapterConfig(); + configDate.ForType() + .Map(dest => dest.Modified, src => new DateTime(2025, 9, 24)) + .IgnoreNullValues(true); + + + _update.Adapt(_sourse, configDate); + + + + var _sourseEmailUpdate = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); + + var _updateEmail = new UpdateUser + { + Email = "245@gmail.com", + }; + + + var config = new TypeAdapterConfig(); + config.ForType() + + .IgnoreNullValues(true); + + + var _resultEmail = _updateEmail.Adapt(_sourseEmailUpdate, config); + + + _sourse.Id.ShouldBe("123"); + _sourse.Created.ShouldBe(new DateTime(2023, 9, 24)); + _sourse.Modified.ShouldBe(new DateTime(2025, 9, 24)); + _sourse.Email.ShouldBe("123@gmail.com"); + + + _sourseEmailUpdate.Id.ShouldBe("123"); + _sourseEmailUpdate.Created.ShouldBe(new DateTime(2023, 9, 24)); + _sourseEmailUpdate.Modified.ShouldBe(null); + _sourseEmailUpdate.Email.ShouldBe("245@gmail.com"); + + + + } + + } + + + + + + + #region NowNotWorking + + + + + /// + /// https://github.com/MapsterMapper/Mapster/issues/430 + /// + // [TestMethod] + // public void CollectionUpdate() + // { + // List sources = new() + // { + // new(541), + // new(234) + + // }; + + + // var destination = new List(); + + + // var _result = sources.Adapt(destination); + + // destination.Count.ShouldBe(_result.Count); + + // } + + // /// + // /// https://github.com/MapsterMapper/Mapster/issues/524 + // /// + // [TestMethod] + // public void TSousreIsObjectUpdate() + // { + // var source = new TestClassPublicCtr { X = 123 }; + + // var _result = Somemap(source); + + // _result.X.ShouldBe(123); + + // } + + // TestClassPublicCtr Somemap(object source) + // { + // var dest = new TestClassPublicCtr { X = 321 }; + // var dest1 = source.Adapt(dest); + + // return dest; + // } + + + + + //} + + + + + + #endregion NowNotWorking + + + + + + #region TestClases + + + class UserAccount + { + public UserAccount(string id, string email, DateTime created) + { + Id = id; + Email = email; + Created = created; + + } + + protected UserAccount() { } + + public string Id { get; set; } + public string? Email { get; set; } + public DateTime Created { get; set; } + public DateTime? Modified { get; set; } + } + + class UpdateUser + { + public string? Id { get; set; } + public string? Email { get; set; } + + public DateTime? Created { get; set; } + public DateTime? Modified { get; set; } + + } + + + + + + + class DestinationWithStruct + { + public TestStruct TestStruct { get; set; } + } + + class SourceWithClass + { + public SourceWithStruct SourceWithStruct { get; set; } + } + + class SourceWithStruct + { + public TestStruct TestStruct { get; set; } + } + + struct TestStruct + { + public string Property { get; } + + public TestStruct(string property) : this() + { + Property = property; + } + } + + + class TestClassPublicCtr + { + public TestClassPublicCtr() + { + + } + + public TestClassPublicCtr(int x) + { + X = x; + } + + public int X { get; set; } + } + + + class TestClassProtectedCtr + { + protected TestClassProtectedCtr() + { + + } + + public TestClassProtectedCtr(int x) + { + X = x; + } + + public int X { get; set; } + } + + + class TestClassProtectedCtrPrivateProperty + { + protected TestClassProtectedCtrPrivateProperty() + { + + } + + public TestClassProtectedCtrPrivateProperty(int x, string name) + { + X = x; + Name = name; + } + + public int X { get; private set; } + + public string Name { get; private set; } + } + + + record TestREcord() + { + + + public int X { set; get; } + } + + record TestRecordPositional(int X); + + record struct TestRecordStruct + { + public int X { set; get; } + } + + + #endregion TestClases +} diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index cc803d8d..49027a49 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -54,7 +54,28 @@ protected override Expression CreateInstantiationExpression(Expression source, E { //new TDestination(src.Prop1, src.Prop2) - if (arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) + /// + bool IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(CompileArgument arg) + { + if (arg.Settings.EnableNonPublicMembers == null) + return false; + if (arg.Settings.EnableNonPublicMembers == false) + return false; + else + { + if (arg.DestinationType.GetConstructors().Any(x => x.GetParameters() != null)) + { + return true; + } + } + + + return false; + } + + + + if ((arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) && !IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(arg)) return base.CreateInstantiationExpression(source, destination, arg); ClassMapping? classConverter; diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index e5812e16..a0034829 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -4,7 +4,7 @@ namespace Mapster.Adapters { - internal class RecordTypeAdapter : BaseClassAdapter + internal class RecordTypeAdapter : ClassAdapter { protected override int Score => -149; protected override bool UseTargetValue => false; @@ -34,12 +34,12 @@ protected override Expression CreateInstantiationExpression(Expression source, E protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { - return Expression.Empty(); + return base.CreateBlockExpression(source, destination, arg); } protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { - return CreateInstantiationExpression(source, arg); + return base.CreateInstantiationExpression(source, arg); } } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 5959daa5..4d591666 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -170,27 +170,58 @@ public static bool IsRecordType(this Type type) var props = type.GetFieldsAndProperties().ToList(); + /// + /// + /// + #region SupportingСurrentBehavior for Config Clone and Fork + + if (type == typeof(MulticastDelegate)) + return true; + + if (type == typeof(TypeAdapterSetter)) + return true; + + // if (type == typeof(TypeAdapterRule)) + // return true; + + if (type == typeof(TypeAdapterSettings)) + return true; + + if (type.IsValueType && type?.GetConstructors().Length != 0) + { + var test = type.GetConstructors()[0].GetParameters(); + var param = type.GetConstructors()[0].GetParameters().ToArray(); + + if (param[0]?.ParameterType == typeof(TypeTuple) && param[1]?.ParameterType == typeof(TypeAdapterRule)) + return true; + } + + if (type == typeof(TypeTuple)) + return true; + + #endregion SupportingСurrentBehavior for Config Clone and Fork + + //interface with readonly props - if (type.GetTypeInfo().IsInterface && + if (type.GetTypeInfo().IsInterface && props.Any(p => p.SetterModifier != AccessModifier.Public)) return true; - //1 constructor - var ctors = type.GetConstructors().ToList(); - if (ctors.Count != 1) - return false; + //constructor + var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); - //ctor must not empty - var ctorParams = ctors[0].GetParameters(); - if (ctorParams.Length == 0) - return false; + var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) + .Where(x => x.IsFamily == true) + .Any(x => x.GetParameters() + .Any(y => y.ParameterType == type)); - //all parameters should match getter - return props.All(prop => - { - var name = prop.Name.ToPascalCase(); - return ctorParams.Any(p => p.ParameterType == prop.Type && p.Name?.ToPascalCase() == name); - }); + + if (ctors.Count >= 2 && isRecordTypeCtor) + return true; + + + + return false; } public static bool IsConvertible(this Type type) From 88fb01843f1349e3c5a1d91ed978b87a7518d1c5 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 25 Sep 2023 06:24:18 +0500 Subject: [PATCH 196/363] add FakeRecord --- src/Mapster.Tests/WhenRecordRegration.cs | 33 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/Mapster.Tests/WhenRecordRegration.cs b/src/Mapster.Tests/WhenRecordRegration.cs index c6d99b51..b4078a30 100644 --- a/src/Mapster.Tests/WhenRecordRegration.cs +++ b/src/Mapster.Tests/WhenRecordRegration.cs @@ -211,7 +211,7 @@ public void UpdateNullable() } - } + @@ -220,7 +220,15 @@ public void UpdateNullable() #region NowNotWorking + [TestMethod] + public void DetectFakeRecord() + { + var _sourse = new TestClassPublicCtr(200); + var _destination = new FakeRecord { X = 300 }; + + var _result = _sourse.Adapt(_destination); + } /// @@ -268,16 +276,16 @@ public void UpdateNullable() // return dest; // } + #endregion NowNotWorking + } - //} - - #endregion NowNotWorking + @@ -285,6 +293,23 @@ public void UpdateNullable() #region TestClases + public class FakeRecord + { + protected FakeRecord(FakeRecord fake) + { + + } + + public FakeRecord() + { + + } + + public int X { get; set; } + } + + + class UserAccount { From 14a674e1cc12d12c8fb212c8cadd1d044ed94023 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 25 Sep 2023 17:25:52 +0500 Subject: [PATCH 197/363] refactoring test --- src/Mapster.Tests/WhenRecordRegration.cs | 102 +++++++++++++++++------ 1 file changed, 77 insertions(+), 25 deletions(-) diff --git a/src/Mapster.Tests/WhenRecordRegration.cs b/src/Mapster.Tests/WhenRecordRegration.cs index b4078a30..8d3fc51b 100644 --- a/src/Mapster.Tests/WhenRecordRegration.cs +++ b/src/Mapster.Tests/WhenRecordRegration.cs @@ -15,7 +15,7 @@ public class WhenRecordRegress [TestMethod] - public void AdaptRecord() + public void AdaptRecordToRecord() { var _sourse = new TestREcord() { X = 700 }; @@ -23,37 +23,52 @@ public void AdaptRecord() var _result = _sourse.Adapt(_destination); - object.ReferenceEquals(_result, _destination).ShouldBeFalse(); + _result.X.ShouldBe(700); + + object.ReferenceEquals(_result, _destination).ShouldBeFalse(); + } + + [TestMethod] + public void AdaptPositionalRecordToPositionalRecord() + { var _sourcePositional = new TestRecordPositional(600); var _destinationPositional = new TestRecordPositional(900); var _positionalResult = _sourcePositional.Adapt(_destinationPositional); - object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); + _positionalResult.X.ShouldBe(600); + + object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); + } + [TestMethod] + public void AdaptRecordStructToRecordStruct() + { var _sourceStruct = new TestRecordStruct() { X = 1000 }; var _destinationStruct = new TestRecordStruct() { X = 800 }; var _structResult = _sourceStruct.Adapt(_destinationStruct); - object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); - _result.X.ShouldBe(700); + _structResult.X.ShouldBe(1000); + + object.ReferenceEquals(_destinationStruct, _structResult).ShouldBeFalse(); - _positionalResult.X.ShouldBe(600); - _structResult.X.ShouldBe(1000); } + + + [TestMethod] public void AdaptRecordToClass() { @@ -64,11 +79,14 @@ public void AdaptRecordToClass() var _result = _sourse.Adapt(_destination); - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + _destination.ShouldBeOfType(); _destination.X.ShouldBe(200); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } [TestMethod] @@ -81,11 +99,14 @@ public void AdaptClassToRecord() var _result = _sourse.Adapt(_destination); - object.ReferenceEquals(_destination, _result).ShouldBeFalse(); + _destination.ShouldBeOfType(); _result.X.ShouldBe(200); + + + object.ReferenceEquals(_destination, _result).ShouldBeFalse(); } @@ -93,22 +114,54 @@ public void AdaptClassToRecord() [TestMethod] - public void AdaptClassIsNotNewInstanse() + public void AdaptClassToClassPublicCtr_IsNotInstanse() { var _sourse = new TestClassPublicCtr(200); - var _destination = new TestClassProtectedCtr(400); + var _destination = new TestClassPublicCtr(400); var _result = _sourse.Adapt(_destination); + + + _destination.ShouldBeOfType(); + + _destination.X.ShouldBe(200); + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + } + + + [TestMethod] + public void AdaptClassToClassProtectdCtr_IsNotInstanse() + { + var _sourse = new TestClassPublicCtr(200); + + var _destination = new TestClassProtectedCtr(400); + + var _result = _sourse.Adapt(_destination); + + + _destination.ShouldBeOfType(); _destination.X.ShouldBe(200); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } + + + + + + + + /// /// https://github.com/MapsterMapper/Mapster/issues/615 /// @@ -136,7 +189,7 @@ public void AdaptClassIncludeStruct() /// https://github.com/MapsterMapper/Mapster/issues/482 /// [TestMethod] - public void AdaptClassIsPrivateProperty() + public void AdaptClassToClassFromPrivateProperty_IsNotInstanse() { var _sourse = new TestClassPublicCtr(200); @@ -145,13 +198,16 @@ public void AdaptClassIsPrivateProperty() var _result = _sourse.Adapt(_destination); - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + _destination.ShouldBeOfType(); _destination.X.ShouldBe(200); _destination.Name.ShouldBe("Me"); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } @@ -214,21 +270,17 @@ public void UpdateNullable() + #region NowNotWorking + // [TestMethod] + //public void DetectFakeRecord() + //{ + // var _sourse = new TestClassPublicCtr(200); + // var _destination = new FakeRecord { X = 300 }; - - #region NowNotWorking - - [TestMethod] - public void DetectFakeRecord() - { - var _sourse = new TestClassPublicCtr(200); - - var _destination = new FakeRecord { X = 300 }; - - var _result = _sourse.Adapt(_destination); - } + // var _result = _sourse.Adapt(_destination); + //} /// From 397d5a4915b15287aea16e404ef4f0ccf2144f96 Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Tue, 26 Sep 2023 23:24:36 +0200 Subject: [PATCH 198/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index b1feca32..b2a678b4 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -116,9 +116,6 @@ Sample.CodeGen - Sub2 - Dto2 ``` - - - ### Generate DTOs and mapping codes There are 3 flavors, to generate DTOs and mapping codes @@ -128,4 +125,38 @@ There are 3 flavors, to generate DTOs and mapping codes ### Sample -- https://github.com/MapsterMapper/Mapster/tree/master/src/Sample.CodeGen \ No newline at end of file +- https://github.com/MapsterMapper/Mapster/tree/master/src/Sample.CodeGen + +### Troubleshooting + +#### System.IO.FileNotFoundException +If you get an error similar to `Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly '...'. The system cannot find the file specified.` and you are using Mapster.Tool 8.4.1 or newer, then you can try one of the following workarounds: + +**Workaround 1** + +Add `true` to your csproj file as follows: + +```xml + + + net8.0 + true + + [...] +``` + +**Workaround 2** + +Change your `dotnet build` command to `dotnet build -p:CopyLocalLockFileAssemblies=true` as follows: + +```xml + + + [...] + +``` + + + +``` +``` \ No newline at end of file From 9b91b85fa5bf2fe866f42a6d72e6e9a1b6c00877 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 27 Sep 2023 14:29:51 +0500 Subject: [PATCH 199/363] Fix From Rewiew --- ...tion.cs => WhenMappingRecordRegression.cs} | 127 +++++++++--------- 1 file changed, 65 insertions(+), 62 deletions(-) rename src/Mapster.Tests/{WhenRecordRegration.cs => WhenMappingRecordRegression.cs} (75%) diff --git a/src/Mapster.Tests/WhenRecordRegration.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs similarity index 75% rename from src/Mapster.Tests/WhenRecordRegration.cs rename to src/Mapster.Tests/WhenMappingRecordRegression.cs index 8d3fc51b..efc48053 100644 --- a/src/Mapster.Tests/WhenRecordRegration.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -10,18 +10,18 @@ namespace Mapster.Tests /// [TestClass] - public class WhenRecordRegress + public class WhenMappingRecordRegression { [TestMethod] public void AdaptRecordToRecord() { - var _sourse = new TestREcord() { X = 700 }; + var _source = new TestRecord() { X = 700 }; - var _destination = new TestREcord() { X = 500 }; + var _destination = new TestRecord() { X = 500 }; - var _result = _sourse.Adapt(_destination); + var _result = _source.Adapt(_destination); @@ -72,11 +72,11 @@ public void AdaptRecordStructToRecordStruct() [TestMethod] public void AdaptRecordToClass() { - var _sourse = new TestRecordPositional(200); + var _sourсe = new TestRecordPositional(200); var _destination = new TestClassProtectedCtr(400); - var _result = _sourse.Adapt(_destination); + var _result = _sourсe.Adapt(_destination); @@ -92,11 +92,11 @@ public void AdaptRecordToClass() [TestMethod] public void AdaptClassToRecord() { - var _sourse = new TestClassProtectedCtr(200); + var _sourсe = new TestClassProtectedCtr(200); var _destination = new TestRecordPositional(400); - var _result = _sourse.Adapt(_destination); + var _result = _sourсe.Adapt(_destination); @@ -116,11 +116,11 @@ public void AdaptClassToRecord() [TestMethod] public void AdaptClassToClassPublicCtr_IsNotInstanse() { - var _sourse = new TestClassPublicCtr(200); + var _source = new TestClassPublicCtr(200); var _destination = new TestClassPublicCtr(400); - var _result = _sourse.Adapt(_destination); + var _result = _source.Adapt(_destination); @@ -137,11 +137,11 @@ public void AdaptClassToClassPublicCtr_IsNotInstanse() [TestMethod] public void AdaptClassToClassProtectdCtr_IsNotInstanse() { - var _sourse = new TestClassPublicCtr(200); + var _source = new TestClassPublicCtr(200); var _destination = new TestClassProtectedCtr(400); - var _result = _sourse.Adapt(_destination); + var _result = _source.Adapt(_destination); @@ -191,11 +191,11 @@ public void AdaptClassIncludeStruct() [TestMethod] public void AdaptClassToClassFromPrivateProperty_IsNotInstanse() { - var _sourse = new TestClassPublicCtr(200); + var _source = new TestClassPublicCtr(200); var _destination = new TestClassProtectedCtrPrivateProperty(400, "Me"); - var _result = _sourse.Adapt(_destination); + var _result = _source.Adapt(_destination); @@ -217,7 +217,7 @@ public void AdaptClassToClassFromPrivateProperty_IsNotInstanse() [TestMethod] public void UpdateNullable() { - var _sourse = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); + var _source = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); var _update = new UpdateUser { @@ -231,7 +231,7 @@ public void UpdateNullable() .IgnoreNullValues(true); - _update.Adapt(_sourse, configDate); + _update.Adapt(_source, configDate); @@ -252,10 +252,10 @@ public void UpdateNullable() var _resultEmail = _updateEmail.Adapt(_sourseEmailUpdate, config); - _sourse.Id.ShouldBe("123"); - _sourse.Created.ShouldBe(new DateTime(2023, 9, 24)); - _sourse.Modified.ShouldBe(new DateTime(2025, 9, 24)); - _sourse.Email.ShouldBe("123@gmail.com"); + _source.Id.ShouldBe("123"); + _source.Created.ShouldBe(new DateTime(2023, 9, 24)); + _source.Modified.ShouldBe(new DateTime(2025, 9, 24)); + _source.Email.ShouldBe("123@gmail.com"); _sourseEmailUpdate.Id.ShouldBe("123"); @@ -272,61 +272,64 @@ public void UpdateNullable() #region NowNotWorking - // [TestMethod] - //public void DetectFakeRecord() - //{ - // var _sourse = new TestClassPublicCtr(200); + [TestMethod] + public void DetectFakeRecord() + { + var _source = new TestClassPublicCtr(200); - // var _destination = new FakeRecord { X = 300 }; + var _destination = new FakeRecord { X = 300 }; - // var _result = _sourse.Adapt(_destination); - //} + var _result = _source.Adapt(_destination); - /// - /// https://github.com/MapsterMapper/Mapster/issues/430 - /// - // [TestMethod] - // public void CollectionUpdate() - // { - // List sources = new() - // { - // new(541), - // new(234) + } + - // }; + /// + /// https://github.com/MapsterMapper/Mapster/issues/430 + /// + [TestMethod] + public void CollectionUpdate() + { + List sources = new() + { + new(541), + new(234) + + }; - // var destination = new List(); + var destination = new List(); - // var _result = sources.Adapt(destination); + var _result = sources.Adapt(destination); - // destination.Count.ShouldBe(_result.Count); + destination.Count.ShouldBe(_result.Count); - // } + } - // /// - // /// https://github.com/MapsterMapper/Mapster/issues/524 - // /// - // [TestMethod] - // public void TSousreIsObjectUpdate() - // { - // var source = new TestClassPublicCtr { X = 123 }; + /// + /// https://github.com/MapsterMapper/Mapster/issues/524 + /// + + [TestMethod] + public void TSousreIsObjectUpdate() + { + var source = new TestClassPublicCtr { X = 123 }; - // var _result = Somemap(source); + var _result = Somemap(source); - // _result.X.ShouldBe(123); + _result.X.ShouldBe(123); - // } + } - // TestClassPublicCtr Somemap(object source) - // { - // var dest = new TestClassPublicCtr { X = 321 }; - // var dest1 = source.Adapt(dest); + TestClassPublicCtr Somemap(object source) + { + var dest = new TestClassPublicCtr { X = 321 }; + var dest1 = source.Adapt(dest); - // return dest; - // } + return dest; + } #endregion NowNotWorking @@ -337,13 +340,13 @@ public void UpdateNullable() - - #region TestClases + + #region TestClasses public class FakeRecord { @@ -473,7 +476,7 @@ public TestClassProtectedCtrPrivateProperty(int x, string name) } - record TestREcord() + record TestRecord() { @@ -488,5 +491,5 @@ record struct TestRecordStruct } - #endregion TestClases + #endregion TestClasses } From 68380023ef4c10c35ee00a63c01e20e901c905c8 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 27 Sep 2023 14:38:20 +0500 Subject: [PATCH 200/363] fix deleting forgotten comments --- .../WhenMappingRecordRegression.cs | 64 +++++-------------- src/Mapster/Utils/ReflectionUtils.cs | 4 +- 2 files changed, 16 insertions(+), 52 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index efc48053..375f7686 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -12,8 +12,6 @@ namespace Mapster.Tests public class WhenMappingRecordRegression { - - [TestMethod] public void AdaptRecordToRecord() { @@ -66,9 +64,6 @@ public void AdaptRecordStructToRecordStruct() } - - - [TestMethod] public void AdaptRecordToClass() { @@ -109,10 +104,6 @@ public void AdaptClassToRecord() object.ReferenceEquals(_destination, _result).ShouldBeFalse(); } - - - - [TestMethod] public void AdaptClassToClassPublicCtr_IsNotInstanse() { @@ -155,13 +146,6 @@ public void AdaptClassToClassProtectdCtr_IsNotInstanse() } - - - - - - - /// /// https://github.com/MapsterMapper/Mapster/issues/615 /// @@ -268,21 +252,19 @@ public void UpdateNullable() } - - #region NowNotWorking [TestMethod] - public void DetectFakeRecord() - { - var _source = new TestClassPublicCtr(200); + public void DetectFakeRecord() + { + var _source = new TestClassPublicCtr(200); - var _destination = new FakeRecord { X = 300 }; + var _destination = new FakeRecord { X = 300 }; - var _result = _source.Adapt(_destination); + var _result = _source.Adapt(_destination); - } + } /// @@ -290,8 +272,8 @@ public void DetectFakeRecord() /// [TestMethod] public void CollectionUpdate() - { - List sources = new() + { + List sources = new() { new(541), new(234) @@ -302,16 +284,16 @@ public void CollectionUpdate() var destination = new List(); - var _result = sources.Adapt(destination); + var _result = sources.Adapt(destination); - destination.Count.ShouldBe(_result.Count); + destination.Count.ShouldBe(_result.Count); } /// /// https://github.com/MapsterMapper/Mapster/issues/524 /// - + [TestMethod] public void TSousreIsObjectUpdate() { @@ -323,7 +305,7 @@ public void TSousreIsObjectUpdate() } - TestClassPublicCtr Somemap(object source) + TestClassPublicCtr Somemap(object source) { var dest = new TestClassPublicCtr { X = 321 }; var dest1 = source.Adapt(dest); @@ -331,19 +313,10 @@ TestClassPublicCtr Somemap(object source) return dest; } - #endregion NowNotWorking - - - } - - - - - - - + #endregion NowNotWorking + } #region TestClasses @@ -364,8 +337,6 @@ public FakeRecord() } - - class UserAccount { public UserAccount(string id, string email, DateTime created) @@ -395,10 +366,6 @@ class UpdateUser } - - - - class DestinationWithStruct { public TestStruct TestStruct { get; set; } @@ -478,7 +445,7 @@ public TestClassProtectedCtrPrivateProperty(int x, string name) record TestRecord() { - + public int X { set; get; } } @@ -490,6 +457,5 @@ record struct TestRecordStruct public int X { set; get; } } - #endregion TestClasses } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 4d591666..28e30c12 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -170,9 +170,7 @@ public static bool IsRecordType(this Type type) var props = type.GetFieldsAndProperties().ToList(); - /// - /// - /// + #region SupportingСurrentBehavior for Config Clone and Fork if (type == typeof(MulticastDelegate)) From cf5515716d72547f293baa743843f655f44bc90c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 27 Sep 2023 15:33:07 +0500 Subject: [PATCH 201/363] Fix Naming_ add [Ignore] from not working test add new test from Adapt Object to Type --- .../WhenMappingRecordRegression.cs | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 375f7686..15588bd5 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -105,7 +105,7 @@ public void AdaptClassToRecord() } [TestMethod] - public void AdaptClassToClassPublicCtr_IsNotInstanse() + public void AdaptClassToClassPublicCtrIsNotInstanse() { var _source = new TestClassPublicCtr(200); @@ -126,7 +126,7 @@ public void AdaptClassToClassPublicCtr_IsNotInstanse() [TestMethod] - public void AdaptClassToClassProtectdCtr_IsNotInstanse() + public void AdaptClassToClassProtectdCtrIsNotInstanse() { var _source = new TestClassPublicCtr(200); @@ -173,7 +173,7 @@ public void AdaptClassIncludeStruct() /// https://github.com/MapsterMapper/Mapster/issues/482 /// [TestMethod] - public void AdaptClassToClassFromPrivateProperty_IsNotInstanse() + public void AdaptClassToClassFromPrivatePropertyIsNotInstanse() { var _source = new TestClassPublicCtr(200); @@ -252,9 +252,36 @@ public void UpdateNullable() } + /// + /// https://github.com/MapsterMapper/Mapster/issues/524 + /// + [TestMethod] + public void TSousreIsObjectUpdateUseDynamicCast() + { + var source = new TestClassPublicCtr { X = 123 }; + + var _result = SomemapWithDynamic(source); + + _result.X.ShouldBe(123); + + } + + TestClassPublicCtr SomemapWithDynamic(object source) + { + var dest = new TestClassPublicCtr { X = 321 }; + var dest1 = source.Adapt(dest,source.GetType(),dest.GetType()); + + return dest; + } + + + + + #region NowNotWorking [TestMethod] + [Ignore] public void DetectFakeRecord() { var _source = new TestClassPublicCtr(200); @@ -264,12 +291,18 @@ public void DetectFakeRecord() var _result = _source.Adapt(_destination); + _destination.X.ShouldBe(200); + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + + } /// - /// https://github.com/MapsterMapper/Mapster/issues/430 + /// https://github.com/MapsterMapper/Mapster/issues/430 /// + [Ignore] [TestMethod] public void CollectionUpdate() { @@ -291,9 +324,11 @@ public void CollectionUpdate() } /// - /// https://github.com/MapsterMapper/Mapster/issues/524 + /// https://github.com/MapsterMapper/Mapster/issues/524 + /// Not work. Already has a special overload: + /// .Adapt(this object source, object destination, Type sourceType, Type destinationType) /// - + [Ignore] [TestMethod] public void TSousreIsObjectUpdate() { @@ -308,7 +343,7 @@ public void TSousreIsObjectUpdate() TestClassPublicCtr Somemap(object source) { var dest = new TestClassPublicCtr { X = 321 }; - var dest1 = source.Adapt(dest); + var dest1 = source.Adapt(dest); // typeof(TSource) always return Type as Object. Need use dynamic or Cast to Runtime Type before Adapt return dest; } From 1ab9810613230ed303d32e9ea84f9d50adf99fe2 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 27 Sep 2023 22:39:51 +0500 Subject: [PATCH 202/363] remove whitespace and misprint _sourceEmailUpdate --- .../WhenMappingRecordRegression.cs | 161 ++---------------- 1 file changed, 15 insertions(+), 146 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 15588bd5..917e9683 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -9,22 +9,16 @@ namespace Mapster.Tests /// Tests for https://github.com/MapsterMapper/Mapster/issues/537 /// [TestClass] - public class WhenMappingRecordRegression { [TestMethod] public void AdaptRecordToRecord() { var _source = new TestRecord() { X = 700 }; - var _destination = new TestRecord() { X = 500 }; - var _result = _source.Adapt(_destination); - - _result.X.ShouldBe(700); - object.ReferenceEquals(_result, _destination).ShouldBeFalse(); } @@ -32,55 +26,33 @@ public void AdaptRecordToRecord() public void AdaptPositionalRecordToPositionalRecord() { var _sourcePositional = new TestRecordPositional(600); - var _destinationPositional = new TestRecordPositional(900); - var _positionalResult = _sourcePositional.Adapt(_destinationPositional); - - _positionalResult.X.ShouldBe(600); - object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); - } [TestMethod] public void AdaptRecordStructToRecordStruct() { var _sourceStruct = new TestRecordStruct() { X = 1000 }; - var _destinationStruct = new TestRecordStruct() { X = 800 }; - var _structResult = _sourceStruct.Adapt(_destinationStruct); - - _structResult.X.ShouldBe(1000); - object.ReferenceEquals(_destinationStruct, _structResult).ShouldBeFalse(); - - } - [TestMethod] public void AdaptRecordToClass() { var _sourсe = new TestRecordPositional(200); - var _destination = new TestClassProtectedCtr(400); - var _result = _sourсe.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _destination.X.ShouldBe(200); - - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } @@ -88,19 +60,11 @@ public void AdaptRecordToClass() public void AdaptClassToRecord() { var _sourсe = new TestClassProtectedCtr(200); - var _destination = new TestRecordPositional(400); - var _result = _sourсe.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _result.X.ShouldBe(200); - - object.ReferenceEquals(_destination, _result).ShouldBeFalse(); } @@ -108,44 +72,26 @@ public void AdaptClassToRecord() public void AdaptClassToClassPublicCtrIsNotInstanse() { var _source = new TestClassPublicCtr(200); - var _destination = new TestClassPublicCtr(400); - var _result = _source.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _destination.X.ShouldBe(200); - - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } - [TestMethod] public void AdaptClassToClassProtectdCtrIsNotInstanse() { var _source = new TestClassPublicCtr(200); - var _destination = new TestClassProtectedCtr(400); - var _result = _source.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _destination.X.ShouldBe(200); - - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } - /// /// https://github.com/MapsterMapper/Mapster/issues/615 /// @@ -165,7 +111,6 @@ public void AdaptClassIncludeStruct() }; var destination = source.Adapt(); - destination.TestStruct.Property.ShouldBe("A"); } @@ -176,25 +121,15 @@ public void AdaptClassIncludeStruct() public void AdaptClassToClassFromPrivatePropertyIsNotInstanse() { var _source = new TestClassPublicCtr(200); - var _destination = new TestClassProtectedCtrPrivateProperty(400, "Me"); - var _result = _source.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _destination.X.ShouldBe(200); - _destination.Name.ShouldBe("Me"); - - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } - /// /// https://github.com/MapsterMapper/Mapster/issues/427 /// @@ -202,56 +137,41 @@ public void AdaptClassToClassFromPrivatePropertyIsNotInstanse() public void UpdateNullable() { var _source = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); - var _update = new UpdateUser { Id = "123", - }; - var configDate = new TypeAdapterConfig(); + configDate.ForType() .Map(dest => dest.Modified, src => new DateTime(2025, 9, 24)) .IgnoreNullValues(true); - _update.Adapt(_source, configDate); - - - var _sourseEmailUpdate = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); - + var _sourceEmailUpdate = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); var _updateEmail = new UpdateUser { Email = "245@gmail.com", }; - var config = new TypeAdapterConfig(); config.ForType() - .IgnoreNullValues(true); - - var _resultEmail = _updateEmail.Adapt(_sourseEmailUpdate, config); - + var _resultEmail = _updateEmail.Adapt(_sourceEmailUpdate, config); _source.Id.ShouldBe("123"); _source.Created.ShouldBe(new DateTime(2023, 9, 24)); _source.Modified.ShouldBe(new DateTime(2025, 9, 24)); _source.Email.ShouldBe("123@gmail.com"); - - - _sourseEmailUpdate.Id.ShouldBe("123"); - _sourseEmailUpdate.Created.ShouldBe(new DateTime(2023, 9, 24)); - _sourseEmailUpdate.Modified.ShouldBe(null); - _sourseEmailUpdate.Email.ShouldBe("245@gmail.com"); - - + _sourceEmailUpdate.Id.ShouldBe("123"); + _sourceEmailUpdate.Created.ShouldBe(new DateTime(2023, 9, 24)); + _sourceEmailUpdate.Modified.ShouldBe(null); + _sourceEmailUpdate.Email.ShouldBe("245@gmail.com"); } - /// /// https://github.com/MapsterMapper/Mapster/issues/524 /// @@ -259,25 +179,20 @@ public void UpdateNullable() public void TSousreIsObjectUpdateUseDynamicCast() { var source = new TestClassPublicCtr { X = 123 }; - var _result = SomemapWithDynamic(source); - + _result.X.ShouldBe(123); - } TestClassPublicCtr SomemapWithDynamic(object source) { var dest = new TestClassPublicCtr { X = 321 }; var dest1 = source.Adapt(dest,source.GetType(),dest.GetType()); - + return dest; } - - - #region NowNotWorking [TestMethod] @@ -285,20 +200,12 @@ TestClassPublicCtr SomemapWithDynamic(object source) public void DetectFakeRecord() { var _source = new TestClassPublicCtr(200); - var _destination = new FakeRecord { X = 300 }; - var _result = _source.Adapt(_destination); - - _destination.X.ShouldBe(200); - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); - - } - /// /// https://github.com/MapsterMapper/Mapster/issues/430 /// @@ -307,20 +214,14 @@ public void DetectFakeRecord() public void CollectionUpdate() { List sources = new() - { + { new(541), new(234) - }; - - var destination = new List(); - - var _result = sources.Adapt(destination); destination.Count.ShouldBe(_result.Count); - } /// @@ -333,11 +234,9 @@ public void CollectionUpdate() public void TSousreIsObjectUpdate() { var source = new TestClassPublicCtr { X = 123 }; - var _result = Somemap(source); _result.X.ShouldBe(123); - } TestClassPublicCtr Somemap(object source) @@ -350,7 +249,6 @@ TestClassPublicCtr Somemap(object source) #endregion NowNotWorking - } @@ -358,20 +256,12 @@ TestClassPublicCtr Somemap(object source) public class FakeRecord { - protected FakeRecord(FakeRecord fake) - { - - } - - public FakeRecord() - { - - } + protected FakeRecord(FakeRecord fake) { } + public FakeRecord() { } public int X { get; set; } } - class UserAccount { public UserAccount(string id, string email, DateTime created) @@ -379,9 +269,7 @@ public UserAccount(string id, string email, DateTime created) Id = id; Email = email; Created = created; - } - protected UserAccount() { } public string Id { get; set; } @@ -394,13 +282,10 @@ class UpdateUser { public string? Id { get; set; } public string? Email { get; set; } - public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - } - class DestinationWithStruct { public TestStruct TestStruct { get; set; } @@ -419,20 +304,15 @@ class SourceWithStruct struct TestStruct { public string Property { get; } - public TestStruct(string property) : this() { Property = property; } } - class TestClassPublicCtr { - public TestClassPublicCtr() - { - - } + public TestClassPublicCtr() { } public TestClassPublicCtr(int x) { @@ -442,13 +322,9 @@ public TestClassPublicCtr(int x) public int X { get; set; } } - class TestClassProtectedCtr { - protected TestClassProtectedCtr() - { - - } + protected TestClassProtectedCtr() { } public TestClassProtectedCtr(int x) { @@ -458,13 +334,9 @@ public TestClassProtectedCtr(int x) public int X { get; set; } } - class TestClassProtectedCtrPrivateProperty { - protected TestClassProtectedCtrPrivateProperty() - { - - } + protected TestClassProtectedCtrPrivateProperty() { } public TestClassProtectedCtrPrivateProperty(int x, string name) { @@ -477,11 +349,8 @@ public TestClassProtectedCtrPrivateProperty(int x, string name) public string Name { get; private set; } } - record TestRecord() { - - public int X { set; get; } } From 74452002a79286d13da558fee62b26e141e75785 Mon Sep 17 00:00:00 2001 From: Haritha Rathnayake Date: Sun, 8 Oct 2023 16:13:39 +0530 Subject: [PATCH 203/363] Implemented TypeAdapter methods to validate destination props with source --- src/Mapster/TypeAdapter.cs | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 2e1db2f1..ad2d0c1b 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; +using Mapster.Models; namespace Mapster { @@ -170,6 +173,60 @@ public static TDestination Adapt(this TSource source, TDe return del.DynamicInvoke(source, destination); } } + + /// + /// Validate properties and Adapt the source object to the destination type. + /// + /// Source type. + /// Destination type. + /// Source object to adapt. + /// Adapted destination type. + public static TDestination ValidateAndAdapt(this TSource source) + { + var entityType = typeof(TSource); + var selectorType = typeof(TDestination); + + var entityProperties = new HashSet(entityType.GetProperties().Select(p => p.Name)); + var selectorProperties = new HashSet(selectorType.GetProperties().Select(p=> p.Name)); + + foreach (var selectorProperty in selectorProperties) + { + if (entityProperties.Contains(selectorProperty)) continue; + throw new Exception($"Property {selectorProperty} does not exist in {entityType.Name} and is not configured in Mapster"); + } + return source.Adapt(); + } + + /// + /// Validate properties and Adapt the source object to the destination type. + /// + /// Source type. + /// Destination type. + /// Source object to adapt. + /// Configuration + /// Adapted destination type. + public static TDestination ValidateAndAdapt(this TSource source, TypeAdapterConfig config) + { + var entityType = typeof(TSource); + var selectorType = typeof(TDestination); + + var entityProperties = new HashSet(entityType.GetProperties().Select(p => p.Name)); + var selectorProperties = new HashSet(selectorType.GetProperties().Select(p=> p.Name)); + + // Get the rule map for the current types + var ruleMap = config.RuleMap; + var typeTuple = new TypeTuple(entityType, selectorType); + ruleMap.TryGetValue(typeTuple, out var rule); + + foreach (var selectorProperty in selectorProperties) + { + if (entityProperties.Contains(selectorProperty)) continue; + // Check whether the adapter config has a config for the property + if (rule != null && rule.Settings.Resolvers.Any(r => r.DestinationMemberName.Equals(selectorProperty))) continue; + throw new Exception($"Property {selectorProperty} does not exist in {entityType.Name} and is not configured in Mapster"); + } + return source.Adapt(config); + } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S1104:Fields should not have public accessibility", Justification = "")] From 794f4fde57bc05f7a535bcc0e25f618fe8a24fe3 Mon Sep 17 00:00:00 2001 From: Haritha Rathnayake Date: Sun, 8 Oct 2023 19:03:42 +0530 Subject: [PATCH 204/363] Implemented testing for TypeAdapter.ValidateAndAdapt --- .../WhenRequiresPropsValidation.cs | 48 +++++++++++++++++ ...equiresPropsValidationWithAdapterConfig.cs | 53 +++++++++++++++++++ src/Mapster/TypeAdapter.cs | 20 +++---- 3 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 src/Mapster.Tests/WhenRequiresPropsValidation.cs create mode 100644 src/Mapster.Tests/WhenRequiresPropsValidationWithAdapterConfig.cs diff --git a/src/Mapster.Tests/WhenRequiresPropsValidation.cs b/src/Mapster.Tests/WhenRequiresPropsValidation.cs new file mode 100644 index 00000000..0652df6d --- /dev/null +++ b/src/Mapster.Tests/WhenRequiresPropsValidation.cs @@ -0,0 +1,48 @@ +using System; +using Mapster.Tests.Classes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenRequiresPropsValidation + { + [TestInitialize] + public void Setup() + { + TypeAdapterConfig.GlobalSettings.Clear(); + } + + [TestCleanup] + public void TestCleanup() + { + TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.Exact); + } + + [TestMethod] + public void DestinationProps_Exist_In_Source() + { + var product = new Product {Id = Guid.NewGuid(), Title = "ProductA", CreatedUser = new User {Name = "UserA"}}; + + var dto = product.ValidateAndAdapt(); + + dto.ShouldNotBeNull(); + dto.Id.ShouldBe(product.Id); + } + + [TestMethod] + public void DestinationProps_Not_Exist_In_Source() + { + var product = new Product {Id = Guid.NewGuid(), Title = "ProductA", CreatedUser = new User {Name = "UserA"}}; + + ProductDTO productDtoRef; + var notExistingPropName = nameof(productDtoRef.CreatedUserName); + + var ex = Should.Throw(() => product.ValidateAndAdapt()); + + ex.Message.ShouldContain(notExistingPropName); + ex.Message.ShouldContain(nameof(Product)); + } + } +} diff --git a/src/Mapster.Tests/WhenRequiresPropsValidationWithAdapterConfig.cs b/src/Mapster.Tests/WhenRequiresPropsValidationWithAdapterConfig.cs new file mode 100644 index 00000000..eb845416 --- /dev/null +++ b/src/Mapster.Tests/WhenRequiresPropsValidationWithAdapterConfig.cs @@ -0,0 +1,53 @@ +using System; +using Mapster.Tests.Classes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenRequiresPropsValidationWithAdapterConfig + { + [TestInitialize] + public void Setup() + { + TypeAdapterConfig.GlobalSettings.Clear(); + } + + [TestCleanup] + public void TestCleanup() + { + TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.Exact); + } + + [TestMethod] + public void DestinationProps_Not_Exist_In_Source_But_Configured() + { + var product = new Product {Id = Guid.NewGuid(), Title = "ProductA", CreatedUser = new User {Name = "UserA"}}; + + var adapterSettings = TypeAdapterConfig.NewConfig() + .Map(dest => dest.CreatedUserName, src => $"{src.CreatedUser.Name} {src.CreatedUser.Surname}"); + + var dto = product.ValidateAndAdapt(adapterSettings.Config); + + dto.ShouldNotBeNull(); + dto.CreatedUserName.ShouldBe($"{product.CreatedUser.Name} {product.CreatedUser.Surname}"); + } + + [TestMethod] + public void DestinationProps_Not_Exist_In_Source_And_MisConfigured() + { + var product = new Product {Id = Guid.NewGuid(), Title = "ProductA", CreatedUser = new User {Name = "UserA"}}; + + var adapterSettings = TypeAdapterConfig.NewConfig(); + + ProductDTO productDtoRef; + var notExistingPropName = nameof(productDtoRef.CreatedUserName); + + var ex = Should.Throw(() => product.ValidateAndAdapt(adapterSettings.Config)); + + ex.Message.ShouldContain(notExistingPropName); + ex.Message.ShouldContain(nameof(Product)); + } + } +} diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index ad2d0c1b..8c284b4b 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -183,22 +183,22 @@ public static TDestination Adapt(this TSource source, TDe /// Adapted destination type. public static TDestination ValidateAndAdapt(this TSource source) { - var entityType = typeof(TSource); + var sourceType = typeof(TSource); var selectorType = typeof(TDestination); - var entityProperties = new HashSet(entityType.GetProperties().Select(p => p.Name)); + var sourceProperties = new HashSet(sourceType.GetProperties().Select(p => p.Name)); var selectorProperties = new HashSet(selectorType.GetProperties().Select(p=> p.Name)); foreach (var selectorProperty in selectorProperties) { - if (entityProperties.Contains(selectorProperty)) continue; - throw new Exception($"Property {selectorProperty} does not exist in {entityType.Name} and is not configured in Mapster"); + if (sourceProperties.Contains(selectorProperty)) continue; + throw new Exception($"Property {selectorProperty} does not exist in {sourceType.Name} and is not configured in Mapster"); } return source.Adapt(); } /// - /// Validate properties and Adapt the source object to the destination type. + /// Validate properties with configuration and Adapt the source object to the destination type. /// /// Source type. /// Destination type. @@ -207,23 +207,23 @@ public static TDestination ValidateAndAdapt(this TSource /// Adapted destination type. public static TDestination ValidateAndAdapt(this TSource source, TypeAdapterConfig config) { - var entityType = typeof(TSource); + var sourceType = typeof(TSource); var selectorType = typeof(TDestination); - var entityProperties = new HashSet(entityType.GetProperties().Select(p => p.Name)); + var sourceProperties = new HashSet(sourceType.GetProperties().Select(p => p.Name)); var selectorProperties = new HashSet(selectorType.GetProperties().Select(p=> p.Name)); // Get the rule map for the current types var ruleMap = config.RuleMap; - var typeTuple = new TypeTuple(entityType, selectorType); + var typeTuple = new TypeTuple(sourceType, selectorType); ruleMap.TryGetValue(typeTuple, out var rule); foreach (var selectorProperty in selectorProperties) { - if (entityProperties.Contains(selectorProperty)) continue; + if (sourceProperties.Contains(selectorProperty)) continue; // Check whether the adapter config has a config for the property if (rule != null && rule.Settings.Resolvers.Any(r => r.DestinationMemberName.Equals(selectorProperty))) continue; - throw new Exception($"Property {selectorProperty} does not exist in {entityType.Name} and is not configured in Mapster"); + throw new Exception($"Property {selectorProperty} does not exist in {sourceType.Name} and is not configured in Mapster"); } return source.Adapt(config); } From 636eca0479bf57fcb2906ffde8928f3a9eeeae9b Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 10 Oct 2023 14:54:19 +0500 Subject: [PATCH 205/363] test check Current Work From ImplicitOperator to Class --- .../WhenMappingRecordRegression.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 917e9683..cf123a44 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -192,6 +192,31 @@ TestClassPublicCtr SomemapWithDynamic(object source) return dest; } + /// + /// https://github.com/MapsterMapper/Mapster/issues/569 + /// + [TestMethod] + public void ImplicitOperatorCurrentWorkFromClass() + { + var guid = Guid.NewGuid(); + + var pocoWithGuid1 = new PocoWithGuid { Id = guid }; + var pocoWithId2 = new PocoWithId { Id = new Id(guid) }; + + var pocoWithId1 = pocoWithGuid1.Adapt(); + var pocoWithGuid2 = pocoWithId2.Adapt(); + + pocoWithId1.Id.ToString().Equals(guid.ToString()).ShouldBeTrue(); + pocoWithGuid2.Id.Equals(guid).ShouldBeTrue(); + + + var _result = pocoWithId1.Adapt(pocoWithGuid2); + + _result.Id.ToString().Equals(guid.ToString()).ShouldBeTrue(); // Guid value transmitted + object.ReferenceEquals(_result, pocoWithGuid2).ShouldBeTrue(); // Not created new instanse from class pocoWithGuid2 + _result.ShouldBeOfType(); + + } #region NowNotWorking @@ -254,6 +279,28 @@ TestClassPublicCtr Somemap(object source) #region TestClasses + class PocoWithGuid + { + public Guid Id { get; init; } + } + + class PocoWithId + { + public Id Id { get; init; } + } + + class Id + { + private readonly Guid _guid; + + public Id(Guid id) => _guid = id; + + public static implicit operator Id(Guid value) => new(value); + public static implicit operator Guid(Id value) => value._guid; + + public override string ToString() => _guid.ToString(); + } + public class FakeRecord { protected FakeRecord(FakeRecord fake) { } From 62ab7d6a399bc90a3392f2ddad6db447a434605c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 10 Oct 2023 15:28:51 +0500 Subject: [PATCH 206/363] add Sealed record Detection and Test --- .../WhenMappingRecordRegression.cs | 33 +++++++++++++++++++ src/Mapster/Utils/ReflectionUtils.cs | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index cf123a44..e872e1b9 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -68,6 +68,28 @@ public void AdaptClassToRecord() object.ReferenceEquals(_destination, _result).ShouldBeFalse(); } + [TestMethod] + public void AdaptToSealtedRecord() + { + var _sourceRecord = new TestRecord() { X = 2000 }; + var _destinationSealtedRecord = new TestSealedRecord() { X = 3000 }; + var _RecordResult = _sourceRecord.Adapt(_destinationSealtedRecord); + + _RecordResult.X.ShouldBe(2000); + object.ReferenceEquals(_destinationSealtedRecord, _RecordResult).ShouldBeFalse(); + } + + [TestMethod] + public void AdaptToSealtedPositionalRecord() + { + var _sourceRecord = new TestRecord() { X = 2000 }; + var _destinationSealtedPositionalRecord = new TestSealedRecordPositional(4000); + var _RecordResult = _sourceRecord.Adapt(_destinationSealtedPositionalRecord); + + _RecordResult.X.ShouldBe(2000); + object.ReferenceEquals(_destinationSealtedPositionalRecord, _RecordResult).ShouldBeFalse(); + } + [TestMethod] public void AdaptClassToClassPublicCtrIsNotInstanse() { @@ -408,5 +430,16 @@ record struct TestRecordStruct public int X { set; get; } } + /// + /// Different Checked Constructor Attribute From Spec + /// https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/proposals/csharp-9.0/records#copy-and-clone-members + /// + sealed record TestSealedRecord() + { + public int X { get; set; } + } + + sealed record TestSealedRecordPositional(int X); + #endregion TestClasses } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 28e30c12..a0e1d3ac 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -209,7 +209,7 @@ public static bool IsRecordType(this Type type) var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) - .Where(x => x.IsFamily == true) + .Where(x => x.IsFamily == true || (type.Attributes.HasFlag(TypeAttributes.Sealed) && x.IsPrivate == true)) // add target from Sealed record .Any(x => x.GetParameters() .Any(y => y.ParameterType == type)); From 531f0ed5939ef3b7682ef57d5e35e34710f95598 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 10 Oct 2023 16:44:02 +0500 Subject: [PATCH 207/363] add RecordType identity helper add targeting to RecordType Clone Method --- .../Utils/RecordTypeIdentityHelper.cs | 52 +++++++++++++++++++ .../WhenMappingRecordRegression.cs | 1 - src/Mapster/Utils/ReflectionUtils.cs | 12 +---- 3 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs diff --git a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs new file mode 100644 index 00000000..b473802b --- /dev/null +++ b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Mapster.Utils +{ + /// + /// CheckTools from Distinctive features of RecordType according to specification: + /// https://github.com/dotnet/docs/blob/main/docs/csharp/language-reference/builtin-types/record.md + /// + public static class RecordTypeIdentityHelper + { + private static bool IsRecordСonstructor(Type type) + { + var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); + + var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) + .Where(x => x.IsFamily == true || (type.IsSealed && x.IsPrivate == true)) // add target from Sealed record + .Any(x => x.GetParameters() + .Any(y => y.ParameterType == type)); + + + if (ctors.Count >= 2 && isRecordTypeCtor) + return true; + + + return false; + + } + + private static bool IsIncludedRecordCloneMethod(Type type) + { + if( type.GetMethod("$")?.MethodImplementationFlags.HasFlag(MethodImplAttributes.IL) == true) + return true; + + return false; + } + + public static bool IsRecordType(Type type) + { + if (IsRecordСonstructor(type) && IsIncludedRecordCloneMethod(type)) + return true; + + + return false; + } + + } +} diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index e872e1b9..e22f85b1 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -243,7 +243,6 @@ public void ImplicitOperatorCurrentWorkFromClass() #region NowNotWorking [TestMethod] - [Ignore] public void DetectFakeRecord() { var _source = new TestClassPublicCtr(200); diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index a0e1d3ac..fe44e790 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -205,20 +205,10 @@ public static bool IsRecordType(this Type type) props.Any(p => p.SetterModifier != AccessModifier.Public)) return true; - //constructor - var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); - - var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) - .Where(x => x.IsFamily == true || (type.Attributes.HasFlag(TypeAttributes.Sealed) && x.IsPrivate == true)) // add target from Sealed record - .Any(x => x.GetParameters() - .Any(y => y.ParameterType == type)); - - - if (ctors.Count >= 2 && isRecordTypeCtor) + if(RecordTypeIdentityHelper.IsRecordType(type)) return true; - return false; } From f678d12e3ed87b180af17abe8158ab6f7f4f0a25 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 15 Oct 2023 14:47:11 +0500 Subject: [PATCH 208/363] refactoring and del empty lines --- src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs | 13 ++++--------- src/Mapster.Tests/WhenMappingRecordRegression.cs | 7 ++----- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs index b473802b..4e5aa197 100644 --- a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs +++ b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace Mapster.Utils { @@ -17,18 +14,18 @@ private static bool IsRecordСonstructor(Type type) { var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); + if (ctors.Count < 2) + return false; + var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) .Where(x => x.IsFamily == true || (type.IsSealed && x.IsPrivate == true)) // add target from Sealed record .Any(x => x.GetParameters() .Any(y => y.ParameterType == type)); - - if (ctors.Count >= 2 && isRecordTypeCtor) + if (isRecordTypeCtor) return true; - return false; - } private static bool IsIncludedRecordCloneMethod(Type type) @@ -44,9 +41,7 @@ public static bool IsRecordType(Type type) if (IsRecordСonstructor(type) && IsIncludedRecordCloneMethod(type)) return true; - return false; } - } } diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index e22f85b1..f5ef6add 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -221,7 +221,6 @@ TestClassPublicCtr SomemapWithDynamic(object source) public void ImplicitOperatorCurrentWorkFromClass() { var guid = Guid.NewGuid(); - var pocoWithGuid1 = new PocoWithGuid { Id = guid }; var pocoWithId2 = new PocoWithId { Id = new Id(guid) }; @@ -231,7 +230,6 @@ public void ImplicitOperatorCurrentWorkFromClass() pocoWithId1.Id.ToString().Equals(guid.ToString()).ShouldBeTrue(); pocoWithGuid2.Id.Equals(guid).ShouldBeTrue(); - var _result = pocoWithId1.Adapt(pocoWithGuid2); _result.Id.ToString().Equals(guid.ToString()).ShouldBeTrue(); // Guid value transmitted @@ -240,8 +238,6 @@ public void ImplicitOperatorCurrentWorkFromClass() } - #region NowNotWorking - [TestMethod] public void DetectFakeRecord() { @@ -252,6 +248,8 @@ public void DetectFakeRecord() object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } + #region NowNotWorking + /// /// https://github.com/MapsterMapper/Mapster/issues/430 /// @@ -313,7 +311,6 @@ class PocoWithId class Id { private readonly Guid _guid; - public Id(Guid id) => _guid = id; public static implicit operator Id(Guid value) => new(value); From 9a2cf671b47bef2d1a2e1d46f5ba9092f63867f4 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 15 Oct 2023 16:57:17 +0500 Subject: [PATCH 209/363] Fix Issue #640 --- .../WhenMappingNullableEnumRegression.cs | 108 ++++++++++++++++++ src/Mapster/Adapters/BaseAdapter.cs | 36 +++--- src/Mapster/Utils/ExpressionEx.cs | 24 ++++ 3 files changed, 151 insertions(+), 17 deletions(-) create mode 100644 src/Mapster.Tests/WhenMappingNullableEnumRegression.cs diff --git a/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs b/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs new file mode 100644 index 00000000..c3a605e1 --- /dev/null +++ b/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs @@ -0,0 +1,108 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenMappingNullableEnumRegression + { + /// + /// https://github.com/MapsterMapper/Mapster/issues/640 + /// + [TestMethod] + public void NullEnumToNullClass() + { + TypeAdapterConfig + .NewConfig() + .MapWith(s => s == null ? null : new KeyValueData(s.ToString(), Enums.Manager)); + + MyClass myClass = new() { TypeEmployer = MyEnum.User }; + + var _result = myClass?.Adapt(); // Work + + _result.TypeEmployer.Key.ShouldBe(MyEnum.User.ToString()); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/640 + /// + [Ignore] // Will work after RecordType fix + [TestMethod] + public void UpdateNullEnumToClass() + { + TypeAdapterConfig + .NewConfig() + .MapWith(s => s == null ? null : new KeyValueData(s.ToString(), Enums.Manager)); + + + MyClass myClass = new() { TypeEmployer = MyEnum.User }; + + var mDest2 = new MyDestination() { TypeEmployer = new KeyValueData("Admin", null) }; + + var _MyDestination = myClass?.Adapt(); // Work + var _result = _MyDestination.Adapt(mDest2); + + _result.TypeEmployer.Key.ShouldBe(MyEnum.User.ToString()); + } + + + } + + #region TestClasses + + class MyDestination + { + public KeyValueData? TypeEmployer { get; set; } + } + + class MyClass + { + public MyEnum? TypeEmployer { get; set; } + } + + enum MyEnum + { + Anonymous = 0, + User = 2, + } + + class FakeResourceManager + { + + } + + class Enums + { + protected Enums(string data) {} + + public static FakeResourceManager Manager { get; set; } + + } + + record KeyValueData + { + private readonly string? keyHolder; + private string? description; + + public KeyValueData(string key, FakeResourceManager manager) + { + this.keyHolder = key?.ToString(); + Description = manager?.ToString(); + } + + public string Key + { + get => keyHolder!; + set { } + } + + public string? Description + { + get => description; + set => description ??= value; + } + } + + #endregion TestClasses +} diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index b7b6fc67..4450e3f0 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -143,7 +143,7 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de var blocks = new List(); var label = Expression.Label(arg.DestinationType); - //var drvdSource = source as TDerivedSource + //var drvdSource = _source as TDerivedSource //if (drvdSource != null) // return adapt(drvdSource); foreach (var tuple in arg.Settings.Includes) @@ -218,7 +218,7 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de else { //TDestination result; - //if (source == null) + //if (_source == null) // return default(TDestination); if (source.CanBeNull()) { @@ -237,15 +237,15 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de assignActions.Add(Expression.Assign(transformedSource, transform)); assignActions.Add(assign); - //before(source, result, destination); + //before(_source, result, destination); var beforeMappings = arg.Settings.BeforeMappingFactories.Select(it => InvokeMapping(it, source, result, destination, arg, true)).Reverse(); assignActions.AddRange(beforeMappings); - //result.prop = adapt(source.prop); + //result.prop = adapt(_source.prop); var mapping = CreateBlockExpression(transformedSource, result, arg); var settingActions = new List {mapping}; - //after(source, result, destination); + //after(_source, result, destination); var afterMappings = arg.Settings.AfterMappingFactories.Select(it => InvokeMapping(it, source, result, destination, arg, false)).Reverse(); settingActions.AddRange(afterMappings); @@ -254,13 +254,13 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de //using (var scope = new MapContextScope()) { // var references = scope.Context.Reference; - // var key = new ReferenceTuple(source, typeof(TDestination)); + // var key = new ReferenceTuple(_source, typeof(TDestination)); // if (references.TryGetValue(key, out var cache)) // return (TDestination)cache; // // var result = new TDestination(); - // references[source] = (object)result; - // result.prop = adapt(source.prop); + // references[_source] = (object)result; + // result.prop = adapt(_source.prop); // return result; //} @@ -348,7 +348,7 @@ private static Expression InvokeMapping( protected Expression? CreateInlineExpressionBody(Expression source, CompileArgument arg) { - //source == null ? default(TDestination) : adapt(source) + //_source == null ? default(TDestination) : adapt(_source) var exp = CreateInlineExpression(source, arg); if (exp == null) @@ -450,17 +450,19 @@ protected Expression CreateAdaptExpression(Expression source, Type destinationTy } internal Expression CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg, MemberMapping? mapping, Expression? destination = null) { - if (source.Type == destinationType && arg.MapType == MapType.Projection) - return source; + var _source = source.NullableEnumExtractor(); // Extraction Nullable Enum - //adapt(source); + if (_source.Type == destinationType && arg.MapType == MapType.Projection) + return _source; + + //adapt(_source); var notUsingDestinationValue = mapping is not { UseDestinationValue: true }; - var exp = source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true && notUsingDestinationValue && - !arg.Context.Config.HasRuleFor(source.Type, destinationType) - ? source - : CreateAdaptExpressionCore(source, destinationType, arg, mapping, destination); + var exp = _source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true && notUsingDestinationValue && + !arg.Context.Config.HasRuleFor(_source.Type, destinationType) + ? _source + : CreateAdaptExpressionCore(_source, destinationType, arg, mapping, destination); - //transform(adapt(source)); + //transform(adapt(_source)); if (notUsingDestinationValue) { var transform = arg.Settings.DestinationTransforms.Find(it => it.Condition(exp.Type)); diff --git a/src/Mapster/Utils/ExpressionEx.cs b/src/Mapster/Utils/ExpressionEx.cs index 031b29d7..91fb6c07 100644 --- a/src/Mapster/Utils/ExpressionEx.cs +++ b/src/Mapster/Utils/ExpressionEx.cs @@ -301,6 +301,30 @@ public static Expression NotNullReturn(this Expression exp, Expression value) value); } + /// + /// Unpack Enum Nullable TSource value + /// + /// + /// + public static Expression NullableEnumExtractor(this Expression param) + { + var _SourceType = param.Type; + + if (_SourceType.IsNullable()) + { + var _genericType = param.Type.GetGenericArguments()[0]!; + + if (_genericType.IsEnum) + { + var ExtractionExpression = Expression.Convert(param, _genericType); + return ExtractionExpression; + } + + return param; + } + + return param; + } public static Expression ApplyNullPropagation(this Expression getter) { var current = getter; From 60878d834843ee75a6c720225da407a4b332c417 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 15 Oct 2023 17:05:41 +0500 Subject: [PATCH 210/363] del empty lines --- src/Mapster.Tests/WhenMappingNullableEnumRegression.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs b/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs index c3a605e1..4f105219 100644 --- a/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs +++ b/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs @@ -34,8 +34,7 @@ public void UpdateNullEnumToClass() TypeAdapterConfig .NewConfig() .MapWith(s => s == null ? null : new KeyValueData(s.ToString(), Enums.Manager)); - - + MyClass myClass = new() { TypeEmployer = MyEnum.User }; var mDest2 = new MyDestination() { TypeEmployer = new KeyValueData("Admin", null) }; @@ -45,8 +44,6 @@ public void UpdateNullEnumToClass() _result.TypeEmployer.Key.ShouldBe(MyEnum.User.ToString()); } - - } #region TestClasses @@ -75,9 +72,7 @@ class FakeResourceManager class Enums { protected Enums(string data) {} - public static FakeResourceManager Manager { get; set; } - } record KeyValueData From bb765229616aaae771d17a1ed791f59155ac46b9 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 15 Oct 2023 17:25:48 +0500 Subject: [PATCH 211/363] Full work only cast Emun to object --- src/Mapster.Tests/WhenMappingNullableEnumRegression.cs | 7 +++++++ src/Mapster/Utils/ExpressionEx.cs | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs b/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs index 4f105219..c6b4281e 100644 --- a/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs +++ b/src/Mapster.Tests/WhenMappingNullableEnumRegression.cs @@ -19,9 +19,16 @@ public void NullEnumToNullClass() MyClass myClass = new() { TypeEmployer = MyEnum.User }; + MyClass myClassNull = new() { TypeEmployer = null}; + + var _result = myClass?.Adapt(); // Work + var _resultNull = myClassNull.Adapt(); // Null Not Error When (object)s if (MyEnum)s - NullReferenceException + _result.TypeEmployer.Key.ShouldBe(MyEnum.User.ToString()); + + _resultNull.TypeEmployer.ShouldBeNull(); } /// diff --git a/src/Mapster/Utils/ExpressionEx.cs b/src/Mapster/Utils/ExpressionEx.cs index 91fb6c07..a1ab8550 100644 --- a/src/Mapster/Utils/ExpressionEx.cs +++ b/src/Mapster/Utils/ExpressionEx.cs @@ -312,11 +312,11 @@ public static Expression NullableEnumExtractor(this Expression param) if (_SourceType.IsNullable()) { - var _genericType = param.Type.GetGenericArguments()[0]!; + var _genericType = param.Type.GetGenericArguments()[0]; if (_genericType.IsEnum) { - var ExtractionExpression = Expression.Convert(param, _genericType); + var ExtractionExpression = Expression.Convert(param, typeof(object)); return ExtractionExpression; } From fcd8139301b1d50a5689beb3d146cf21ceee21cf Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 17 Oct 2023 06:46:43 +0500 Subject: [PATCH 212/363] refactoring test --- src/Mapster.Tests/WhenMappingRecordRegression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index f5ef6add..d0d55c56 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -41,7 +41,7 @@ public void AdaptRecordStructToRecordStruct() var _structResult = _sourceStruct.Adapt(_destinationStruct); _structResult.X.ShouldBe(1000); - object.ReferenceEquals(_destinationStruct, _structResult).ShouldBeFalse(); + _destinationStruct.X.Equals(_structResult.X).ShouldBeFalse(); } [TestMethod] From df2c0ece2efbebe44cefb58bd0906b607158621b Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 17 Oct 2023 06:53:05 +0500 Subject: [PATCH 213/363] Refactoring RecordTypeIdentityHelper --- src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs index 4e5aa197..d9c113f9 100644 --- a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs +++ b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs @@ -17,10 +17,11 @@ private static bool IsRecordСonstructor(Type type) if (ctors.Count < 2) return false; - var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) + var isRecordTypeCtor = + ctors .Where(x => x.IsFamily == true || (type.IsSealed && x.IsPrivate == true)) // add target from Sealed record .Any(x => x.GetParameters() - .Any(y => y.ParameterType == type)); + .Any(y => y.ParameterType == type)); if (isRecordTypeCtor) return true; From 4f016ded56c285b9f8885c26629e6fd81bff12a6 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 18 Oct 2023 21:45:04 +0500 Subject: [PATCH 214/363] Object To TDestination fixed --- .../WhenMappingObjectRegression.cs | 156 ++++++++++++++++++ .../WhenMappingRecordRegression.cs | 45 +---- src/Mapster/TypeAdapter.cs | 27 +++ 3 files changed, 184 insertions(+), 44 deletions(-) create mode 100644 src/Mapster.Tests/WhenMappingObjectRegression.cs diff --git a/src/Mapster.Tests/WhenMappingObjectRegression.cs b/src/Mapster.Tests/WhenMappingObjectRegression.cs new file mode 100644 index 00000000..d6cf7776 --- /dev/null +++ b/src/Mapster.Tests/WhenMappingObjectRegression.cs @@ -0,0 +1,156 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenMappingObjectRegression + { + /// + /// https://github.com/MapsterMapper/Mapster/issues/524 + /// + [TestMethod] + public void TSourceIsObjectUpdate() + { + var source = new Source524 { X1 = 123 }; + var _result = Somemap(source); + + _result.X1.ShouldBe(123); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/524 + /// + [TestMethod] + public void TSourceIsObjectUpdateUseDynamicCast() + { + var source = new Source524 { X1 = 123 }; + var _result = SomemapWithDynamic(source); + + _result.X1.ShouldBe(123); + } + + [TestMethod] + public void UpdateManyDest() + { + var source = new Source524 { X1 = 123 }; + var _result = SomemapManyDest(source); + + _result.X1.ShouldBe(123); + _result.X2.ShouldBe(127); + } + + [TestMethod] + public void UpdateToRealObject() + { + var source = new Source524 { X1 = 123 }; + var RealObject = new Object(); + + var _result = source.Adapt(RealObject); + + _result.ShouldBeOfType(); + ((Source524)_result).X1.ShouldBe(source.X1); + + } + + [TestMethod] + public void RealObjectCastToDestination() /// Warning potential Infinity Loop in ObjectAdapter!!! + { + var source = new Source524 { X1 = 123 }; + var RealObject = new Object(); + + var _result = RealObject.Adapt(source); + + _result.ShouldBeOfType(); + ((Source524)_result).X1.ShouldBe(source.X1); + } + + [TestMethod] + public void UpdateObjectInsaider() + { + var _source = new InsaderObject() { X1 = 1 }; + var _Destination = new InsaderObject() { X1 = 2 }; + + var _result = _source.Adapt(_Destination); + + _result.X1.ShouldBe(_source.X1); + } + + [TestMethod] + public void UpdateObjectInsaiderToObject() + { + var _source = new InsaderObject() { X1 = 1 }; + var _Destination = new InsaderObject() { X1 = new Object() }; + + var _result = _source.Adapt(_Destination); + + _result.X1.ShouldBe(_source.X1); + } + + [TestMethod] + public void UpdateObjectInsaiderWhenObjectinTSource() + { + var _source = new InsaderObject() { X1 = new Object() }; + var _Destination = new InsaderObject() { X1 = 3 }; + + var _result = _source.Adapt(_Destination); + + _result.X1.ShouldBe(_source.X1); + } + + + #region TestFunctions + + Dest524 Somemap(object source) + { + var dest = new Dest524 { X1 = 321 }; + var dest1 = source.Adapt(dest); + + return dest; + } + + ManyDest524 SomemapManyDest(object source) + { + var dest = new ManyDest524 { X1 = 321, X2 = 127 }; + var dest1 = source.Adapt(dest); + + return dest; + } + + Dest524 SomemapWithDynamic(object source) + { + var dest = new Dest524 { X1 = 321 }; + var dest1 = source.Adapt(dest, source.GetType(), dest.GetType()); + + return dest; + } + + #endregion TestFunctions + + #region TestClasses + class Source524 + { + public int X1 { get; set; } + } + class Dest524 + { + public int X1 { get; set; } + } + + class ManyDest524 + { + public int X1 { get; set;} + + public int X2 { get; set;} + } + + class InsaderObject + { + public Object X1 { get; set;} + } + + + #endregion TestClasses + } +} diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index f5ef6add..37bde9c4 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -41,7 +41,7 @@ public void AdaptRecordStructToRecordStruct() var _structResult = _sourceStruct.Adapt(_destinationStruct); _structResult.X.ShouldBe(1000); - object.ReferenceEquals(_destinationStruct, _structResult).ShouldBeFalse(); + _structResult.X.ShouldNotBe(_destinationStruct.X); } [TestMethod] @@ -194,26 +194,6 @@ public void UpdateNullable() } - /// - /// https://github.com/MapsterMapper/Mapster/issues/524 - /// - [TestMethod] - public void TSousreIsObjectUpdateUseDynamicCast() - { - var source = new TestClassPublicCtr { X = 123 }; - var _result = SomemapWithDynamic(source); - - _result.X.ShouldBe(123); - } - - TestClassPublicCtr SomemapWithDynamic(object source) - { - var dest = new TestClassPublicCtr { X = 321 }; - var dest1 = source.Adapt(dest,source.GetType(),dest.GetType()); - - return dest; - } - /// /// https://github.com/MapsterMapper/Mapster/issues/569 /// @@ -268,29 +248,6 @@ public void CollectionUpdate() destination.Count.ShouldBe(_result.Count); } - /// - /// https://github.com/MapsterMapper/Mapster/issues/524 - /// Not work. Already has a special overload: - /// .Adapt(this object source, object destination, Type sourceType, Type destinationType) - /// - [Ignore] - [TestMethod] - public void TSousreIsObjectUpdate() - { - var source = new TestClassPublicCtr { X = 123 }; - var _result = Somemap(source); - - _result.X.ShouldBe(123); - } - - TestClassPublicCtr Somemap(object source) - { - var dest = new TestClassPublicCtr { X = 321 }; - var dest1 = source.Adapt(dest); // typeof(TSource) always return Type as Object. Need use dynamic or Cast to Runtime Type before Adapt - - return dest; - } - #endregion NowNotWorking } diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 2e1db2f1..0b92359b 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -93,10 +93,37 @@ public static TDestination Adapt(this TSource source, TDe /// Adapted destination type. public static TDestination Adapt(this TSource source, TDestination destination, TypeAdapterConfig config) { + var sourceType = source.GetType(); + var destinationType = destination.GetType(); + + if (sourceType == typeof(object)) // Infinity loop in ObjectAdapter if Runtime Type of source is Object + return destination; + + if (typeof(TSource) == typeof(object) || typeof(TDestination) == typeof(object)) + return UpdateFuncFromPackedinObject(source, destination, config, sourceType, destinationType); + var fn = config.GetMapToTargetFunction(); return fn(source, destination); } + private static TDestination UpdateFuncFromPackedinObject(TSource source, TDestination destination, TypeAdapterConfig config, Type sourceType, Type destinationType) + { + dynamic del = config.GetMapToTargetFunction(sourceType, destinationType); + + + if (sourceType.GetTypeInfo().IsVisible && destinationType.GetTypeInfo().IsVisible) + { + dynamic objfn = del; + return objfn((dynamic)source, (dynamic)destination); + } + else + { + //NOTE: if type is non-public, we cannot use dynamic + //DynamicInvoke is slow, but works with non-public + return (TDestination)del.DynamicInvoke(source, destination); + } + } + /// /// Adapt the source object to the destination type. /// From d6d93392a406397aba58af2658c9d5e4f6b67ae6 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 22 Oct 2023 08:20:28 +0500 Subject: [PATCH 215/363] del object update test --- .../WhenMappingRecordRegression.cs | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index d0d55c56..51f88e8a 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -268,29 +268,6 @@ public void CollectionUpdate() destination.Count.ShouldBe(_result.Count); } - /// - /// https://github.com/MapsterMapper/Mapster/issues/524 - /// Not work. Already has a special overload: - /// .Adapt(this object source, object destination, Type sourceType, Type destinationType) - /// - [Ignore] - [TestMethod] - public void TSousreIsObjectUpdate() - { - var source = new TestClassPublicCtr { X = 123 }; - var _result = Somemap(source); - - _result.X.ShouldBe(123); - } - - TestClassPublicCtr Somemap(object source) - { - var dest = new TestClassPublicCtr { X = 321 }; - var dest1 = source.Adapt(dest); // typeof(TSource) always return Type as Object. Need use dynamic or Cast to Runtime Type before Adapt - - return dest; - } - #endregion NowNotWorking } From 18519257c620823cc46a397c32cb987340c9dc6e Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 22 Oct 2023 08:28:17 +0500 Subject: [PATCH 216/363] add Record inline creation function --- src/Mapster/Adapters/RecordTypeAdapter.cs | 60 +++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index a0034829..4d065e28 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -1,4 +1,6 @@ -using System.Linq.Expressions; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; using System.Reflection; using Mapster.Utils; @@ -26,10 +28,12 @@ protected override Expression CreateInstantiationExpression(Expression source, E : arg.DestinationType; if (destType == null) return base.CreateInstantiationExpression(source, destination, arg); - var ctor = destType.GetConstructors()[0]; + var ctor = destType.GetConstructors() + .OrderByDescending(it => it.GetParameters().Length).ToArray().FirstOrDefault(); var classModel = GetConstructorModel(ctor, false); var classConverter = CreateClassConverter(source, classModel, arg); - return CreateInstantiationExpression(source, classConverter, arg); + var installExpr = CreateInstantiationExpression(source, classConverter, arg); + return RecordInlineExpression(source, arg, installExpr); } protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) @@ -41,6 +45,56 @@ protected override Expression CreateInlineExpression(Expression source, CompileA { return base.CreateInstantiationExpression(source, arg); } + + private Expression? RecordInlineExpression(Expression source, CompileArgument arg, Expression installExpr) + { + //new TDestination { + // Prop1 = convert(src.Prop1), + // Prop2 = convert(src.Prop2), + //} + + var exp = installExpr; + var memberInit = exp as MemberInitExpression; + var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; + var contructorMembers = newInstance.Arguments.OfType().Select(me => me.Member).ToArray(); + var classModel = GetSetterModel(arg); + var classConverter = CreateClassConverter(source, classModel, arg); + var members = classConverter.Members; + + var lines = new List(); + if (memberInit != null) + lines.AddRange(memberInit.Bindings); + foreach (var member in members) + { + if (member.UseDestinationValue) + return null; + + if (!arg.Settings.Resolvers.Any(r => r.DestinationMemberName == member.DestinationMember.Name) + && member.Getter is MemberExpression memberExp && contructorMembers.Contains(memberExp.Member)) + continue; + + if (member.DestinationMember.SetterModifier == AccessModifier.None) + continue; + + var value = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); + + //special null property check for projection + //if we don't set null to property, EF will create empty object + //except collection type & complex type which cannot be null + if (arg.MapType == MapType.Projection + && member.Getter.Type != member.DestinationMember.Type + && !member.Getter.Type.IsCollection() + && !member.DestinationMember.Type.IsCollection() + && member.Getter.Type.GetTypeInfo().GetCustomAttributesData().All(attr => attr.GetAttributeType().Name != "ComplexTypeAttribute")) + { + value = member.Getter.NotNullReturn(value); + } + var bind = Expression.Bind((MemberInfo)member.DestinationMember.Info!, value); + lines.Add(bind); + } + + return Expression.MemberInit(newInstance, lines); + } } } From dc82e5a9c8aedeb6709acee8e9d8330b27e21fb6 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 22 Oct 2023 09:37:55 +0500 Subject: [PATCH 217/363] add Test from Inline adapt Function --- .../WhenMappingRecordRegression.cs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 51f88e8a..83ba34b1 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -247,6 +247,56 @@ public void DetectFakeRecord() _destination.X.ShouldBe(200); object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } + + [TestMethod] + public void OnlyInlineRecordWorked() + { + var _sourcePoco = new InlinePoco501() { MyInt = 1 , MyString = "Hello" }; + var _sourceOnlyInitRecord = new OnlyInitRecord501 { MyInt = 2, MyString = "Hello World" }; + + var _resultOnlyinitRecord = _sourcePoco.Adapt(); + var _updateResult = _sourceOnlyInitRecord.Adapt(_resultOnlyinitRecord); + + _resultOnlyinitRecord.MyInt.ShouldBe(1); + _resultOnlyinitRecord.MyString.ShouldBe("Hello"); + _updateResult.MyInt.ShouldBe(2); + _updateResult.MyString.ShouldBe("Hello World"); + } + + [TestMethod] + public void MultyCtorRecordWorked() + { + var _sourcePoco = new InlinePoco501() { MyInt = 1, MyString = "Hello" }; + var _sourceMultyCtorRecord = new MultiCtorRecord (2, "Hello World"); + + var _resultMultyCtorRecord = _sourcePoco.Adapt(); + var _updateResult = _sourceMultyCtorRecord.Adapt(_resultMultyCtorRecord); + + _resultMultyCtorRecord.MyInt.ShouldBe(1); + _resultMultyCtorRecord.MyString.ShouldBe("Hello"); + _updateResult.MyInt.ShouldBe(2); + _updateResult.MyString.ShouldBe("Hello World"); + } + + [TestMethod] + public void MultiCtorAndInlineRecordWorked() + { + var _sourcePoco = new MultiCtorAndInlinePoco() { MyInt = 1, MyString = "Hello", MyEmail = "123@gmail.com", InitData="Test"}; + var _sourceMultiCtorAndInline = new MultiCtorAndInlineRecord(2, "Hello World") { InitData = "Worked", MyEmail = "243@gmail.com" }; + + var _resultMultiCtorAndInline = _sourcePoco.Adapt(); + var _updateResult = _sourceMultiCtorAndInline.Adapt(_resultMultiCtorAndInline); + + _resultMultiCtorAndInline.MyInt.ShouldBe(1); + _resultMultiCtorAndInline.MyString.ShouldBe("Hello"); + _resultMultiCtorAndInline.MyEmail.ShouldBe("123@gmail.com"); + _resultMultiCtorAndInline.InitData.ShouldBe("Test"); + _updateResult.MyInt.ShouldBe(2); + _updateResult.MyString.ShouldBe("Hello World"); + _updateResult.MyEmail.ShouldBe("243@gmail.com"); + _updateResult.InitData.ShouldBe("Worked"); + } + #region NowNotWorking @@ -275,6 +325,63 @@ public void CollectionUpdate() #region TestClasses + class MultiCtorAndInlinePoco + { + public int MyInt { get; set; } + public string MyString { get; set; } + public string MyEmail { get; set; } + public string InitData { get; set; } + } + + record MultiCtorAndInlineRecord + { + public MultiCtorAndInlineRecord(int myInt) + { + MyInt = myInt; + } + + public MultiCtorAndInlineRecord(int myInt, string myString) : this(myInt) + { + MyString = myString; + } + + + public int MyInt { get; private set; } + public string MyString { get; private set; } + public string MyEmail { get; set; } + public string InitData { get; init; } + } + + record MultiCtorRecord + { + public MultiCtorRecord(int myInt) + { + MyInt = myInt; + } + + public MultiCtorRecord(int myInt, string myString) : this(myInt) + { + MyString = myString; + } + + public int MyInt { get; private set; } + public string MyString { get; private set; } + } + + + class InlinePoco501 + { + public int MyInt { get; set; } + public string MyString { get; set; } + } + + + record OnlyInitRecord501 + { + public int MyInt { get; init; } + public string MyString { get; init; } + } + class PocoWithGuid { public Guid Id { get; init; } From e6db0c459a554bd6d6ebc650b5da442ba9e1bd89 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 22 Oct 2023 10:15:32 +0500 Subject: [PATCH 218/363] del not Record test --- .../WhenMappingRecordRegression.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 83ba34b1..2eb50f70 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -194,26 +194,6 @@ public void UpdateNullable() } - /// - /// https://github.com/MapsterMapper/Mapster/issues/524 - /// - [TestMethod] - public void TSousreIsObjectUpdateUseDynamicCast() - { - var source = new TestClassPublicCtr { X = 123 }; - var _result = SomemapWithDynamic(source); - - _result.X.ShouldBe(123); - } - - TestClassPublicCtr SomemapWithDynamic(object source) - { - var dest = new TestClassPublicCtr { X = 321 }; - var dest1 = source.Adapt(dest,source.GetType(),dest.GetType()); - - return dest; - } - /// /// https://github.com/MapsterMapper/Mapster/issues/569 /// From 16b0b0ffb0527ace046fb1052b9a2ad4101ccd9b Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 22 Oct 2023 10:50:47 +0500 Subject: [PATCH 219/363] Add attribute --- .../Attributes/AdaptAsAttribute.cs | 18 ++++++++++++++++++ src/Mapster.Core/Enums/AdaptDirectives.cs | 8 ++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/Mapster.Core/Attributes/AdaptAsAttribute.cs create mode 100644 src/Mapster.Core/Enums/AdaptDirectives.cs diff --git a/src/Mapster.Core/Attributes/AdaptAsAttribute.cs b/src/Mapster.Core/Attributes/AdaptAsAttribute.cs new file mode 100644 index 00000000..8f439bcb --- /dev/null +++ b/src/Mapster.Core/Attributes/AdaptAsAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace Mapster.Attributes +{ + [AttributeUsage(AttributeTargets.Class + | AttributeTargets.Struct + | AttributeTargets.Interface + | AttributeTargets.Property + | AttributeTargets.Field, AllowMultiple = true)] + public class AdaptAsAttribute : Attribute + { + public AdaptDirectives AdaptDirective { get; } + public AdaptAsAttribute(AdaptDirectives directive) + { + this.AdaptDirective = directive; + } + } +} diff --git a/src/Mapster.Core/Enums/AdaptDirectives.cs b/src/Mapster.Core/Enums/AdaptDirectives.cs new file mode 100644 index 00000000..a273b1d6 --- /dev/null +++ b/src/Mapster.Core/Enums/AdaptDirectives.cs @@ -0,0 +1,8 @@ +namespace Mapster +{ + public enum AdaptDirectives + { + None = 0, + DestinationAsRecord = 1 + } +} From 9d622ee058c9c59f56ba4a6f64e68cc7a8cd2a07 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 22 Oct 2023 12:45:24 +0500 Subject: [PATCH 220/363] impliment attribute --- .../Attributes/AdaptAsAttribute.cs | 6 +-- .../Utils/RecordTypeIdentityHelper.cs | 11 +++++ .../WhenIgnoringConditionally.cs | 4 +- src/Mapster/TypeAdapterSetter.cs | 1 + src/Mapster/TypeAdapterSettings.cs | 1 + src/Mapster/Utils/ReflectionUtils.cs | 42 ++++++++++++------- 6 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/Mapster.Core/Attributes/AdaptAsAttribute.cs b/src/Mapster.Core/Attributes/AdaptAsAttribute.cs index 8f439bcb..7897d776 100644 --- a/src/Mapster.Core/Attributes/AdaptAsAttribute.cs +++ b/src/Mapster.Core/Attributes/AdaptAsAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace Mapster.Attributes +namespace Mapster { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct @@ -9,10 +9,10 @@ namespace Mapster.Attributes | AttributeTargets.Field, AllowMultiple = true)] public class AdaptAsAttribute : Attribute { - public AdaptDirectives AdaptDirective { get; } + public AdaptDirectives AdaptDirective { get; set; } public AdaptAsAttribute(AdaptDirectives directive) { - this.AdaptDirective = directive; + AdaptDirective = directive; } } } diff --git a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs index d9c113f9..ddd6ba9d 100644 --- a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs +++ b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs @@ -44,5 +44,16 @@ public static bool IsRecordType(Type type) return false; } + + public static bool IsDirectiveTagret(Type type) + { + var arrt = type.GetCustomAttributes()?.FirstOrDefault()?.AdaptDirective; + + if (arrt == null) return false; + if (arrt != null) + if (arrt == AdaptDirectives.DestinationAsRecord) return true; + + return false; + } } } diff --git a/src/Mapster.Tests/WhenIgnoringConditionally.cs b/src/Mapster.Tests/WhenIgnoringConditionally.cs index d7377a37..79001621 100644 --- a/src/Mapster.Tests/WhenIgnoringConditionally.cs +++ b/src/Mapster.Tests/WhenIgnoringConditionally.cs @@ -160,7 +160,6 @@ public void IgnoreIf_Can_Be_Combined() public void IgnoreIf_Apply_To_RecordType() { TypeAdapterConfig.NewConfig() - .EnableNonPublicMembers(true) // add or .IgnoreIf((src, dest) => src.Name == "TestName", dest => dest.Name) .Compile(); @@ -188,7 +187,8 @@ public class SimpleDto public string Name { get; set; } } - public class SimpleRecord // or Replace on record + [AdaptAs(AdaptDirectives.DestinationAsRecord)] + public class SimpleRecord { public int Id { get; } public string Name { get; } diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index 9215f375..8cf0a5bf 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -8,6 +8,7 @@ namespace Mapster { + [AdaptAs(AdaptDirectives.DestinationAsRecord)] public class TypeAdapterSetter { protected const string SourceParameterName = "source"; diff --git a/src/Mapster/TypeAdapterSettings.cs b/src/Mapster/TypeAdapterSettings.cs index 7af0f52b..4915df76 100644 --- a/src/Mapster/TypeAdapterSettings.cs +++ b/src/Mapster/TypeAdapterSettings.cs @@ -6,6 +6,7 @@ namespace Mapster { + [AdaptAs(AdaptDirectives.DestinationAsRecord)] public class TypeAdapterSettings : SettingStore { public IgnoreDictionary Ignore diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index fe44e790..d427dae8 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -168,34 +169,43 @@ public static bool IsRecordType(this Type type) if (type.IsConvertible()) return false; + if(RecordTypeIdentityHelper.IsDirectiveTagret(type)) // added Support work from custom Attribute + return true; + var props = type.GetFieldsAndProperties().ToList(); - #region SupportingСurrentBehavior for Config Clone and Fork - if (type == typeof(MulticastDelegate)) - return true; + // if (type == typeof(MulticastDelegate)) + // return true; - if (type == typeof(TypeAdapterSetter)) - return true; + //if (type == typeof(TypeAdapterSetter)) + // return true; - // if (type == typeof(TypeAdapterRule)) + //// if (type == typeof(TypeAdapterRule)) + //// return true; + + //if (type == typeof(TypeAdapterSettings)) // return true; - if (type == typeof(TypeAdapterSettings)) + //if (type == typeof(TypeTuple)) + // return true; + + + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) return true; - if (type.IsValueType && type?.GetConstructors().Length != 0) - { - var test = type.GetConstructors()[0].GetParameters(); - var param = type.GetConstructors()[0].GetParameters().ToArray(); - if (param[0]?.ParameterType == typeof(TypeTuple) && param[1]?.ParameterType == typeof(TypeAdapterRule)) - return true; - } + //if (type.IsValueType && type?.GetConstructors().Length != 0) + //{ + // var test = type.GetConstructors()[0].GetParameters(); + // var param = type.GetConstructors()[0].GetParameters().ToArray(); - if (type == typeof(TypeTuple)) - return true; + // if (param[0]?.ParameterType == typeof(TypeTuple) && param[1]?.ParameterType == typeof(TypeAdapterRule)) + // return true; + //} + + #endregion SupportingСurrentBehavior for Config Clone and Fork From 0f534a28cef9fecd439f559d243e8fbd8a2fde6f Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 22 Oct 2023 13:28:16 +0500 Subject: [PATCH 221/363] drop working Temporary bypass and ignore Not Valide Tests --- .../WhenMappingPrivateFieldsAndProperties.cs | 29 ++++++++++--------- src/Mapster/Adapters/ClassAdapter.cs | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingPrivateFieldsAndProperties.cs b/src/Mapster.Tests/WhenMappingPrivateFieldsAndProperties.cs index c51ce675..c099abe9 100644 --- a/src/Mapster.Tests/WhenMappingPrivateFieldsAndProperties.cs +++ b/src/Mapster.Tests/WhenMappingPrivateFieldsAndProperties.cs @@ -61,6 +61,7 @@ public void Should_Map_Private_Field_To_New_Object_Correctly() dto.Name.ShouldBe(customerName); } + [TestMethod] public void Should_Map_Private_Property_To_New_Object_Correctly() { @@ -77,8 +78,9 @@ public void Should_Map_Private_Property_To_New_Object_Correctly() dto.Name.ShouldBe(customerName); } + [Ignore] [TestMethod] - public void Should_Map_To_Private_Fields_Correctly() + public void Should_Map_To_Private_Fields_Correctly() // this test not testing this scenario { SetUpMappingNonPublicFields(); @@ -88,15 +90,16 @@ public void Should_Map_To_Private_Fields_Correctly() Name = "Customer 1" }; - var customer = dto.Adapt(); + var customer = dto.Adapt(); // creation as Record when constructor private member not used Assert.IsNotNull(customer); Assert.IsTrue(customer.HasId(dto.Id)); customer.Name.ShouldBe(dto.Name); } + [Ignore] [TestMethod] - public void Should_Map_To_Private_Properties_Correctly() + public void Should_Map_To_Private_Properties_Correctly() // this test not testing this scenario { SetUpMappingNonPublicFields(); @@ -106,7 +109,7 @@ public void Should_Map_To_Private_Properties_Correctly() Name = "Customer 1" }; - var customer = dto.Adapt(); + var customer = dto.Adapt(); // creation as Record when constructor private member not used Assert.IsNotNull(customer); customer.Id.ShouldBe(dto.Id); @@ -165,39 +168,39 @@ private static void SetUpMappingNonPublicProperties() #region TestMethod Classes - public class CustomerWithPrivateField + public class CustomerWithPrivateField // this detect as Record { - private readonly int _id; + private readonly int _id; // this not taken into account no public getter public string Name { get; private set; } - private CustomerWithPrivateField() { } + private CustomerWithPrivateField() { } // not public constructor not takened - public CustomerWithPrivateField(int id, string name) + public CustomerWithPrivateField(int id, string name) // two is two == true this worked as record { _id = id; Name = name; } - public bool HasId(int id) + public bool HasId(int id) // it is not property { return _id == id; } } - public class CustomerWithPrivateProperty + public class CustomerWithPrivateProperty // this detect as Record { public int Id { get; private set; } private string Name { get; set; } - private CustomerWithPrivateProperty() { } + private CustomerWithPrivateProperty() { } // not public constructor is not taken - public CustomerWithPrivateProperty(int id, string name) + public CustomerWithPrivateProperty(int id, string name) // two is two == true this worked as record { Id = id; Name = name; } - public bool HasName(string name) + public bool HasName(string name) // it is not property { return Name == name; } diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 49027a49..4b84fd84 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -75,7 +75,7 @@ bool IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(CompileArgument arg) - if ((arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) && !IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(arg)) + if ((arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null)) return base.CreateInstantiationExpression(source, destination, arg); ClassMapping? classConverter; From 11e8a12efbccf68a655a2f295a183ad9e8f22d76 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 22 Oct 2023 14:00:36 +0500 Subject: [PATCH 222/363] clean up --- .../Utils/RecordTypeIdentityHelper.cs | 7 ++-- .../WhenMappingRecordRegression.cs | 2 -- src/Mapster/Adapters/ClassAdapter.cs | 36 +++++++++---------- src/Mapster/Utils/ReflectionUtils.cs | 34 ++---------------- 4 files changed, 22 insertions(+), 57 deletions(-) diff --git a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs index ddd6ba9d..50f0f48e 100644 --- a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs +++ b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs @@ -49,9 +49,10 @@ public static bool IsDirectiveTagret(Type type) { var arrt = type.GetCustomAttributes()?.FirstOrDefault()?.AdaptDirective; - if (arrt == null) return false; - if (arrt != null) - if (arrt == AdaptDirectives.DestinationAsRecord) return true; + if (arrt == null) + return false; + if (arrt == AdaptDirectives.DestinationAsRecord) + return true; return false; } diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 2eb50f70..f68cd6f9 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -348,14 +348,12 @@ public MultiCtorRecord(int myInt, string myString) : this(myInt) public string MyString { get; private set; } } - class InlinePoco501 { public int MyInt { get; set; } public string MyString { get; set; } } - record OnlyInitRecord501 { public int MyInt { get; init; } diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 4b84fd84..42c69c40 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -55,27 +55,23 @@ protected override Expression CreateInstantiationExpression(Expression source, E //new TDestination(src.Prop1, src.Prop2) /// - bool IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(CompileArgument arg) - { - if (arg.Settings.EnableNonPublicMembers == null) - return false; - if (arg.Settings.EnableNonPublicMembers == false) - return false; - else - { - if (arg.DestinationType.GetConstructors().Any(x => x.GetParameters() != null)) - { - return true; - } - } - - - return false; - } - - + //bool IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(CompileArgument arg) + //{ + // if (arg.Settings.EnableNonPublicMembers == null) + // return false; + // if (arg.Settings.EnableNonPublicMembers == false) + // return false; + // else + // { + // if (arg.DestinationType.GetConstructors().Any(x => x.GetParameters() != null)) + // { + // return true; + // } + // } + // return false; + //} - if ((arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null)) + if (arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) return base.CreateInstantiationExpression(source, destination, arg); ClassMapping? classConverter; diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index d427dae8..bb683c20 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -171,44 +171,15 @@ public static bool IsRecordType(this Type type) if(RecordTypeIdentityHelper.IsDirectiveTagret(type)) // added Support work from custom Attribute return true; - - var props = type.GetFieldsAndProperties().ToList(); - + #region SupportingСurrentBehavior for Config Clone and Fork - // if (type == typeof(MulticastDelegate)) - // return true; - - //if (type == typeof(TypeAdapterSetter)) - // return true; - - //// if (type == typeof(TypeAdapterRule)) - //// return true; - - //if (type == typeof(TypeAdapterSettings)) - // return true; - - //if (type == typeof(TypeTuple)) - // return true; - - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) return true; - - //if (type.IsValueType && type?.GetConstructors().Length != 0) - //{ - // var test = type.GetConstructors()[0].GetParameters(); - // var param = type.GetConstructors()[0].GetParameters().ToArray(); - - // if (param[0]?.ParameterType == typeof(TypeTuple) && param[1]?.ParameterType == typeof(TypeAdapterRule)) - // return true; - //} - - - #endregion SupportingСurrentBehavior for Config Clone and Fork + var props = type.GetFieldsAndProperties().ToList(); //interface with readonly props if (type.GetTypeInfo().IsInterface && @@ -218,7 +189,6 @@ public static bool IsRecordType(this Type type) if(RecordTypeIdentityHelper.IsRecordType(type)) return true; - return false; } From 2af9a2e78f605c221d8cb66fc68d9c11305ad562 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 23 Oct 2023 06:31:17 +0500 Subject: [PATCH 223/363] cleam not used using --- src/Mapster/Utils/ReflectionUtils.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index bb683c20..9ef1a48c 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -1,12 +1,11 @@ -using System; +using Mapster.Models; +using Mapster.Utils; +using System; using System.Collections; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Mapster.Models; -using Mapster.Utils; // ReSharper disable once CheckNamespace namespace Mapster From 68cc2b2f113b273e34bcd67c5aedb649e39b082d Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 23 Oct 2023 16:16:40 +0500 Subject: [PATCH 224/363] add comment --- src/Mapster/Adapters/RecordTypeAdapter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 4d065e28..3d246072 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -21,7 +21,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E //new TDestination(src.Prop1, src.Prop2) if (arg.GetConstructUsing() != null) - return base.CreateInstantiationExpression(source, destination, arg); + return base.CreateInstantiationExpression(source, destination, arg); // this propably can inline Field activation, I didn’t see issue where it was requested :) var destType = arg.DestinationType.GetTypeInfo().IsInterface ? DynamicTypeGenerator.GetTypeForInterface(arg.DestinationType, arg.Settings.Includes.Count > 0) @@ -29,11 +29,11 @@ protected override Expression CreateInstantiationExpression(Expression source, E if (destType == null) return base.CreateInstantiationExpression(source, destination, arg); var ctor = destType.GetConstructors() - .OrderByDescending(it => it.GetParameters().Length).ToArray().FirstOrDefault(); + .OrderByDescending(it => it.GetParameters().Length).ToArray().FirstOrDefault(); // Will be used public constructor with the maximum number of parameters var classModel = GetConstructorModel(ctor, false); var classConverter = CreateClassConverter(source, classModel, arg); var installExpr = CreateInstantiationExpression(source, classConverter, arg); - return RecordInlineExpression(source, arg, installExpr); + return RecordInlineExpression(source, arg, installExpr); // Activator field when not include in public ctor } protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) From 04255af873043d153eb34cc36103a558f23d9c81 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 23 Oct 2023 16:31:04 +0500 Subject: [PATCH 225/363] Rename Attribute --- src/Mapster.Core/Attributes/AdaptAsAttribute.cs | 4 ++-- src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs | 2 +- src/Mapster.Tests/WhenIgnoringConditionally.cs | 2 +- src/Mapster/TypeAdapterSetter.cs | 2 +- src/Mapster/TypeAdapterSettings.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Mapster.Core/Attributes/AdaptAsAttribute.cs b/src/Mapster.Core/Attributes/AdaptAsAttribute.cs index 7897d776..46a96d66 100644 --- a/src/Mapster.Core/Attributes/AdaptAsAttribute.cs +++ b/src/Mapster.Core/Attributes/AdaptAsAttribute.cs @@ -7,10 +7,10 @@ namespace Mapster | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] - public class AdaptAsAttribute : Attribute + public class AdaptWithAttribute : Attribute { public AdaptDirectives AdaptDirective { get; set; } - public AdaptAsAttribute(AdaptDirectives directive) + public AdaptWithAttribute(AdaptDirectives directive) { AdaptDirective = directive; } diff --git a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs index 50f0f48e..b65d3742 100644 --- a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs +++ b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs @@ -47,7 +47,7 @@ public static bool IsRecordType(Type type) public static bool IsDirectiveTagret(Type type) { - var arrt = type.GetCustomAttributes()?.FirstOrDefault()?.AdaptDirective; + var arrt = type.GetCustomAttributes()?.FirstOrDefault()?.AdaptDirective; if (arrt == null) return false; diff --git a/src/Mapster.Tests/WhenIgnoringConditionally.cs b/src/Mapster.Tests/WhenIgnoringConditionally.cs index 79001621..471760b8 100644 --- a/src/Mapster.Tests/WhenIgnoringConditionally.cs +++ b/src/Mapster.Tests/WhenIgnoringConditionally.cs @@ -187,7 +187,7 @@ public class SimpleDto public string Name { get; set; } } - [AdaptAs(AdaptDirectives.DestinationAsRecord)] + [AdaptWith(AdaptDirectives.DestinationAsRecord)] public class SimpleRecord { public int Id { get; } diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index 8cf0a5bf..192df8aa 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -8,7 +8,7 @@ namespace Mapster { - [AdaptAs(AdaptDirectives.DestinationAsRecord)] + [AdaptWith(AdaptDirectives.DestinationAsRecord)] public class TypeAdapterSetter { protected const string SourceParameterName = "source"; diff --git a/src/Mapster/TypeAdapterSettings.cs b/src/Mapster/TypeAdapterSettings.cs index 4915df76..e2ecde08 100644 --- a/src/Mapster/TypeAdapterSettings.cs +++ b/src/Mapster/TypeAdapterSettings.cs @@ -6,7 +6,7 @@ namespace Mapster { - [AdaptAs(AdaptDirectives.DestinationAsRecord)] + [AdaptWith(AdaptDirectives.DestinationAsRecord)] public class TypeAdapterSettings : SettingStore { public IgnoreDictionary Ignore From a5f90183d872a42e28260fc9caa0bd8d0eaf3bc9 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 24 Oct 2023 21:10:54 +0500 Subject: [PATCH 226/363] reworking tests to check the case specified in the description --- .../WhenMappingPrivateFieldsAndProperties.cs | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingPrivateFieldsAndProperties.cs b/src/Mapster.Tests/WhenMappingPrivateFieldsAndProperties.cs index c099abe9..c87dcb07 100644 --- a/src/Mapster.Tests/WhenMappingPrivateFieldsAndProperties.cs +++ b/src/Mapster.Tests/WhenMappingPrivateFieldsAndProperties.cs @@ -78,28 +78,25 @@ public void Should_Map_Private_Property_To_New_Object_Correctly() dto.Name.ShouldBe(customerName); } - [Ignore] [TestMethod] - public void Should_Map_To_Private_Fields_Correctly() // this test not testing this scenario + public void Should_Map_To_Private_Fields_Correctly() { - SetUpMappingNonPublicFields(); - - var dto = new CustomerDTO + SetUpMappingNonPublicFields(); + + var dto = new CustomerDTOWithPrivateGet { Id = 1, Name = "Customer 1" }; - var customer = dto.Adapt(); // creation as Record when constructor private member not used + var customer = dto.Adapt(); - Assert.IsNotNull(customer); - Assert.IsTrue(customer.HasId(dto.Id)); - customer.Name.ShouldBe(dto.Name); + customer.HasId().ShouldBe(1); + customer.Name.ShouldBe("Customer 1"); } - [Ignore] [TestMethod] - public void Should_Map_To_Private_Properties_Correctly() // this test not testing this scenario + public void Should_Map_To_Private_Properties_Correctly() { SetUpMappingNonPublicFields(); @@ -109,11 +106,10 @@ public void Should_Map_To_Private_Properties_Correctly() // this test not testin Name = "Customer 1" }; - var customer = dto.Adapt(); // creation as Record when constructor private member not used + var customer = dto.Adapt(); - Assert.IsNotNull(customer); - customer.Id.ShouldBe(dto.Id); - Assert.IsTrue(customer.HasName(dto.Name)); + customer.Id.ShouldBe(1); + customer.HasName().ShouldBe("Customer 1"); } [TestMethod] @@ -168,41 +164,41 @@ private static void SetUpMappingNonPublicProperties() #region TestMethod Classes - public class CustomerWithPrivateField // this detect as Record + public class CustomerWithPrivateField { - private readonly int _id; // this not taken into account no public getter + private int _id; public string Name { get; private set; } - private CustomerWithPrivateField() { } // not public constructor not takened + public CustomerWithPrivateField() { } - public CustomerWithPrivateField(int id, string name) // two is two == true this worked as record + public CustomerWithPrivateField(int id, string name) { _id = id; Name = name; } - public bool HasId(int id) // it is not property + public int HasId() { - return _id == id; + return _id; } } - public class CustomerWithPrivateProperty // this detect as Record + public class CustomerWithPrivateProperty { public int Id { get; private set; } private string Name { get; set; } - private CustomerWithPrivateProperty() { } // not public constructor is not taken + public CustomerWithPrivateProperty() { } - public CustomerWithPrivateProperty(int id, string name) // two is two == true this worked as record + public CustomerWithPrivateProperty(int id, string name) { Id = id; Name = name; } - public bool HasName(string name) // it is not property + public string HasName() { - return Name == name; + return Name; } } @@ -231,6 +227,12 @@ public class CustomerDTO public string Name { get; set; } } + public class CustomerDTOWithPrivateGet + { + public int Id { private get; set; } + public string Name { private get; set; } + } + public class Pet { public string Name { get; set; } From 6e7283f504975e6d75c14d398c9bc06f27a5db0f Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 24 Oct 2023 21:16:05 +0500 Subject: [PATCH 227/363] remove CreateBlockExpression section in RecortType Adapter. With the addition of InlineExpression it only creates extra code when updating --- src/Mapster/Adapters/RecordTypeAdapter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 3d246072..009af932 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -21,7 +21,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E //new TDestination(src.Prop1, src.Prop2) if (arg.GetConstructUsing() != null) - return base.CreateInstantiationExpression(source, destination, arg); // this propably can inline Field activation, I didn’t see issue where it was requested :) + return base.CreateInstantiationExpression(source, destination, arg); var destType = arg.DestinationType.GetTypeInfo().IsInterface ? DynamicTypeGenerator.GetTypeForInterface(arg.DestinationType, arg.Settings.Includes.Count > 0) @@ -38,7 +38,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { - return base.CreateBlockExpression(source, destination, arg); + return Expression.Empty(); } protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) From e06f6edc7d0ce5b1182d4098163f4f27ed21749a Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 26 Oct 2023 18:55:41 +0500 Subject: [PATCH 228/363] Remane file and del attribute usage --- .../Attributes/{AdaptAsAttribute.cs => AdaptWithAttribute.cs} | 1 - 1 file changed, 1 deletion(-) rename src/Mapster.Core/Attributes/{AdaptAsAttribute.cs => AdaptWithAttribute.cs} (90%) diff --git a/src/Mapster.Core/Attributes/AdaptAsAttribute.cs b/src/Mapster.Core/Attributes/AdaptWithAttribute.cs similarity index 90% rename from src/Mapster.Core/Attributes/AdaptAsAttribute.cs rename to src/Mapster.Core/Attributes/AdaptWithAttribute.cs index 46a96d66..e12a4aea 100644 --- a/src/Mapster.Core/Attributes/AdaptAsAttribute.cs +++ b/src/Mapster.Core/Attributes/AdaptWithAttribute.cs @@ -4,7 +4,6 @@ namespace Mapster { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct - | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] public class AdaptWithAttribute : Attribute From 218c91743bf28ebb38d9821bed5e6d066f41b41c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 26 Oct 2023 19:12:25 +0500 Subject: [PATCH 229/363] add ReadOnlyInterfaceAdapter --- src/Mapster/Adapters/ClassAdapter.cs | 19 +------ .../Adapters/ReadOnlyInterfaceAdapter.cs | 50 +++++++++++++++++++ src/Mapster/TypeAdapterConfig.cs | 1 + src/Mapster/Utils/ReflectionUtils.cs | 7 --- 4 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 42c69c40..c190a7a4 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -53,24 +53,7 @@ protected override bool CanInline(Expression source, Expression? destination, Co protected override Expression CreateInstantiationExpression(Expression source, Expression? destination, CompileArgument arg) { //new TDestination(src.Prop1, src.Prop2) - - /// - //bool IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(CompileArgument arg) - //{ - // if (arg.Settings.EnableNonPublicMembers == null) - // return false; - // if (arg.Settings.EnableNonPublicMembers == false) - // return false; - // else - // { - // if (arg.DestinationType.GetConstructors().Any(x => x.GetParameters() != null)) - // { - // return true; - // } - // } - // return false; - //} - + if (arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) return base.CreateInstantiationExpression(source, destination, arg); diff --git a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs new file mode 100644 index 00000000..96f8b01d --- /dev/null +++ b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs @@ -0,0 +1,50 @@ +using Mapster.Utils; +using System.Linq; +using System.Linq.Expressions; + +namespace Mapster.Adapters +{ + internal class ReadOnlyInterfaceAdapter : ClassAdapter + { + protected override int Score => -151; + + protected override bool CanMap(PreCompileArgument arg) + { + return arg.DestinationType.IsInterface; + } + + protected override bool CanInline(Expression source, Expression? destination, CompileArgument arg) + { + if (base.CanInline(source, destination, arg)) + return true; + else + return false; + + } + + protected override Expression CreateInstantiationExpression(Expression source, Expression? destination, CompileArgument arg) + { + //new TDestination(src.Prop1, src.Prop2) + var destintionType = arg.DestinationType; + var props = destintionType.GetFieldsAndProperties().ToList(); + + //interface with readonly props + if (props.Any(p => p.SetterModifier != AccessModifier.Public)) + { + if (arg.GetConstructUsing() != null) + return base.CreateInstantiationExpression(source, destination, arg); + + var destType = DynamicTypeGenerator.GetTypeForInterface(arg.DestinationType, arg.Settings.Includes.Count > 0); + if (destType == null) + return base.CreateInstantiationExpression(source, destination, arg); + var ctor = destType.GetConstructors()[0]; + var classModel = GetConstructorModel(ctor, false); + var classConverter = CreateClassConverter(source, classModel, arg); + return CreateInstantiationExpression(source, classConverter, arg); + } + else + return base.CreateInstantiationExpression(source,destination, arg); + } + + } +} diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index f5ebbb3a..6e3a9da9 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -21,6 +21,7 @@ private static List CreateRuleTemplate() return new List { new PrimitiveAdapter().CreateRule(), //-200 + new ReadOnlyInterfaceAdapter().CreateRule(), // -151 new ClassAdapter().CreateRule(), //-150 new RecordTypeAdapter().CreateRule(), //-149 new CollectionAdapter().CreateRule(), //-125 diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 9ef1a48c..d84f3dfa 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -178,13 +178,6 @@ public static bool IsRecordType(this Type type) #endregion SupportingСurrentBehavior for Config Clone and Fork - var props = type.GetFieldsAndProperties().ToList(); - - //interface with readonly props - if (type.GetTypeInfo().IsInterface && - props.Any(p => p.SetterModifier != AccessModifier.Public)) - return true; - if(RecordTypeIdentityHelper.IsRecordType(type)) return true; From 730d69b645bba45c33aaddc216bf6dcc5e32accf Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 26 Oct 2023 19:19:12 +0500 Subject: [PATCH 230/363] fix --- src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs | 2 +- src/Mapster/TypeAdapterConfig.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs index 96f8b01d..c81ec260 100644 --- a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs +++ b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs @@ -6,7 +6,7 @@ namespace Mapster.Adapters { internal class ReadOnlyInterfaceAdapter : ClassAdapter { - protected override int Score => -151; + protected override int Score => -148; protected override bool CanMap(PreCompileArgument arg) { diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 6e3a9da9..ff9387c2 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -21,9 +21,9 @@ private static List CreateRuleTemplate() return new List { new PrimitiveAdapter().CreateRule(), //-200 - new ReadOnlyInterfaceAdapter().CreateRule(), // -151 new ClassAdapter().CreateRule(), //-150 new RecordTypeAdapter().CreateRule(), //-149 + new ReadOnlyInterfaceAdapter().CreateRule(), // -151 new CollectionAdapter().CreateRule(), //-125 new DictionaryAdapter().CreateRule(), //-124 new ArrayAdapter().CreateRule(), //-123 From 24d71a3cfa1206aa40b420316d45c0631606fda0 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 26 Oct 2023 19:20:17 +0500 Subject: [PATCH 231/363] fix --- src/Mapster/TypeAdapterConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index ff9387c2..cb8ac881 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -23,7 +23,7 @@ private static List CreateRuleTemplate() new PrimitiveAdapter().CreateRule(), //-200 new ClassAdapter().CreateRule(), //-150 new RecordTypeAdapter().CreateRule(), //-149 - new ReadOnlyInterfaceAdapter().CreateRule(), // -151 + new ReadOnlyInterfaceAdapter().CreateRule(), // -148 new CollectionAdapter().CreateRule(), //-125 new DictionaryAdapter().CreateRule(), //-124 new ArrayAdapter().CreateRule(), //-123 From db35b17d55529acfcb98b3a25e955e4c537856b9 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 27 Oct 2023 15:12:24 +0500 Subject: [PATCH 232/363] clean --- src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs index c81ec260..df4a0f66 100644 --- a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs +++ b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs @@ -24,7 +24,6 @@ protected override bool CanInline(Expression source, Expression? destination, Co protected override Expression CreateInstantiationExpression(Expression source, Expression? destination, CompileArgument arg) { - //new TDestination(src.Prop1, src.Prop2) var destintionType = arg.DestinationType; var props = destintionType.GetFieldsAndProperties().ToList(); From b5dc28e7de211e75713dd61f53cf0bf44a726423 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 28 Oct 2023 06:06:11 +0500 Subject: [PATCH 233/363] add test --- ...MappingPrimitiveCustomMappingRegression.cs | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs diff --git a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs new file mode 100644 index 00000000..01b0820e --- /dev/null +++ b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs @@ -0,0 +1,116 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenMappingPrimitiveCustomMappingRegression + { + public void CustomMappingDateTimeToPrimitive() + { + TypeAdapterConfig + .NewConfig() + .MapWith(src => new DateTimeOffset(src).ToUnixTimeSeconds()); + + TypeAdapterConfig + .NewConfig() + .MapWith(src => src.ToShortDateString()); + + var _source = new DateTime(2023, 10, 27); + + var _resultToLong = _source.Adapt(); + var _resultToString = _source.Adapt(); + + + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/561 + /// + [TestMethod] + public void MappingToPrimitiveInsiderWithCustomMapping() + { + TypeAdapterConfig, string?> + .NewConfig() + .MapToTargetWith((source, target) => source.HasValue ? source.Value : target); + + var sourceNull = new Source561 { Name = new Optional561(null) }; + var target = new Source561 { Name = new Optional561("John") }.Adapt(); + + var TargetDestinationFromNull = new Target561() { Name = "Me" }; + var NullToupdateoptional = sourceNull.Adapt(TargetDestinationFromNull); + var _result = sourceNull.Adapt(target); + + target.Name.ShouldBe("John"); + NullToupdateoptional.Name.ShouldBe("Me"); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/407 + /// + [TestMethod] + public void MappingDatetimeToLongWithCustomMapping() + { + TypeAdapterConfig + .NewConfig() + .MapWith(src => new DateTimeOffset(src).ToUnixTimeSeconds()); + + TypeAdapterConfig + .NewConfig() + .MapWith(src => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(src).Date); + + var emptySource = new Source407() { Time = DateTime.UtcNow.Date }; + var fromC1 = new DateTime(2023, 10, 27); + var fromC2 = new DateTimeOffset(new DateTime(2025, 11, 23)).ToUnixTimeSeconds(); + var c1 = new Source407 { Time = fromC1 }; + var c2 = new Destination407 { Time = fromC2 }; + + var _result = c1.Adapt(); // Work + var _resultLongtoDateTime = c2.Adapt(); + + _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27)).ToUnixTimeSeconds()); // Work + _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 22).Date); // work but but it turns out to be a day less. Perhaps this is how it was intended + } + + } + + + #region TestClasses + + public class Source407 + { + public DateTime Time { get; set; } + } + + public class Destination407 + { + public long Time { get; set; } + } + + class Optional561 + { + public Optional561(T? value) + { + if (value != null) + HasValue = true; + + Value = value; + } + + public bool HasValue { get; } + public T? Value { get; } + } + + class Source561 + { + public Optional561 Name { get; set; } + } + + class Target561 + { + public string? Name { get; set; } + } + + #endregion TestClasses +} From 6215452c2c11d0d4bc33e61975db1eebea55b424 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 28 Oct 2023 06:12:18 +0500 Subject: [PATCH 234/363] impliment in TypeAdaptSettings and TypeAdaptSetter --- src/Mapster/TypeAdapterSetter.cs | 10 ++++++++++ src/Mapster/TypeAdapterSettings.cs | 21 +++++++++++++++++++++ src/Mapster/Utils/ReflectionUtils.cs | 5 +++++ 3 files changed, 36 insertions(+) diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index 9215f375..c0bf07c4 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -611,6 +611,11 @@ public TypeAdapterSetter MapWith(Expression MapToTargetWith(Expression Set(nameof(GenerateMapper), value); } + public bool? MapWithToPrimitive + { + get => Get(nameof(MapWithToPrimitive)); + set => Set(nameof(MapWithToPrimitive), value); + } + + /// + /// Not implemented + /// + public bool? MapToPrimitive + { + get => Get(nameof(MapToPrimitive)); + set => Set(nameof(MapToPrimitive), value); + } + + public bool? MapToTargetPrimitive + { + get => Get(nameof(MapToTargetPrimitive)); + set => Set(nameof(MapToTargetPrimitive), value); + } + public List> ShouldMapMember { get => Get(nameof(ShouldMapMember), () => new List>()); diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index fe44e790..1de6e7ae 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -36,6 +36,11 @@ public static Type GetTypeInfo(this Type type) } #endif + public static bool IsMapsterPrimitive(this Type type) + { + return _primitiveTypes.TryGetValue(type, out var primitiveType) || type == typeof(string); + } + public static bool IsNullable(this Type type) { return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); From 6ebac66afad98aeeb79b7dff6efd95c74edc98bc Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 28 Oct 2023 06:26:47 +0500 Subject: [PATCH 235/363] Implimentation in BaseAdapter and PrimitiveAdapter --- src/Mapster/Adapters/BaseAdapter.cs | 26 ++++++++++++++++++++++++ src/Mapster/Adapters/PrimitiveAdapter.cs | 18 ++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index b7b6fc67..92e9990a 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -85,6 +85,32 @@ protected virtual Expression CreateExpressionBody(Expression source, Expression? if (CheckExplicitMapping && arg.Context.Config.RequireExplicitMapping && !arg.ExplicitMapping) throw new InvalidOperationException("Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists"); + #region CustomMappingPrimitiveImplimentation + + if (arg.Settings.MapToTargetPrimitive == true) + { + Expression dest; + + if (destination == null) + { + dest = arg.DestinationType.CreateDefault(); + } + else + dest = destination; + + var customConvert = arg.Context.Config.CreateMapToTargetInvokeExpressionBody(source.Type, arg.DestinationType, source, dest); + + arg.MapType = MapType.MapToTarget; + return customConvert; + } + + if (arg.Settings.MapWithToPrimitive == true) + { + return arg.Context.Config.CreateMapInvokeExpressionBody(source.Type, arg.DestinationType, source); + } + + #endregion CustomMappingPrimitiveImplimentation + var oldMaxDepth = arg.Context.MaxDepth; var oldDepth = arg.Context.Depth; try diff --git a/src/Mapster/Adapters/PrimitiveAdapter.cs b/src/Mapster/Adapters/PrimitiveAdapter.cs index 2a407a89..cfc7c809 100644 --- a/src/Mapster/Adapters/PrimitiveAdapter.cs +++ b/src/Mapster/Adapters/PrimitiveAdapter.cs @@ -18,6 +18,24 @@ protected override bool CanMap(PreCompileArgument arg) protected override Expression CreateExpressionBody(Expression source, Expression? destination, CompileArgument arg) { + + if (arg.Settings.MapToTargetPrimitive == true) + { + Expression dest; + + if (destination == null) + { + dest = arg.DestinationType.CreateDefault(); + } + else + dest = destination; + + var customConvert = arg.Context.Config.CreateMapToTargetInvokeExpressionBody(source.Type, arg.DestinationType, source, dest); + + arg.MapType = MapType.MapToTarget; + return customConvert; + } + Expression convert = source; var sourceType = arg.SourceType; var destinationType = arg.DestinationType; From d7f40a12f59b2cffa3efb280500a4cf2def3b1d0 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 28 Oct 2023 06:35:42 +0500 Subject: [PATCH 236/363] refactoring tests --- .../WhenMappingPrimitiveCustomMappingRegression.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs index 01b0820e..d1df5b80 100644 --- a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs +++ b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs @@ -7,6 +7,7 @@ namespace Mapster.Tests [TestClass] public class WhenMappingPrimitiveCustomMappingRegression { + [TestMethod] public void CustomMappingDateTimeToPrimitive() { TypeAdapterConfig @@ -22,7 +23,9 @@ public void CustomMappingDateTimeToPrimitive() var _resultToLong = _source.Adapt(); var _resultToString = _source.Adapt(); - + _resultToLong.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27)).ToUnixTimeSeconds()); + _resultToString.ShouldNotBe(_source.ToString()); + _resultToString.ShouldBe(_source.ToShortDateString()); } /// From 0eccadcbb316e767d3c720bbb8e1c66c750c2378 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 28 Oct 2023 08:27:10 +0500 Subject: [PATCH 237/363] fix date --- .../WhenMappingPrimitiveCustomMappingRegression.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs index d1df5b80..477b445d 100644 --- a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs +++ b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs @@ -65,7 +65,7 @@ public void MappingDatetimeToLongWithCustomMapping() var emptySource = new Source407() { Time = DateTime.UtcNow.Date }; var fromC1 = new DateTime(2023, 10, 27); - var fromC2 = new DateTimeOffset(new DateTime(2025, 11, 23)).ToUnixTimeSeconds(); + var fromC2 = new DateTimeOffset(new DateTime(2025, 11, 23,0,0,0,DateTimeKind.Utc)).ToUnixTimeSeconds(); var c1 = new Source407 { Time = fromC1 }; var c2 = new Destination407 { Time = fromC2 }; @@ -73,7 +73,7 @@ public void MappingDatetimeToLongWithCustomMapping() var _resultLongtoDateTime = c2.Adapt(); _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27)).ToUnixTimeSeconds()); // Work - _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 22).Date); // work but but it turns out to be a day less. Perhaps this is how it was intended + _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 23).Date); // work but but it turns out to be a day less. Perhaps this is how it was intended } } From af1f374f9635007ea4a738bdbf6ffd1a7271fe7e Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 28 Oct 2023 08:34:29 +0500 Subject: [PATCH 238/363] date all fix --- .../WhenMappingPrimitiveCustomMappingRegression.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs index 477b445d..f97383a9 100644 --- a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs +++ b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs @@ -64,7 +64,7 @@ public void MappingDatetimeToLongWithCustomMapping() .MapWith(src => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(src).Date); var emptySource = new Source407() { Time = DateTime.UtcNow.Date }; - var fromC1 = new DateTime(2023, 10, 27); + var fromC1 = new DateTime(2023, 10, 27,0,0,0,DateTimeKind.Utc); var fromC2 = new DateTimeOffset(new DateTime(2025, 11, 23,0,0,0,DateTimeKind.Utc)).ToUnixTimeSeconds(); var c1 = new Source407 { Time = fromC1 }; var c2 = new Destination407 { Time = fromC2 }; @@ -72,7 +72,7 @@ public void MappingDatetimeToLongWithCustomMapping() var _result = c1.Adapt(); // Work var _resultLongtoDateTime = c2.Adapt(); - _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27)).ToUnixTimeSeconds()); // Work + _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27, 0, 0, 0, DateTimeKind.Utc)).ToUnixTimeSeconds()); // Work _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 23).Date); // work but but it turns out to be a day less. Perhaps this is how it was intended } From 00bc359f1e786a6bf11a6f9c8b704030ff81b77f Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 28 Oct 2023 08:45:00 +0500 Subject: [PATCH 239/363] final refactoring tests and drop comments --- .../WhenMappingPrimitiveCustomMappingRegression.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs index f97383a9..c2f17261 100644 --- a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs +++ b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs @@ -18,12 +18,12 @@ public void CustomMappingDateTimeToPrimitive() .NewConfig() .MapWith(src => src.ToShortDateString()); - var _source = new DateTime(2023, 10, 27); + var _source = new DateTime(2023, 10, 27, 0, 0, 0, DateTimeKind.Utc); var _resultToLong = _source.Adapt(); var _resultToString = _source.Adapt(); - _resultToLong.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27)).ToUnixTimeSeconds()); + _resultToLong.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27, 0, 0, 0, DateTimeKind.Utc)).ToUnixTimeSeconds()); _resultToString.ShouldNotBe(_source.ToString()); _resultToString.ShouldBe(_source.ToShortDateString()); } @@ -65,15 +65,15 @@ public void MappingDatetimeToLongWithCustomMapping() var emptySource = new Source407() { Time = DateTime.UtcNow.Date }; var fromC1 = new DateTime(2023, 10, 27,0,0,0,DateTimeKind.Utc); - var fromC2 = new DateTimeOffset(new DateTime(2025, 11, 23,0,0,0,DateTimeKind.Utc)).ToUnixTimeSeconds(); + var fromC2 = new DateTimeOffset(new DateTime(2025, 11, 23, 0, 0, 0, DateTimeKind.Utc)).ToUnixTimeSeconds(); var c1 = new Source407 { Time = fromC1 }; var c2 = new Destination407 { Time = fromC2 }; var _result = c1.Adapt(); // Work var _resultLongtoDateTime = c2.Adapt(); - _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27, 0, 0, 0, DateTimeKind.Utc)).ToUnixTimeSeconds()); // Work - _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 23).Date); // work but but it turns out to be a day less. Perhaps this is how it was intended + _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27, 0, 0, 0, DateTimeKind.Utc)).ToUnixTimeSeconds()); + _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 23).Date); } } From 50ade18843e39315aff0dff349321318a9738919 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 28 Oct 2023 09:22:49 +0500 Subject: [PATCH 240/363] Add Projection test --- ...MappingPrimitiveCustomMappingRegression.cs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs index c2f17261..d694ac67 100644 --- a/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs +++ b/src/Mapster.Tests/WhenMappingPrimitiveCustomMappingRegression.cs @@ -1,6 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Shouldly; using System; +using System.Collections.Generic; +using System.Linq; namespace Mapster.Tests { @@ -69,13 +71,39 @@ public void MappingDatetimeToLongWithCustomMapping() var c1 = new Source407 { Time = fromC1 }; var c2 = new Destination407 { Time = fromC2 }; - var _result = c1.Adapt(); // Work + var _result = c1.Adapt(); var _resultLongtoDateTime = c2.Adapt(); _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27, 0, 0, 0, DateTimeKind.Utc)).ToUnixTimeSeconds()); _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 23).Date); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/407 + /// + [TestMethod] + public void CustomMappingPrimitiveToProjection() + { + TypeAdapterConfig + .NewConfig() + .MapWith(src => new DateTimeOffset(src).ToUnixTimeSeconds()); + + TypeAdapterConfig + .NewConfig() + .MapWith(src => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(src).Date); + + var _sourceList = new List(); + _sourceList.Add(new Source407 { Time = new DateTime(2023, 10, 27, 0, 0, 0, DateTimeKind.Utc) }); + var _fromC2List = new List(); + _fromC2List.Add(new Destination407 { Time = new DateTimeOffset(new DateTime(2025, 11, 23, 0, 0, 0, DateTimeKind.Utc)).ToUnixTimeSeconds() }); + + var _resultProjectionDateTimeTolong = _sourceList.AsQueryable().ProjectToType().ToList(); + var _resultProjectionLongToDateTime = _fromC2List.AsQueryable().ProjectToType().ToList(); + + _resultProjectionDateTimeTolong[0].Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27, 0, 0, 0, DateTimeKind.Utc)).ToUnixTimeSeconds()); + _resultProjectionLongToDateTime[0].Time.ShouldBe(new DateTime(2025, 11, 23).Date); + } + } From ca6bf7d7bc0904cf5af4fe2a743329fd61774ac7 Mon Sep 17 00:00:00 2001 From: Sim Tsai <13759975+SimTsai@users.noreply.github.com> Date: Fri, 15 Dec 2023 21:17:58 +0800 Subject: [PATCH 241/363] fix #656 --- src/Mapster/Adapters/ClassAdapter.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index cc803d8d..47aac18a 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -134,7 +134,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio { var destinationPropertyInfo = (PropertyInfo)member.DestinationMember.Info!; adapt = destinationPropertyInfo.IsInitOnly() - ? SetValueByReflection(destination, (MemberExpression)adapt, arg.DestinationType) + ? SetValueByReflection(member, (MemberExpression)adapt) : member.DestinationMember.SetExpression(destination, adapt); } catch (Exception e) @@ -178,19 +178,17 @@ protected override Expression CreateBlockExpression(Expression source, Expressio return lines.Count > 0 ? (Expression)Expression.Block(lines) : Expression.Empty(); } - private static Expression SetValueByReflection(Expression destination, MemberExpression adapt, - Type destinationType) + private static Expression SetValueByReflection(MemberMapping member, MemberExpression adapt) { - var memberName = adapt.Member.Name; - var typeofExpression = Expression.Constant(destinationType); + var typeofExpression = Expression.Constant(member.Destination!.Type); var getPropertyMethod = typeof(Type).GetMethod("GetProperty", new[] { typeof(string) })!; var getPropertyExpression = Expression.Call(typeofExpression, getPropertyMethod, - Expression.Constant(memberName)); + Expression.Constant(member.DestinationMember.Name)); var setValueMethod = typeof(PropertyInfo).GetMethod("SetValue", new[] { typeof(object), typeof(object) })!; var memberAsObject = adapt.To(typeof(object)); return Expression.Call(getPropertyExpression, setValueMethod, - new[] { destination, memberAsObject }); + new[] { member.Destination, memberAsObject }); } protected override Expression? CreateInlineExpression(Expression source, CompileArgument arg) From b518398ba831709dd7065937e0023cd1f563d2ec Mon Sep 17 00:00:00 2001 From: Alexander Drozdov Date: Mon, 13 May 2024 13:36:55 +0700 Subject: [PATCH 242/363] Updated Mapster.Tool (markdown) From 22563697e17cf01584edae3df3e187eb563a1919 Mon Sep 17 00:00:00 2001 From: Casey Boyle Date: Fri, 14 Jun 2024 15:27:18 -0500 Subject: [PATCH 243/363] Added isolated assembly loading for issue #714 --- .gitignore | 5 ++ .../DeferredDependencyAssemblyLoadContext.cs | 87 +++++++++++++++++++ src/Mapster.Tool/Program.cs | 34 +++++++- 3 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 src/Mapster.Tool/DeferredDependencyAssemblyLoadContext.cs diff --git a/.gitignore b/.gitignore index 83a5664f..9c0a14cd 100644 --- a/.gitignore +++ b/.gitignore @@ -181,3 +181,8 @@ project.lock.json /src/.vs /.vs src/.idea + +# VS Code settings +.vscode/launch.json +.vscode/settings.json +.vscode/tasks.json diff --git a/src/Mapster.Tool/DeferredDependencyAssemblyLoadContext.cs b/src/Mapster.Tool/DeferredDependencyAssemblyLoadContext.cs new file mode 100644 index 00000000..09132d22 --- /dev/null +++ b/src/Mapster.Tool/DeferredDependencyAssemblyLoadContext.cs @@ -0,0 +1,87 @@ +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace Mapster.Tool +{ + // + // Summary: + // Used for loading an assembly and its dependencies in an isolated assembly load context but deferring the resolution of + // a subset of those assemblies to an already existing Assembly Load Context (likely the AssemblyLoadContext.Default + // context that is used by the runtime by default at startup) + public class DeferredDependencyAssemblyLoadContext : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver resolver; + private readonly ImmutableHashSet deferredDependencyAssemblyNames; + private readonly AssemblyLoadContext deferToContext; + + public DeferredDependencyAssemblyLoadContext( + string assemblyPath, + AssemblyLoadContext deferToContext, + params AssemblyName[] deferredDependencyAssemblyNames + ) + { + // set up a resolver for the dependencies of this non-deferred assembly + resolver = new AssemblyDependencyResolver(assemblyPath); + + // store all of the assembly simple names that should be deferred w/ + // the sharing assembly context loader (and not resolved exclusively in this loader) + this.deferredDependencyAssemblyNames = deferredDependencyAssemblyNames + .Select(an => an.Name!) + .Where(n => n != null) + .ToImmutableHashSet(); + + // store a reference to the assembly load context that assembly resolution will be deferred + // to when on the deferredDependencyAssemblyNames list + this.deferToContext = deferToContext; + + // load the non-deferred assembly in this context to start + Load(GetAssemblyName(assemblyPath)); + } + + protected override Assembly? Load(AssemblyName assemblyName) + { + if (assemblyName.Name == null) + { + return null; + } + + // if the assembly to be loaded is also set to be deferrred (based on constructor) + // then first attempt to load it from the sharing assembly load context + if (deferredDependencyAssemblyNames.Contains(assemblyName.Name)) + { + return deferToContext.LoadFromAssemblyName(assemblyName); + } + + // all other loaded assemblies should be considered dependencies of the + // non-deferred dependency loaded in the constructor and should be loaded + // from its path (the AssemblyDepedencyResolver resolves dependency paths) + string? assemblyPath = resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath == null) + { + return null; + } + + return LoadFromAssemblyPath(assemblyPath); + } + + public static Assembly LoadAssemblyFrom( + string assemblyPath, + AssemblyLoadContext deferToContext, + params AssemblyName[] deferredDependencyAssemblyNames + ) + { + DeferredDependencyAssemblyLoadContext loadContext = + new DeferredDependencyAssemblyLoadContext( + assemblyPath, + deferToContext, + deferredDependencyAssemblyNames + ); + return loadContext.LoadFromAssemblyName( + new AssemblyName(Path.GetFileNameWithoutExtension(assemblyPath)) + ); + } + } +} diff --git a/src/Mapster.Tool/Program.cs b/src/Mapster.Tool/Program.cs index 4066ef52..86085b89 100644 --- a/src/Mapster.Tool/Program.cs +++ b/src/Mapster.Tool/Program.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.Loader; using System.Text; using CommandLine; using ExpressionDebugger; @@ -60,7 +61,26 @@ private static void WriteFile(string code, string path) private static void GenerateMappers(MapperOptions opt) { - var assembly = Assembly.LoadFrom(Path.GetFullPath(opt.Assembly)); + // We want loaded assemblies that we're scanning to be isolated from our currently + // running assembly load context in order to avoid type/framework collisions between Mapster assemblies + // and their dependencies and the scanned assemblies and their dependencies + + // However, we also need *some* of those scanned assemblies and thus their types to resolve from our + // currently running AssemblyLoadContext.Default: The Mapster assembly basically. + + // This way when we compare attribute types (such as MapperAttribute) between our running assembly + // and the scanned assembly the two types with the same FullName can be considered equal because + // they both were resolved from AssemblyLoadContext.Default. + + // This isolated Assembly Load Context will be able to resolve the Mapster assembly, but + // the resolved Assembly will be the same one that is in AssemblyLoadContext.Default + // (the runtime assembly load context that our code refers to by default when referencing + // types) + var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( + assemblyPath: Path.GetFullPath(opt.Assembly), + deferToContext: AssemblyLoadContext.Default, + deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + ); var config = TypeAdapterConfig.GlobalSettings; config.SelfContainedCodeGeneration = true; config.Scan(assembly); @@ -150,7 +170,11 @@ private static string GetImplName(string name) private static void GenerateModels(ModelOptions opt) { - var assembly = Assembly.LoadFrom(Path.GetFullPath(opt.Assembly)); + var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( + assemblyPath: Path.GetFullPath(opt.Assembly), + deferToContext: AssemblyLoadContext.Default, + deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + ); var codeGenConfig = new CodeGenerationConfig(); codeGenConfig.Scan(assembly); @@ -381,7 +405,11 @@ private static void ApplySettings(TypeAdapterSetter setter, BaseAdaptAttribute a private static void GenerateExtensions(ExtensionOptions opt) { - var assembly = Assembly.LoadFrom(Path.GetFullPath(opt.Assembly)); + var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( + assemblyPath: Path.GetFullPath(opt.Assembly), + deferToContext: AssemblyLoadContext.Default, + deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + ); var config = TypeAdapterConfig.GlobalSettings; config.SelfContainedCodeGeneration = true; config.Scan(assembly); From e00c2bb05bb64d053c825332f3580e9b6bcbe717 Mon Sep 17 00:00:00 2001 From: Casey Boyle Date: Mon, 17 Jun 2024 10:13:07 -0500 Subject: [PATCH 244/363] Added isolated assembly loading for issue #714 --- .github/workflows/dotnet-buildandtest.yml | 2 +- .gitignore | 5 + .../DeferredDependencyAssemblyLoadContext.cs | 87 +++++ src/Mapster.Tool/Program.cs | 347 +++++++++++++----- 4 files changed, 339 insertions(+), 102 deletions(-) create mode 100644 src/Mapster.Tool/DeferredDependencyAssemblyLoadContext.cs diff --git a/.github/workflows/dotnet-buildandtest.yml b/.github/workflows/dotnet-buildandtest.yml index 7e30a04a..37dc390a 100644 --- a/.github/workflows/dotnet-buildandtest.yml +++ b/.github/workflows/dotnet-buildandtest.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.x.x - name: Show dotnet version run: | dotnet --list-sdks diff --git a/.gitignore b/.gitignore index 83a5664f..9c0a14cd 100644 --- a/.gitignore +++ b/.gitignore @@ -181,3 +181,8 @@ project.lock.json /src/.vs /.vs src/.idea + +# VS Code settings +.vscode/launch.json +.vscode/settings.json +.vscode/tasks.json diff --git a/src/Mapster.Tool/DeferredDependencyAssemblyLoadContext.cs b/src/Mapster.Tool/DeferredDependencyAssemblyLoadContext.cs new file mode 100644 index 00000000..09132d22 --- /dev/null +++ b/src/Mapster.Tool/DeferredDependencyAssemblyLoadContext.cs @@ -0,0 +1,87 @@ +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace Mapster.Tool +{ + // + // Summary: + // Used for loading an assembly and its dependencies in an isolated assembly load context but deferring the resolution of + // a subset of those assemblies to an already existing Assembly Load Context (likely the AssemblyLoadContext.Default + // context that is used by the runtime by default at startup) + public class DeferredDependencyAssemblyLoadContext : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver resolver; + private readonly ImmutableHashSet deferredDependencyAssemblyNames; + private readonly AssemblyLoadContext deferToContext; + + public DeferredDependencyAssemblyLoadContext( + string assemblyPath, + AssemblyLoadContext deferToContext, + params AssemblyName[] deferredDependencyAssemblyNames + ) + { + // set up a resolver for the dependencies of this non-deferred assembly + resolver = new AssemblyDependencyResolver(assemblyPath); + + // store all of the assembly simple names that should be deferred w/ + // the sharing assembly context loader (and not resolved exclusively in this loader) + this.deferredDependencyAssemblyNames = deferredDependencyAssemblyNames + .Select(an => an.Name!) + .Where(n => n != null) + .ToImmutableHashSet(); + + // store a reference to the assembly load context that assembly resolution will be deferred + // to when on the deferredDependencyAssemblyNames list + this.deferToContext = deferToContext; + + // load the non-deferred assembly in this context to start + Load(GetAssemblyName(assemblyPath)); + } + + protected override Assembly? Load(AssemblyName assemblyName) + { + if (assemblyName.Name == null) + { + return null; + } + + // if the assembly to be loaded is also set to be deferrred (based on constructor) + // then first attempt to load it from the sharing assembly load context + if (deferredDependencyAssemblyNames.Contains(assemblyName.Name)) + { + return deferToContext.LoadFromAssemblyName(assemblyName); + } + + // all other loaded assemblies should be considered dependencies of the + // non-deferred dependency loaded in the constructor and should be loaded + // from its path (the AssemblyDepedencyResolver resolves dependency paths) + string? assemblyPath = resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath == null) + { + return null; + } + + return LoadFromAssemblyPath(assemblyPath); + } + + public static Assembly LoadAssemblyFrom( + string assemblyPath, + AssemblyLoadContext deferToContext, + params AssemblyName[] deferredDependencyAssemblyNames + ) + { + DeferredDependencyAssemblyLoadContext loadContext = + new DeferredDependencyAssemblyLoadContext( + assemblyPath, + deferToContext, + deferredDependencyAssemblyNames + ); + return loadContext.LoadFromAssemblyName( + new AssemblyName(Path.GetFileNameWithoutExtension(assemblyPath)) + ); + } + } +} diff --git a/src/Mapster.Tool/Program.cs b/src/Mapster.Tool/Program.cs index 4066ef52..633f23ed 100644 --- a/src/Mapster.Tool/Program.cs +++ b/src/Mapster.Tool/Program.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.Loader; using System.Text; using CommandLine; using ExpressionDebugger; @@ -16,7 +17,8 @@ class Program { static void Main(string[] args) { - Parser.Default.ParseArguments(args) + Parser.Default + .ParseArguments(args) .WithParsed(GenerateMappers) .WithParsed(GenerateModels) .WithParsed(GenerateExtensions); @@ -39,9 +41,13 @@ static void Main(string[] args) private static string GetOutput(string baseOutput, string? segment, string typeName) { var fullBasePath = Path.GetFullPath(baseOutput); - return segment == null - ? Path.Combine(fullBasePath, typeName + ".g.cs") - : Path.Combine(fullBasePath, segment.Replace('.', Path.DirectorySeparatorChar), typeName + ".g.cs"); + return segment == null + ? Path.Combine(fullBasePath, typeName + ".g.cs") + : Path.Combine( + fullBasePath, + segment.Replace('.', Path.DirectorySeparatorChar), + typeName + ".g.cs" + ); } private static void WriteFile(string code, string path) @@ -60,7 +66,26 @@ private static void WriteFile(string code, string path) private static void GenerateMappers(MapperOptions opt) { - var assembly = Assembly.LoadFrom(Path.GetFullPath(opt.Assembly)); + // We want loaded assemblies that we're scanning to be isolated from our currently + // running assembly load context in order to avoid type/framework collisions between Mapster assemblies + // and their dependencies and the scanned assemblies and their dependencies + + // However, we also need *some* of those scanned assemblies and thus their types to resolve from our + // currently running AssemblyLoadContext.Default: The Mapster assembly basically. + + // This way when we compare attribute types (such as MapperAttribute) between our running assembly + // and the scanned assembly the two types with the same FullName can be considered equal because + // they both were resolved from AssemblyLoadContext.Default. + + // This isolated Assembly Load Context will be able to resolve the Mapster assembly, but + // the resolved Assembly will be the same one that is in AssemblyLoadContext.Default + // (the runtime assembly load context that our code refers to by default when referencing + // types) + var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( + assemblyPath: Path.GetFullPath(opt.Assembly), + deferToContext: AssemblyLoadContext.Default, + deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + ); var config = TypeAdapterConfig.GlobalSettings; config.SelfContainedCodeGeneration = true; config.Scan(assembly); @@ -78,7 +103,7 @@ private static void GenerateMappers(MapperOptions opt) var segments = GetSegments(type.Namespace, opt.BaseNamespace); var definitions = new TypeDefinitions { - Implements = new[] {type}, + Implements = new[] { type }, Namespace = CreateNamespace(opt.Namespace, segments, type.Namespace), TypeName = attr.Name ?? GetImplName(GetCodeFriendlyTypeName(type)), IsInternal = attr.IsInternal, @@ -88,7 +113,9 @@ private static void GenerateMappers(MapperOptions opt) var path = GetOutput(opt.Output, segments, definitions.TypeName); if (opt.SkipExistingFiles && File.Exists(path)) { - Console.WriteLine($"Skipped: {type.FullName}. Mapper {definitions.TypeName} already exists."); + Console.WriteLine( + $"Skipped: {type.FullName}. Mapper {definitions.TypeName} already exists." + ); continue; } @@ -110,8 +137,11 @@ private static void GenerateMappers(MapperOptions opt) var funcArgs = propArgs.GetGenericArguments(); var tuple = new TypeTuple(funcArgs[0], funcArgs[1]); var expr = config.CreateMapExpression(tuple, MapType.Projection); - translator.VisitLambda(expr, ExpressionTranslator.LambdaType.PublicLambda, - prop.Name); + translator.VisitLambda( + expr, + ExpressionTranslator.LambdaType.PublicLambda, + prop.Name + ); } } @@ -127,16 +157,21 @@ private static void GenerateMappers(MapperOptions opt) if (methodArgs.Length < 1 || methodArgs.Length > 2) continue; var tuple = new TypeTuple(methodArgs[0].ParameterType, method.ReturnType); - var expr = config.CreateMapExpression(tuple, - methodArgs.Length == 1 ? MapType.Map : MapType.MapToTarget); - translator.VisitLambda(expr, ExpressionTranslator.LambdaType.PublicMethod, - method.Name); + var expr = config.CreateMapExpression( + tuple, + methodArgs.Length == 1 ? MapType.Map : MapType.MapToTarget + ); + translator.VisitLambda( + expr, + ExpressionTranslator.LambdaType.PublicMethod, + method.Name + ); } } - var code = opt.GenerateNullableDirective ? - $"#nullable enable{Environment.NewLine}{translator}" : - translator.ToString(); + var code = opt.GenerateNullableDirective + ? $"#nullable enable{Environment.NewLine}{translator}" + : translator.ToString(); WriteFile(code, path); } } @@ -150,7 +185,11 @@ private static string GetImplName(string name) private static void GenerateModels(ModelOptions opt) { - var assembly = Assembly.LoadFrom(Path.GetFullPath(opt.Assembly)); + var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( + assemblyPath: Path.GetFullPath(opt.Assembly), + deferToContext: AssemblyLoadContext.Default, + deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + ); var codeGenConfig = new CodeGenerationConfig(); codeGenConfig.Scan(assembly); @@ -165,7 +204,11 @@ private static void GenerateModels(ModelOptions opt) foreach (var type in types) { var builders = type.GetAdaptAttributeBuilders(codeGenConfig) - .Where(it => !string.IsNullOrEmpty(it.Attribute.Name) && it.Attribute.Name != "[name]") + .Where( + it => + !string.IsNullOrEmpty(it.Attribute.Name) + && it.Attribute.Name != "[name]" + ) .ToList(); if (builders.Count == 0) continue; @@ -182,10 +225,13 @@ private static void GenerateModels(ModelOptions opt) { var nilCtxAttr = type.GetCustomAttributesData() .FirstOrDefault(it => it.AttributeType.Name == "NullableContextAttribute"); - return nilCtxAttr?.ConstructorArguments.Count == 1 && nilCtxAttr.ConstructorArguments[0].Value is byte b - ? (byte?) b + return + nilCtxAttr?.ConstructorArguments.Count == 1 + && nilCtxAttr.ConstructorArguments[0].Value is byte b + ? (byte?)b : null; } + private static void CreateModel(ModelOptions opt, Type type, AdaptAttributeBuilder builder) { var segments = GetSegments(type.Namespace, opt.BaseNamespace); @@ -202,7 +248,9 @@ private static void CreateModel(ModelOptions opt, Type type, AdaptAttributeBuild var path = GetOutput(opt.Output, segments, definitions.TypeName); if (opt.SkipExistingFiles && File.Exists(path)) { - Console.WriteLine($"Skipped: {type.FullName}. Model {definitions.TypeName} already exists."); + Console.WriteLine( + $"Skipped: {type.FullName}. Model {definitions.TypeName} already exists." + ); return; } @@ -210,33 +258,43 @@ private static void CreateModel(ModelOptions opt, Type type, AdaptAttributeBuild var isAdaptTo = attr is AdaptToAttribute; var isTwoWays = attr is AdaptTwoWaysAttribute; var side = isAdaptTo ? MemberSide.Source : MemberSide.Destination; - var properties = type.GetFieldsAndProperties().Where(it => - !it.SafeGetCustomAttributes().OfType() - .Any(it2 => isTwoWays || it2.Side == null || it2.Side == side)); + var properties = type.GetFieldsAndProperties() + .Where( + it => + !it.SafeGetCustomAttributes() + .OfType() + .Any(it2 => isTwoWays || it2.Side == null || it2.Side == side) + ); if (attr.IgnoreAttributes != null) { - properties = properties.Where(it => - !it.SafeGetCustomAttributes() - .Select(it2 => it2.GetType()) - .Intersect(attr.IgnoreAttributes) - .Any()); + properties = properties.Where( + it => + !it.SafeGetCustomAttributes() + .Select(it2 => it2.GetType()) + .Intersect(attr.IgnoreAttributes) + .Any() + ); } if (attr.IgnoreNoAttributes != null) { - properties = properties.Where(it => - it.SafeGetCustomAttributes() - .Select(it2 => it2.GetType()) - .Intersect(attr.IgnoreNoAttributes) - .Any()); + properties = properties.Where( + it => + it.SafeGetCustomAttributes() + .Select(it2 => it2.GetType()) + .Intersect(attr.IgnoreNoAttributes) + .Any() + ); } if (attr.IgnoreNamespaces != null) { foreach (var ns in attr.IgnoreNamespaces) { - properties = properties.Where(it => getPropType(it).Namespace?.StartsWith(ns) != true); + properties = properties.Where( + it => getPropType(it).Namespace?.StartsWith(ns) != true + ); } } @@ -248,47 +306,74 @@ private static void CreateModel(ModelOptions opt, Type type, AdaptAttributeBuild var setting = propSettings?.GetValueOrDefault(member.Name); if (setting?.Ignore == true) continue; - + var adaptMember = member.GetCustomAttribute(); if (!isTwoWays && adaptMember?.Side != null && adaptMember.Side != side) adaptMember = null; - var propType = setting?.MapFunc?.ReturnType ?? - setting?.TargetPropertyType ?? - GetPropertyType(member, getPropType(member), attr.GetType(), opt.Namespace, builder); - var nilAttr = member.GetCustomAttributesData() + var propType = + setting?.MapFunc?.ReturnType + ?? setting?.TargetPropertyType + ?? GetPropertyType( + member, + getPropType(member), + attr.GetType(), + opt.Namespace, + builder + ); + var nilAttr = member + .GetCustomAttributesData() .FirstOrDefault(it => it.AttributeType.Name == "NullableAttribute"); - var nilAttrArg = nilAttr?.ConstructorArguments.Count == 1 ? nilAttr.ConstructorArguments[0].Value : null; - translator.Properties.Add(new PropertyDefinitions - { - Name = setting?.TargetPropertyName ?? adaptMember?.Name ?? member.Name, - Type = isNullable ? propType.MakeNullable() : propType, - IsReadOnly = isReadOnly, - NullableContext = nilAttrArg is byte b ? (byte?)b : null, - Nullable = nilAttrArg is byte[] bytes ? bytes : null, - }); + var nilAttrArg = + nilAttr?.ConstructorArguments.Count == 1 + ? nilAttr.ConstructorArguments[0].Value + : null; + translator.Properties.Add( + new PropertyDefinitions + { + Name = setting?.TargetPropertyName ?? adaptMember?.Name ?? member.Name, + Type = isNullable ? propType.MakeNullable() : propType, + IsReadOnly = isReadOnly, + NullableContext = nilAttrArg is byte b ? (byte?)b : null, + Nullable = nilAttrArg is byte[] bytes ? bytes : null, + } + ); } - var code = opt.GenerateNullableDirective ? - $"#nullable enable{Environment.NewLine}{translator}" : - translator.ToString(); + var code = opt.GenerateNullableDirective + ? $"#nullable enable{Environment.NewLine}{translator}" + : translator.ToString(); WriteFile(code, path); static Type getPropType(MemberInfo mem) { - return mem is PropertyInfo p ? p.PropertyType : ((FieldInfo) mem).FieldType; + return mem is PropertyInfo p ? p.PropertyType : ((FieldInfo)mem).FieldType; } } - private static readonly Dictionary _mockTypes = new Dictionary(); - private static Type GetPropertyType(MemberInfo member, Type propType, Type attrType, string? ns, AdaptAttributeBuilder builder) + private static readonly Dictionary _mockTypes = + new Dictionary(); + + private static Type GetPropertyType( + MemberInfo member, + Type propType, + Type attrType, + string? ns, + AdaptAttributeBuilder builder + ) { - var navAttr = member.SafeGetCustomAttributes() + var navAttr = member + .SafeGetCustomAttributes() .OfType() .FirstOrDefault(it => it.ForAttributes?.Contains(attrType) != false); if (navAttr != null) return navAttr.Type; - if (propType.IsCollection() && propType.IsCollectionCompatible() && propType.IsGenericType && propType.GetGenericArguments().Length == 1) + if ( + propType.IsCollection() + && propType.IsCollectionCompatible() + && propType.IsGenericType + && propType.GetGenericArguments().Length == 1 + ) { var elementType = propType.GetGenericArguments()[0]; var newType = GetPropertyType(member, elementType, attrType, ns, builder); @@ -305,14 +390,16 @@ private static Type GetPropertyType(MemberInfo member, Type propType, Type attrT return alterType; var propTypeAttrs = propType.SafeGetCustomAttributes(); - navAttr = propTypeAttrs.OfType() + navAttr = propTypeAttrs + .OfType() .FirstOrDefault(it => it.ForAttributes?.Contains(attrType) != false); if (navAttr != null) return navAttr.Type; var adaptAttr = builder.TypeSettings.ContainsKey(propType) - ? (BaseAdaptAttribute?) builder.Attribute - : propTypeAttrs.OfType() + ? (BaseAdaptAttribute?)builder.Attribute + : propTypeAttrs + .OfType() .FirstOrDefault(it => it.GetType() == attrType); if (adaptAttr == null) return propType; @@ -330,7 +417,7 @@ private static Type GetPropertyType(MemberInfo member, Type propType, Type attrT private static Type? GetFromType(Type type, BaseAdaptAttribute attr, HashSet types) { - if (!(attr is AdaptFromAttribute) && !(attr is AdaptTwoWaysAttribute)) + if (!(attr is AdaptFromAttribute) && !(attr is AdaptTwoWaysAttribute)) return null; var fromType = attr.Type; @@ -345,7 +432,7 @@ private static Type GetPropertyType(MemberInfo member, Type propType, Type attrT private static Type? GetToType(Type type, BaseAdaptAttribute attr, HashSet types) { - if (!(attr is AdaptToAttribute)) + if (!(attr is AdaptToAttribute)) return null; var toType = attr.Type; @@ -358,19 +445,25 @@ private static Type GetPropertyType(MemberInfo member, Type propType, Type attrT return toType; } - private static void ApplySettings(TypeAdapterSetter setter, BaseAdaptAttribute attr, Dictionary settings) + private static void ApplySettings( + TypeAdapterSetter setter, + BaseAdaptAttribute attr, + Dictionary settings + ) { setter.ApplyAdaptAttribute(attr); foreach (var (name, setting) in settings) { if (setting.MapFunc != null) { - setter.Settings.Resolvers.Add(new InvokerModel - { - DestinationMemberName = setting.TargetPropertyName ?? name, - SourceMemberName = name, - Invoker = setting.MapFunc, - }); + setter.Settings.Resolvers.Add( + new InvokerModel + { + DestinationMemberName = setting.TargetPropertyName ?? name, + SourceMemberName = name, + Invoker = setting.MapFunc, + } + ); } else if (setting.TargetPropertyName != null) { @@ -381,14 +474,18 @@ private static void ApplySettings(TypeAdapterSetter setter, BaseAdaptAttribute a private static void GenerateExtensions(ExtensionOptions opt) { - var assembly = Assembly.LoadFrom(Path.GetFullPath(opt.Assembly)); + var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( + assemblyPath: Path.GetFullPath(opt.Assembly), + deferToContext: AssemblyLoadContext.Default, + deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + ); var config = TypeAdapterConfig.GlobalSettings; config.SelfContainedCodeGeneration = true; config.Scan(assembly); var codeGenConfig = new CodeGenerationConfig(); codeGenConfig.Scan(assembly); - var assemblies = new HashSet {assembly}; + var assemblies = new HashSet { assembly }; foreach (var builder in codeGenConfig.AdaptAttributeBuilders) { foreach (var setting in builder.TypeSettings) @@ -399,7 +496,8 @@ private static void GenerateExtensions(ExtensionOptions opt) var types = assemblies.SelectMany(it => it.GetLoadableTypes()).ToHashSet(); // assemblies defines open generic only, so we have to add specialised types used in mappings - foreach (var (key, _) in config.RuleMap) types.Add(key.Source); + foreach (var (key, _) in config.RuleMap) + types.Add(key.Source); var configDict = new Dictionary(); foreach (var builder in codeGenConfig.AdaptAttributeBuilders) { @@ -423,8 +521,9 @@ private static void GenerateExtensions(ExtensionOptions opt) { var mapperAttr = type.GetGenerateMapperAttributes(codeGenConfig).FirstOrDefault(); var ruleMaps = config.RuleMap - .Where(it => it.Key.Source == type && - it.Value.Settings.GenerateMapper is MapType) + .Where( + it => it.Key.Source == type && it.Value.Settings.GenerateMapper is MapType + ) .ToList(); if (mapperAttr == null && ruleMaps.Count == 0) continue; @@ -452,7 +551,9 @@ private static void GenerateExtensions(ExtensionOptions opt) var path = GetOutput(opt.Output, segments, definitions.TypeName); if (opt.SkipExistingFiles && File.Exists(path)) { - Console.WriteLine($"Skipped: {type.FullName}. Extension class {definitions.TypeName} already exists."); + Console.WriteLine( + $"Skipped: {type.FullName}. Extension class {definitions.TypeName} already exists." + ); continue; } @@ -466,67 +567,109 @@ private static void GenerateExtensions(ExtensionOptions opt) if (fromType != null) { var tuple = new TypeTuple(fromType, type); - var mapType = attr.MapType == 0 ? MapType.Map | MapType.MapToTarget : attr.MapType; - GenerateExtensionMethods(mapType, cloned, tuple, translator, type, mapperAttr.IsHelperClass); + var mapType = + attr.MapType == 0 ? MapType.Map | MapType.MapToTarget : attr.MapType; + GenerateExtensionMethods( + mapType, + cloned, + tuple, + translator, + type, + mapperAttr.IsHelperClass + ); } var toType = GetToType(type, attr, types); if (toType != null && (!(attr is AdaptTwoWaysAttribute) || type != toType)) { var tuple = new TypeTuple(type, toType); - var mapType = attr.MapType == 0 - ? MapType.Map | MapType.MapToTarget - : attr.MapType; - GenerateExtensionMethods(mapType, cloned, tuple, translator, type, mapperAttr.IsHelperClass); + var mapType = + attr.MapType == 0 ? MapType.Map | MapType.MapToTarget : attr.MapType; + GenerateExtensionMethods( + mapType, + cloned, + tuple, + translator, + type, + mapperAttr.IsHelperClass + ); } } foreach (var (tuple, rule) in ruleMaps) { - var mapType = (MapType) rule.Settings.GenerateMapper!; - GenerateExtensionMethods(mapType, config, tuple, translator, type, mapperAttr.IsHelperClass); + var mapType = (MapType)rule.Settings.GenerateMapper!; + GenerateExtensionMethods( + mapType, + config, + tuple, + translator, + type, + mapperAttr.IsHelperClass + ); } - var code = opt.GenerateNullableDirective ? - $"#nullable enable{Environment.NewLine}{translator}" : - translator.ToString(); + var code = opt.GenerateNullableDirective + ? $"#nullable enable{Environment.NewLine}{translator}" + : translator.ToString(); WriteFile(code, path); } } - private static void GenerateExtensionMethods(MapType mapType, TypeAdapterConfig config, TypeTuple tuple, - ExpressionTranslator translator, Type entityType, bool isHelperClass) + private static void GenerateExtensionMethods( + MapType mapType, + TypeAdapterConfig config, + TypeTuple tuple, + ExpressionTranslator translator, + Type entityType, + bool isHelperClass + ) { //add type name to prevent duplication translator.Translate(entityType); var destName = GetCodeFriendlyTypeName(tuple.Destination); - var name = tuple.Destination.Name == entityType.Name - ? destName - : destName.Replace(entityType.Name, ""); + var name = + tuple.Destination.Name == entityType.Name + ? destName + : destName.Replace(entityType.Name, ""); if ((mapType & MapType.Map) > 0) { var expr = config.CreateMapExpression(tuple, MapType.Map); - translator.VisitLambda(expr, isHelperClass ? ExpressionTranslator.LambdaType.PublicMethod : ExpressionTranslator.LambdaType.ExtensionMethod, - "AdaptTo" + name); + translator.VisitLambda( + expr, + isHelperClass + ? ExpressionTranslator.LambdaType.PublicMethod + : ExpressionTranslator.LambdaType.ExtensionMethod, + "AdaptTo" + name + ); } if ((mapType & MapType.MapToTarget) > 0) { var expr2 = config.CreateMapExpression(tuple, MapType.MapToTarget); - translator.VisitLambda(expr2, isHelperClass ? ExpressionTranslator.LambdaType.PublicMethod : ExpressionTranslator.LambdaType.ExtensionMethod, - "AdaptTo"); + translator.VisitLambda( + expr2, + isHelperClass + ? ExpressionTranslator.LambdaType.PublicMethod + : ExpressionTranslator.LambdaType.ExtensionMethod, + "AdaptTo" + ); } if ((mapType & MapType.Projection) > 0) { var proj = config.CreateMapExpression(tuple, MapType.Projection); - translator.VisitLambda(proj, ExpressionTranslator.LambdaType.PublicLambda, - "ProjectTo" + name); + translator.VisitLambda( + proj, + ExpressionTranslator.LambdaType.PublicLambda, + "ProjectTo" + name + ); } } - private static string GetCodeFriendlyTypeName(Type type) => GetCodeFriendlyTypeName(new StringBuilder(), type).ToString(); + private static string GetCodeFriendlyTypeName(Type type) => + GetCodeFriendlyTypeName(new StringBuilder(), type).ToString(); private static StringBuilder GetCodeFriendlyTypeName(StringBuilder sb, Type type) { @@ -544,7 +687,8 @@ private static StringBuilder GetCodeFriendlyTypeName(StringBuilder sb, Type type var name = type.Name; var i = name.IndexOf('`'); - if (i>0) name = name.Remove(i); + if (i > 0) + name = name.Remove(i); name = name switch { "SByte" => "Sbyte", @@ -559,8 +703,9 @@ private static StringBuilder GetCodeFriendlyTypeName(StringBuilder sb, Type type _ => name, }; - if (!string.IsNullOrEmpty(name)) sb.Append(name); + if (!string.IsNullOrEmpty(name)) + sb.Append(name); return sb; } } -} \ No newline at end of file +} From e457bd7fef2f2c2cff531b81c8ce170286656d94 Mon Sep 17 00:00:00 2001 From: Casey Boyle Date: Mon, 17 Jun 2024 10:21:28 -0500 Subject: [PATCH 245/363] added tv specific nuget stuff --- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/nuget.config | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/nuget.config diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index d7921c64..850e0ade 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -10,7 +10,7 @@ Mapster;Tool true Mapster.Tool.snk - 8.4.1-pre01 + 8.4.1-pre02tv enable diff --git a/src/nuget.config b/src/nuget.config new file mode 100644 index 00000000..63cde4d4 --- /dev/null +++ b/src/nuget.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file From a8f11ebc0bce8b79520e6157bc91d1f1c97c4e1c Mon Sep 17 00:00:00 2001 From: Casey Boyle Date: Mon, 17 Jun 2024 15:14:49 -0500 Subject: [PATCH 246/363] added additional assembly to shared assemblies --- src/Mapster.Tool/Program.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Mapster.Tool/Program.cs b/src/Mapster.Tool/Program.cs index 86085b89..335c8093 100644 --- a/src/Mapster.Tool/Program.cs +++ b/src/Mapster.Tool/Program.cs @@ -79,7 +79,8 @@ private static void GenerateMappers(MapperOptions opt) var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( assemblyPath: Path.GetFullPath(opt.Assembly), deferToContext: AssemblyLoadContext.Default, - deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + typeof(MapperAttribute).Assembly.GetName(), + typeof(IRegister).Assembly.GetName() ); var config = TypeAdapterConfig.GlobalSettings; config.SelfContainedCodeGeneration = true; @@ -173,7 +174,8 @@ private static void GenerateModels(ModelOptions opt) var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( assemblyPath: Path.GetFullPath(opt.Assembly), deferToContext: AssemblyLoadContext.Default, - deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + typeof(MapperAttribute).Assembly.GetName(), + typeof(IRegister).Assembly.GetName() ); var codeGenConfig = new CodeGenerationConfig(); codeGenConfig.Scan(assembly); @@ -408,7 +410,8 @@ private static void GenerateExtensions(ExtensionOptions opt) var assembly = DeferredDependencyAssemblyLoadContext.LoadAssemblyFrom( assemblyPath: Path.GetFullPath(opt.Assembly), deferToContext: AssemblyLoadContext.Default, - deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() + typeof(MapperAttribute).Assembly.GetName(), + typeof(IRegister).Assembly.GetName() ); var config = TypeAdapterConfig.GlobalSettings; config.SelfContainedCodeGeneration = true; From 86fd5c4722c6569f52572d40e5a0642e71638a9f Mon Sep 17 00:00:00 2001 From: Casey Boyle Date: Mon, 17 Jun 2024 15:44:36 -0500 Subject: [PATCH 247/363] fix --- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster.Tool/Program.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 850e0ade..d7921c64 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -10,7 +10,7 @@ Mapster;Tool true Mapster.Tool.snk - 8.4.1-pre02tv + 8.4.1-pre01 enable diff --git a/src/Mapster.Tool/Program.cs b/src/Mapster.Tool/Program.cs index d8852a54..1347cb13 100644 --- a/src/Mapster.Tool/Program.cs +++ b/src/Mapster.Tool/Program.cs @@ -481,7 +481,6 @@ private static void GenerateExtensions(ExtensionOptions opt) deferToContext: AssemblyLoadContext.Default, typeof(MapperAttribute).Assembly.GetName(), typeof(IRegister).Assembly.GetName() - deferredDependencyAssemblyNames: typeof(MapperAttribute).Assembly.GetName() ); var config = TypeAdapterConfig.GlobalSettings; config.SelfContainedCodeGeneration = true; From 81af1a74450c317f30a9848bd9030fac7f25e5a9 Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Sun, 21 Jul 2024 17:51:15 +0200 Subject: [PATCH 248/363] Updated Basic usages (markdown) --- Basic-usages.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Basic-usages.md b/Basic-usages.md index 8fa56942..2b351653 100644 --- a/Basic-usages.md +++ b/Basic-usages.md @@ -17,6 +17,9 @@ sourceObject.Adapt(destObject); Mapster also provides extensions to map queryables. +> [!IMPORTANT] +> Avoid calling ProjectToType() before materializing queries from Entity Framework. This is known to cause issues. Instead, call ToList() or ToListAsync() before calling ProjectToType. + ```csharp using (MyDbContext context = new MyDbContext()) { From c14b57d683ca8f2169de3f175981ec580e2c841f Mon Sep 17 00:00:00 2001 From: Vasil Kotsev <9307969+SonnyRR@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:41:14 +0200 Subject: [PATCH 249/363] Bump TFMs to net9.0 --- src/Benchmark/Benchmark.csproj | 2 +- src/ExpressionDebugger/ExpressionDebugger.csproj | 2 +- src/ExpressionTranslator/ExpressionTranslator.csproj | 2 +- src/Mapster.Async.Tests/Mapster.Async.Tests.csproj | 2 +- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.Tests.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj | 2 +- src/Mapster.Tests/Mapster.Tests.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- src/Sample.AspNetCore/Sample.AspNetCore.csproj | 2 +- src/Sample.CodeGen/Sample.CodeGen.csproj | 2 +- src/TemplateTest/TemplateTest.csproj | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Benchmark/Benchmark.csproj b/src/Benchmark/Benchmark.csproj index 739dc721..3126f66c 100644 --- a/src/Benchmark/Benchmark.csproj +++ b/src/Benchmark/Benchmark.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net9.0 true **/*.g.cs diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index 8b5e81f6..893935cb 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0;net8.0; + net6.0;net7.0;net8.0;net9.0 True Chaowlert Chaisrichalermpol Step into debugging from linq expressions diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index 0bad5ffc..e95fb311 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0;net8.0; + net6.0;net7.0;net8.0;net9.0 True Chaowlert Chaisrichalermpol Translate from linq expressions to C# code diff --git a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj index 835d8547..54c31706 100644 --- a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj +++ b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 false diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index 9ec82a66..9014fe4a 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 Async supports for Mapster true Mapster;Async diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index 5bfd0a54..9f4a5ca3 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -1,7 +1,7 @@  Lightweight library for Mapster and Mapster CodeGen - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 Mapster.Core mapster 1.2.2-pre01 diff --git a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj index 4f02a26a..eaa2b455 100644 --- a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj +++ b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 false diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index f806c551..03aee824 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 Dependency Injection supports for Mapster true Mapster;DependencyInjection diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index 2f4b1b25..4e541f94 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 EF6 plugin for Mapster true Mapster;EF6 diff --git a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj index e0a55450..c9e90904 100644 --- a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj +++ b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 false diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index db82d4b6..1365c342 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 EFCore plugin for Mapster true Mapster;EFCore diff --git a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj index bbb9f594..0dc9f5ed 100644 --- a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj +++ b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 false diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index 6bfa1057..96e8b8ba 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 Immutable collection supports for Mapster true Mapster;Immutable diff --git a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj index 1841f5c6..4ef7a51a 100644 --- a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj +++ b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 false diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 0101057d..b33237f1 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 Json.net conversion supports for Mapster true Mapster;Json.net diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index 74dbf23e..12f37027 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 Source generator to generate mapping using Mapster source-generator;mapster true diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index 4121ac36..3420614d 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -1,7 +1,7 @@ - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 false Mapster.Tests Mapster.Tests.snk diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index d7921c64..36873d8c 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -2,7 +2,7 @@ Exe - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 true true dotnet-mapster diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index 60ff3663..126e46b4 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -4,7 +4,7 @@ A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Copyright (c) 2016 Chaowlert Chaisrichalermpol, Eric Swann chaowlert;eric_swann - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 Mapster A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Mapster diff --git a/src/Sample.AspNetCore/Sample.AspNetCore.csproj b/src/Sample.AspNetCore/Sample.AspNetCore.csproj index 7cf57a89..dc0cfe42 100644 --- a/src/Sample.AspNetCore/Sample.AspNetCore.csproj +++ b/src/Sample.AspNetCore/Sample.AspNetCore.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 diff --git a/src/Sample.CodeGen/Sample.CodeGen.csproj b/src/Sample.CodeGen/Sample.CodeGen.csproj index f288e135..7bac508e 100644 --- a/src/Sample.CodeGen/Sample.CodeGen.csproj +++ b/src/Sample.CodeGen/Sample.CodeGen.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 enable diff --git a/src/TemplateTest/TemplateTest.csproj b/src/TemplateTest/TemplateTest.csproj index 8a3bc1b3..42b929e4 100644 --- a/src/TemplateTest/TemplateTest.csproj +++ b/src/TemplateTest/TemplateTest.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0 + net9.0;net8.0;net7.0;net6.0 enable false From 3cf84e2cad2f959db9120232e7789690b93eea70 Mon Sep 17 00:00:00 2001 From: Vasil Kotsev <9307969+SonnyRR@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:44:10 +0200 Subject: [PATCH 250/363] Update dotnet version in github workflow --- .github/workflows/dotnet-buildandtest.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dotnet-buildandtest.yml b/.github/workflows/dotnet-buildandtest.yml index 7e30a04a..ac4265d0 100644 --- a/.github/workflows/dotnet-buildandtest.yml +++ b/.github/workflows/dotnet-buildandtest.yml @@ -12,16 +12,16 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: 9 - name: Show dotnet version run: | dotnet --list-sdks dotnet --list-runtimes - name: Build with dotnet run: dotnet build ./src/Mapster.sln - - name: Run tests on .NET 8.0 + - name: Run tests on .NET 9.0 run: dotnet test --verbosity normal ./src/Mapster.sln From 36f24314a0e4057db09085096ffa165a273da8f4 Mon Sep 17 00:00:00 2001 From: Vasil Kotsev <9307969+SonnyRR@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:50:41 +0200 Subject: [PATCH 251/363] Deprecate net7.0 TFM --- src/ExpressionDebugger/ExpressionDebugger.csproj | 2 +- src/ExpressionTranslator/ExpressionTranslator.csproj | 2 +- src/Mapster.Async.Tests/Mapster.Async.Tests.csproj | 2 +- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.Tests.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj | 2 +- src/Mapster.Tests/Mapster.Tests.csproj | 2 +- src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- src/Sample.AspNetCore/Sample.AspNetCore.csproj | 2 +- src/Sample.CodeGen/Sample.CodeGen.csproj | 2 +- src/TemplateTest/TemplateTest.csproj | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index 893935cb..28f2c3da 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0;net8.0;net9.0 + net6.0;net8.0;net9.0 True Chaowlert Chaisrichalermpol Step into debugging from linq expressions diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index e95fb311..6ac8fda4 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0;net8.0;net9.0 + net6.0;net8.0;net9.0 True Chaowlert Chaisrichalermpol Translate from linq expressions to C# code diff --git a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj index 54c31706..00a9f72b 100644 --- a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj +++ b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 false diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index 9014fe4a..2167451d 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 Async supports for Mapster true Mapster;Async diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index 9f4a5ca3..ff9e130e 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -1,7 +1,7 @@  Lightweight library for Mapster and Mapster CodeGen - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 Mapster.Core mapster 1.2.2-pre01 diff --git a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj index eaa2b455..2e6eeeb9 100644 --- a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj +++ b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 false diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 03aee824..8ecfc6a4 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 Dependency Injection supports for Mapster true Mapster;DependencyInjection diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index 4e541f94..a13f3699 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 EF6 plugin for Mapster true Mapster;EF6 diff --git a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj index c9e90904..bfb504e9 100644 --- a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj +++ b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 false diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index 1365c342..abfe4f8e 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 EFCore plugin for Mapster true Mapster;EFCore diff --git a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj index 0dc9f5ed..75b6f350 100644 --- a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj +++ b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 false diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index 96e8b8ba..ab5d7b60 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 Immutable collection supports for Mapster true Mapster;Immutable diff --git a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj index 4ef7a51a..3b33a4b3 100644 --- a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj +++ b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 false diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index b33237f1..0c2747e0 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 Json.net conversion supports for Mapster true Mapster;Json.net diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index 12f37027..6f8788a5 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 Source generator to generate mapping using Mapster source-generator;mapster true diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index 3420614d..5a1a658d 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -1,7 +1,7 @@ - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 false Mapster.Tests Mapster.Tests.snk diff --git a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj index f5797195..93196575 100644 --- a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj +++ b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0 + net6.0 enable enable diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 36873d8c..04f5969c 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -2,7 +2,7 @@ Exe - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 true true dotnet-mapster diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index 126e46b4..869e60ed 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -4,7 +4,7 @@ A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Copyright (c) 2016 Chaowlert Chaisrichalermpol, Eric Swann chaowlert;eric_swann - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 Mapster A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Mapster diff --git a/src/Sample.AspNetCore/Sample.AspNetCore.csproj b/src/Sample.AspNetCore/Sample.AspNetCore.csproj index dc0cfe42..9b2d5f0f 100644 --- a/src/Sample.AspNetCore/Sample.AspNetCore.csproj +++ b/src/Sample.AspNetCore/Sample.AspNetCore.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 diff --git a/src/Sample.CodeGen/Sample.CodeGen.csproj b/src/Sample.CodeGen/Sample.CodeGen.csproj index 7bac508e..f422010c 100644 --- a/src/Sample.CodeGen/Sample.CodeGen.csproj +++ b/src/Sample.CodeGen/Sample.CodeGen.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 enable diff --git a/src/TemplateTest/TemplateTest.csproj b/src/TemplateTest/TemplateTest.csproj index 42b929e4..eefd774c 100644 --- a/src/TemplateTest/TemplateTest.csproj +++ b/src/TemplateTest/TemplateTest.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net7.0;net6.0 + net9.0;net8.0;net6.0 enable false From 8c7927e32748c6bd7b80321e877d55851833e6a7 Mon Sep 17 00:00:00 2001 From: Vasil Kotsev <9307969+SonnyRR@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:56:57 +0200 Subject: [PATCH 252/363] Deprecate net6.0 TFM --- src/ExpressionDebugger/ExpressionDebugger.csproj | 2 +- src/ExpressionTranslator/ExpressionTranslator.csproj | 2 +- src/Mapster.Async.Tests/Mapster.Async.Tests.csproj | 2 +- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.Tests.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj | 2 +- src/Mapster.Tests/Mapster.Tests.csproj | 2 +- src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- src/Sample.AspNetCore/Sample.AspNetCore.csproj | 2 +- src/Sample.CodeGen/Sample.CodeGen.csproj | 2 +- src/TemplateTest/TemplateTest.csproj | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index 28f2c3da..68b79170 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -1,7 +1,7 @@  - net6.0;net8.0;net9.0 + net8.0;net9.0 True Chaowlert Chaisrichalermpol Step into debugging from linq expressions diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index 6ac8fda4..4224fd66 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -1,7 +1,7 @@  - net6.0;net8.0;net9.0 + net8.0;net9.0 True Chaowlert Chaisrichalermpol Translate from linq expressions to C# code diff --git a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj index 00a9f72b..3bee234c 100644 --- a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj +++ b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; false diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index 2167451d..30408dac 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; Async supports for Mapster true Mapster;Async diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index ff9e130e..dcf1ba3d 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -1,7 +1,7 @@  Lightweight library for Mapster and Mapster CodeGen - net9.0;net8.0;net6.0 + net9.0;net8.0; Mapster.Core mapster 1.2.2-pre01 diff --git a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj index 2e6eeeb9..8cf12944 100644 --- a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj +++ b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; false diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 8ecfc6a4..10ad2411 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; Dependency Injection supports for Mapster true Mapster;DependencyInjection diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index a13f3699..ad098d29 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; EF6 plugin for Mapster true Mapster;EF6 diff --git a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj index bfb504e9..f5f469c9 100644 --- a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj +++ b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; false diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index abfe4f8e..7b689cc4 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; EFCore plugin for Mapster true Mapster;EFCore diff --git a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj index 75b6f350..e8fbaa8c 100644 --- a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj +++ b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; false diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index ab5d7b60..dc3efc35 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; Immutable collection supports for Mapster true Mapster;Immutable diff --git a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj index 3b33a4b3..76da936f 100644 --- a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj +++ b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; false diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 0c2747e0..735adaf7 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; Json.net conversion supports for Mapster true Mapster;Json.net diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index 6f8788a5..6c4e48ed 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; Source generator to generate mapping using Mapster source-generator;mapster true diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index 5a1a658d..dbb5c8c3 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -1,7 +1,7 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0; false Mapster.Tests Mapster.Tests.snk diff --git a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj index 93196575..6ae4b77c 100644 --- a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj +++ b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net9.0;net8.0 enable enable diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 04f5969c..6cc2f108 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -2,7 +2,7 @@ Exe - net9.0;net8.0;net6.0 + net9.0;net8.0; true true dotnet-mapster diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index 869e60ed..384b44b0 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -4,7 +4,7 @@ A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Copyright (c) 2016 Chaowlert Chaisrichalermpol, Eric Swann chaowlert;eric_swann - net9.0;net8.0;net6.0 + net9.0;net8.0; Mapster A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Mapster diff --git a/src/Sample.AspNetCore/Sample.AspNetCore.csproj b/src/Sample.AspNetCore/Sample.AspNetCore.csproj index 9b2d5f0f..cc9df6a8 100644 --- a/src/Sample.AspNetCore/Sample.AspNetCore.csproj +++ b/src/Sample.AspNetCore/Sample.AspNetCore.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; diff --git a/src/Sample.CodeGen/Sample.CodeGen.csproj b/src/Sample.CodeGen/Sample.CodeGen.csproj index f422010c..a0fad4a4 100644 --- a/src/Sample.CodeGen/Sample.CodeGen.csproj +++ b/src/Sample.CodeGen/Sample.CodeGen.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; enable diff --git a/src/TemplateTest/TemplateTest.csproj b/src/TemplateTest/TemplateTest.csproj index eefd774c..048df767 100644 --- a/src/TemplateTest/TemplateTest.csproj +++ b/src/TemplateTest/TemplateTest.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0;net6.0 + net9.0;net8.0; enable false From 07005f3a1109c38ba6bd4cf86a4d36ad1f535fda Mon Sep 17 00:00:00 2001 From: Vasil Kotsev <9307969+SonnyRR@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:11:33 +0200 Subject: [PATCH 253/363] Bump NuGet packages --- src/Benchmark/Benchmark.csproj | 6 +++--- src/ExpressionDebugger/ExpressionDebugger.csproj | 2 +- src/ExpressionTranslator/ExpressionTranslator.csproj | 2 +- src/Mapster.Async.Tests/Mapster.Async.Tests.csproj | 8 ++++---- .../Mapster.DependencyInjection.Tests.csproj | 10 +++++----- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj | 12 ++++++------ src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- .../Mapster.Immutable.Tests.csproj | 10 +++++----- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- .../Mapster.JsonNet.Tests.csproj | 8 ++++---- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- .../Mapster.SourceGenerator.csproj | 4 ++-- src/Mapster.Tests/Mapster.Tests.csproj | 10 +++++----- src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj | 12 ++++++------ src/Mapster.Tool/Mapster.Tool.csproj | 4 ++-- src/Sample.AspNetCore/Sample.AspNetCore.csproj | 8 ++++---- src/Sample.CodeGen/Sample.CodeGen.csproj | 10 +++++----- src/TemplateTest/TemplateTest.csproj | 11 +++++++---- 20 files changed, 65 insertions(+), 62 deletions(-) diff --git a/src/Benchmark/Benchmark.csproj b/src/Benchmark/Benchmark.csproj index 3126f66c..ba4b6b9f 100644 --- a/src/Benchmark/Benchmark.csproj +++ b/src/Benchmark/Benchmark.csproj @@ -19,11 +19,11 @@ - + - - + + diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index 68b79170..3b5154fb 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index 4224fd66..04b402f7 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -26,6 +26,6 @@ - + diff --git a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj index 3bee234c..f2f0bbf1 100644 --- a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj +++ b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj index 8cf12944..53f9a5be 100644 --- a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj +++ b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 10ad2411..fce93857 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index ad098d29..a39075f2 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -11,7 +11,7 @@ 2.0.2-pre01 - + diff --git a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj index f5f469c9..d3729dfd 100644 --- a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj +++ b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj @@ -7,15 +7,15 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index 7b689cc4..5bcc9b72 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj index e8fbaa8c..ca5fda1f 100644 --- a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj +++ b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj @@ -7,14 +7,14 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index dc3efc35..50334372 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj index 76da936f..2b0c5b6c 100644 --- a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj +++ b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 735adaf7..09952203 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index 6c4e48ed..6ddef117 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index dbb5c8c3..1fd66a01 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -9,11 +9,11 @@ true - - - - - + + + + + diff --git a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj index 6ae4b77c..e440ff10 100644 --- a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj +++ b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj @@ -9,15 +9,15 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 6cc2f108..13e9195f 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/src/Sample.AspNetCore/Sample.AspNetCore.csproj b/src/Sample.AspNetCore/Sample.AspNetCore.csproj index cc9df6a8..c24c9428 100644 --- a/src/Sample.AspNetCore/Sample.AspNetCore.csproj +++ b/src/Sample.AspNetCore/Sample.AspNetCore.csproj @@ -6,10 +6,10 @@ - - - - + + + + diff --git a/src/Sample.CodeGen/Sample.CodeGen.csproj b/src/Sample.CodeGen/Sample.CodeGen.csproj index a0fad4a4..9c0bf793 100644 --- a/src/Sample.CodeGen/Sample.CodeGen.csproj +++ b/src/Sample.CodeGen/Sample.CodeGen.csproj @@ -6,11 +6,11 @@ - - - - - + + + + + diff --git a/src/TemplateTest/TemplateTest.csproj b/src/TemplateTest/TemplateTest.csproj index 048df767..96caad44 100644 --- a/src/TemplateTest/TemplateTest.csproj +++ b/src/TemplateTest/TemplateTest.csproj @@ -8,10 +8,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From b0655f80d34caad5cf562a870e636602ab59a96d Mon Sep 17 00:00:00 2001 From: Vasil Kotsev <9307969+SonnyRR@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:25:25 +0200 Subject: [PATCH 254/363] Re-introduce net8 SDK in gh workflow --- .github/workflows/dotnet-buildandtest.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-buildandtest.yml b/.github/workflows/dotnet-buildandtest.yml index ac4265d0..89798aba 100644 --- a/.github/workflows/dotnet-buildandtest.yml +++ b/.github/workflows/dotnet-buildandtest.yml @@ -16,7 +16,9 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 9 + dotnet-version: | + 8.x + 9.x - name: Show dotnet version run: | dotnet --list-sdks From eeaf355199b6ee85e4639215dbdacf1d21ce496a Mon Sep 17 00:00:00 2001 From: Vasil Kotsev <9307969+SonnyRR@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:38:44 +0200 Subject: [PATCH 255/363] Bump package semvers --- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index 30408dac..c98231f4 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -7,7 +7,7 @@ Mapster;Async true Mapster.Async.snk - 2.0.2-pre01 + 2.0.3-pre01 diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index dcf1ba3d..a57b880d 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -4,7 +4,7 @@ net9.0;net8.0; Mapster.Core mapster - 1.2.2-pre01 + 1.2.3-pre01 enable true true diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index fce93857..23cb1b76 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -7,7 +7,7 @@ Mapster;DependencyInjection true Mapster.DependencyInjection.snk - 1.0.2-pre01 + 1.0.3-pre01 diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index a39075f2..9ce95d04 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -8,7 +8,7 @@ True true Mapster.EF6.snk - 2.0.2-pre01 + 2.0.3-pre01 diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index 5bcc9b72..11e97230 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -8,7 +8,7 @@ True true Mapster.EFCore.snk - 5.1.2-pre01 + 5.1.3-pre01 diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index 50334372..e7598f83 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -7,7 +7,7 @@ Mapster;Immutable true Mapster.Immutable.snk - 1.0.2-pre01 + 1.0.3-pre01 enable diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 09952203..3eb615ff 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -7,7 +7,7 @@ Mapster;Json.net true Mapster.JsonNet.snk - 1.1.2-pre01 + 1.1.3-pre01 diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 13e9195f..2e590e02 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -10,7 +10,7 @@ Mapster;Tool true Mapster.Tool.snk - 8.4.1-pre01 + 8.4.2-pre01 enable diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index 384b44b0..b208e106 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -16,7 +16,7 @@ true Mapster - 7.4.1-pre01 + 7.4.2-pre01 enable 1701;1702;8618 From c8e3320d089c93b75ab350cacd98cc8d7353c237 Mon Sep 17 00:00:00 2001 From: Casey Boyle Date: Thu, 2 Jan 2025 16:09:39 -0600 Subject: [PATCH 256/363] Delete src/nuget.config --- src/nuget.config | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/nuget.config diff --git a/src/nuget.config b/src/nuget.config deleted file mode 100644 index 63cde4d4..00000000 --- a/src/nuget.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file From cd6ebe54701f60c171b58075b45db836226ba6bc Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 3 Jan 2025 08:22:50 +0500 Subject: [PATCH 257/363] fix merge conflicts from PR #645 --- .../WhenMappingRecordRegression.cs | 107 +++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 37bde9c4..f68cd6f9 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -41,7 +41,7 @@ public void AdaptRecordStructToRecordStruct() var _structResult = _sourceStruct.Adapt(_destinationStruct); _structResult.X.ShouldBe(1000); - _structResult.X.ShouldNotBe(_destinationStruct.X); + _destinationStruct.X.Equals(_structResult.X).ShouldBeFalse(); } [TestMethod] @@ -227,6 +227,56 @@ public void DetectFakeRecord() _destination.X.ShouldBe(200); object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } + + [TestMethod] + public void OnlyInlineRecordWorked() + { + var _sourcePoco = new InlinePoco501() { MyInt = 1 , MyString = "Hello" }; + var _sourceOnlyInitRecord = new OnlyInitRecord501 { MyInt = 2, MyString = "Hello World" }; + + var _resultOnlyinitRecord = _sourcePoco.Adapt(); + var _updateResult = _sourceOnlyInitRecord.Adapt(_resultOnlyinitRecord); + + _resultOnlyinitRecord.MyInt.ShouldBe(1); + _resultOnlyinitRecord.MyString.ShouldBe("Hello"); + _updateResult.MyInt.ShouldBe(2); + _updateResult.MyString.ShouldBe("Hello World"); + } + + [TestMethod] + public void MultyCtorRecordWorked() + { + var _sourcePoco = new InlinePoco501() { MyInt = 1, MyString = "Hello" }; + var _sourceMultyCtorRecord = new MultiCtorRecord (2, "Hello World"); + + var _resultMultyCtorRecord = _sourcePoco.Adapt(); + var _updateResult = _sourceMultyCtorRecord.Adapt(_resultMultyCtorRecord); + + _resultMultyCtorRecord.MyInt.ShouldBe(1); + _resultMultyCtorRecord.MyString.ShouldBe("Hello"); + _updateResult.MyInt.ShouldBe(2); + _updateResult.MyString.ShouldBe("Hello World"); + } + + [TestMethod] + public void MultiCtorAndInlineRecordWorked() + { + var _sourcePoco = new MultiCtorAndInlinePoco() { MyInt = 1, MyString = "Hello", MyEmail = "123@gmail.com", InitData="Test"}; + var _sourceMultiCtorAndInline = new MultiCtorAndInlineRecord(2, "Hello World") { InitData = "Worked", MyEmail = "243@gmail.com" }; + + var _resultMultiCtorAndInline = _sourcePoco.Adapt(); + var _updateResult = _sourceMultiCtorAndInline.Adapt(_resultMultiCtorAndInline); + + _resultMultiCtorAndInline.MyInt.ShouldBe(1); + _resultMultiCtorAndInline.MyString.ShouldBe("Hello"); + _resultMultiCtorAndInline.MyEmail.ShouldBe("123@gmail.com"); + _resultMultiCtorAndInline.InitData.ShouldBe("Test"); + _updateResult.MyInt.ShouldBe(2); + _updateResult.MyString.ShouldBe("Hello World"); + _updateResult.MyEmail.ShouldBe("243@gmail.com"); + _updateResult.InitData.ShouldBe("Worked"); + } + #region NowNotWorking @@ -255,6 +305,61 @@ public void CollectionUpdate() #region TestClasses + class MultiCtorAndInlinePoco + { + public int MyInt { get; set; } + public string MyString { get; set; } + public string MyEmail { get; set; } + public string InitData { get; set; } + } + + record MultiCtorAndInlineRecord + { + public MultiCtorAndInlineRecord(int myInt) + { + MyInt = myInt; + } + + public MultiCtorAndInlineRecord(int myInt, string myString) : this(myInt) + { + MyString = myString; + } + + + public int MyInt { get; private set; } + public string MyString { get; private set; } + public string MyEmail { get; set; } + public string InitData { get; init; } + } + + record MultiCtorRecord + { + public MultiCtorRecord(int myInt) + { + MyInt = myInt; + } + + public MultiCtorRecord(int myInt, string myString) : this(myInt) + { + MyString = myString; + } + + public int MyInt { get; private set; } + public string MyString { get; private set; } + } + + class InlinePoco501 + { + public int MyInt { get; set; } + public string MyString { get; set; } + } + + record OnlyInitRecord501 + { + public int MyInt { get; init; } + public string MyString { get; init; } + } + class PocoWithGuid { public Guid Id { get; init; } From cea3debb0267343f231a2f1179b808be2e24526d Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 4 Jan 2025 19:41:14 +0500 Subject: [PATCH 258/363] Fix issue #672 --- src/Mapster/Adapters/ClassAdapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index adf1c60c..407c5a5b 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -183,7 +183,7 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre var typeofExpression = Expression.Constant(member.Destination!.Type); var getPropertyMethod = typeof(Type).GetMethod("GetProperty", new[] { typeof(string) })!; var getPropertyExpression = Expression.Call(typeofExpression, getPropertyMethod, - Expression.Constant(member.DestinationMember.Name)); + Expression.Constant(member.DestinationMember.Name, member.DestinationMember.Type)); var setValueMethod = typeof(PropertyInfo).GetMethod("SetValue", new[] { typeof(object), typeof(object) })!; var memberAsObject = adapt.To(typeof(object)); From 4deea1a08701d2741ecbf0f366196204dc33e2c9 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 4 Jan 2025 20:15:20 +0500 Subject: [PATCH 259/363] add mapping init PropertyTest --- src/Mapster.Tests/WhenMappingInitProperty.cs | 48 ++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/Mapster.Tests/WhenMappingInitProperty.cs diff --git a/src/Mapster.Tests/WhenMappingInitProperty.cs b/src/Mapster.Tests/WhenMappingInitProperty.cs new file mode 100644 index 00000000..469fb9f8 --- /dev/null +++ b/src/Mapster.Tests/WhenMappingInitProperty.cs @@ -0,0 +1,48 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; + +namespace Mapster.Tests; + +[TestClass] +public class WhenMappingInitProperty +{ + + #region Tests + /// + /// From Issue #672 + /// https://github.com/MapsterMapper/Mapster/issues/672 + /// + [TestMethod] + public void WhenMappingToHiddenandNewInitFieldDestination() + { + var source = new Source672() { Id = 156}; + var c = source.Adapt(); + var s = source.Adapt(new BDestination()); + + ((ADestination)c).Id.ShouldBe(156); + s.Id.ShouldBe(156); + } + + #endregion Tests + + + #region TestClasses + + class Source672 + { + public long Id { get; init; } + } + + class ADestination + { + public int Id { get; init; } + } + + class BDestination : ADestination + { + public new long Id { get; init; } + } + + + #endregion TestClasses +} From a8baff8d2a13f3ab319012b2aec1369e846a3af6 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 4 Jan 2025 20:38:01 +0500 Subject: [PATCH 260/363] add Constract Using Test --- src/Mapster.Tests/WhenMappingInitProperty.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Mapster.Tests/WhenMappingInitProperty.cs b/src/Mapster.Tests/WhenMappingInitProperty.cs index 469fb9f8..4fda1295 100644 --- a/src/Mapster.Tests/WhenMappingInitProperty.cs +++ b/src/Mapster.Tests/WhenMappingInitProperty.cs @@ -23,6 +23,21 @@ public void WhenMappingToHiddenandNewInitFieldDestination() s.Id.ShouldBe(156); } + [TestMethod] + public void WhenMappingToHiddenandNewInitFieldWithConstructUsing() + { + TypeAdapterConfig.NewConfig().ConstructUsing(_ => new BDestination()); + + + var source = new Source672() { Id = 256 }; + var c = source.Adapt(); + var s = source.Adapt(new BDestination()); + + ((ADestination)c).Id.ShouldBe(256); + s.Id.ShouldBe(256); + } + + #endregion Tests From 2c430e9adb1ac9bd71a77eaadea2f4487bd34bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20=C5=A0t=C3=A1gl?= Date: Tue, 7 Jan 2025 11:43:13 +0100 Subject: [PATCH 261/363] fix regression in interfaces mapping --- src/Mapster.Tests/WhenMappingToInterface.cs | 59 +++++++++++++++++++ .../Adapters/ReadOnlyInterfaceAdapter.cs | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingToInterface.cs b/src/Mapster.Tests/WhenMappingToInterface.cs index 5e6f0358..00af2104 100644 --- a/src/Mapster.Tests/WhenMappingToInterface.cs +++ b/src/Mapster.Tests/WhenMappingToInterface.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json.Serialization; +using Newtonsoft.Json; namespace Mapster.Tests { @@ -266,6 +268,27 @@ public void MappingToInteraceWithReadonlyProps_AllPropsInitialized() ); } + [TestMethod] + public void MappingToInterface_VerifyReadonlyPropsInterfaceRule() + { + SampleInterfaceCls source = new SampleInterfaceCls + { + ActivityData = new SampleActivityData + { + Data = new SampleActivityParsedData + { + Steps = new List { "A", "B", "C" } + } + } + }; + + SampleInterfaceCls target = source.Adapt(); + target.ShouldNotBeNull(); + target.ShouldSatisfyAllConditions( + () => target.ActivityData.ShouldBe(source.ActivityData) + ); + } + public interface IInheritedDtoWithoutProperties : IInheritedDto { } @@ -374,6 +397,42 @@ public class PropertyInitializationTestSource public int Property1 { get; set; } public int Property2 { get; set; } } + + public interface IActivityData + { + + } + + public class SampleInterfaceCls + { + [Newtonsoft.Json.JsonIgnore] + public IActivityData? ActivityData { get; set; } + + public SampleInterfaceCls() + { + + } + + public SampleInterfaceCls(IActivityData data) + { + SetActivityData(data); + } + + public void SetActivityData(IActivityData data) + { + ActivityData = data; + } + } + + public class SampleActivityData : IActivityData + { + public SampleActivityParsedData Data { get; set; } + } + + public class SampleActivityParsedData + { + public List Steps { get; set; } = new List(); + } } } diff --git a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs index df4a0f66..fbd5a78b 100644 --- a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs +++ b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs @@ -10,7 +10,7 @@ internal class ReadOnlyInterfaceAdapter : ClassAdapter protected override bool CanMap(PreCompileArgument arg) { - return arg.DestinationType.IsInterface; + return arg.DestinationType.IsInterface && arg.DestinationType.GetProperties().Length > 0; } protected override bool CanInline(Expression source, Expression? destination, CompileArgument arg) From 93a2d3fe65814b7101b07cdf4c7b7947a892ef2c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 10 Jan 2025 07:04:24 +0500 Subject: [PATCH 262/363] Fix issue #755 - revert to behavior from 7.4.0 --- .../WhenMappingRecordRegression.cs | 184 ++++++++++++++++++ .../Adapters/ReadOnlyInterfaceAdapter.cs | 20 ++ 2 files changed, 204 insertions(+) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index f68cd6f9..534eb2dd 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -278,6 +278,83 @@ public void MultiCtorAndInlineRecordWorked() } + [TestMethod] + public void MappingInterfaceToInterface() + { + TypeAdapterConfig + .ForType() + .Map(dest => dest.TempLength, src => src.Temp.Length); + + + var sourceBase = new SampleInterfaceClsBase + { + ActivityData = new SampleActivityData + { + Data = new SampleActivityParsedData + { + Steps = new List { "A", "B", "C" } + }, + Temp = "Temp data" + + } + + }; + var sourceDerived = new SampleInterfaceClsDerived + { + ActivityData = new SampleActivityData + { + Data = new SampleActivityParsedData + { + Steps = new List { "X", "Y", "Z" } + }, + Temp = "Update Temp data" + + } + + }; + + var sourceExt = new SampleInterfaceClsExtentions + { + ActivityData = new SampleActivityDataExtentions + { + Data = new SampleActivityParsedData + { + Steps = new List { "o", "o", "o" } + }, + Temp = "Extentions data", + TempLength = "Extentions data".Length + + } + + }; + + var TargetBase = sourceBase.Adapt(); + var targetDerived = sourceDerived.Adapt(); + var update = targetDerived.Adapt(TargetBase); + + var targetExtention = sourceExt.Adapt(); + + + var updExt = targetDerived.Adapt(targetExtention); + + targetDerived.ShouldNotBeNull(); + targetDerived.ShouldSatisfyAllConditions( + () => targetDerived.ActivityData.ShouldBe(sourceDerived.ActivityData), + () => update.ActivityData.ShouldBe(targetDerived.ActivityData), + + ()=> updExt.ActivityData.ShouldBe(targetExtention.ActivityData), + () => ((SampleActivityDataExtentions)updExt.ActivityData).Temp.ShouldBe(sourceDerived.ActivityData.Temp), + () => ((SampleActivityDataExtentions)updExt.ActivityData).TempLength.ShouldBe(sourceDerived.ActivityData.Temp.Length), + // All implementations of the IActivityData interface do not provide access to the Data property for all implementations of the SampleActivityData class, + // so this property will not be changed by mapping + () => ((SampleActivityDataExtentions)updExt.ActivityData).Data.ShouldBe(((SampleActivityDataExtentions)targetExtention.ActivityData).Data) + + ); + + } + + + #region NowNotWorking /// @@ -305,6 +382,104 @@ public void CollectionUpdate() #region TestClasses + public interface IActivityDataExtentions : IActivityData + { + public int TempLength { get; set; } + } + + public interface IActivityData : IActivityDataBase + { + public string Temp { get; set; } + } + + public interface IActivityDataBase + { + + } + + + public class SampleInterfaceClsExtentions + { + public IActivityDataExtentions? ActivityData { get; set; } + + public SampleInterfaceClsExtentions() + { + + } + + public SampleInterfaceClsExtentions(IActivityDataExtentions data) + { + SetActivityData(data); + } + + public void SetActivityData(IActivityDataExtentions data) + { + ActivityData = data; + } + } + + + + public class SampleInterfaceClsBase + { + public IActivityDataBase? ActivityData { get; set; } + + public SampleInterfaceClsBase() + { + + } + + public SampleInterfaceClsBase(IActivityDataBase data) + { + SetActivityData(data); + } + + public void SetActivityData(IActivityDataBase data) + { + ActivityData = data; + } + } + + public class SampleInterfaceClsDerived + { + public IActivityData? ActivityData { get; set; } + + public SampleInterfaceClsDerived() + { + + } + + public SampleInterfaceClsDerived(IActivityData data) + { + SetActivityData(data); + } + + public void SetActivityData(IActivityData data) + { + ActivityData = data; + } + } + + public class SampleActivityDataExtentions : IActivityDataExtentions + { + public SampleActivityParsedData Data { get; set; } + public string Temp { get; set; } + public int TempLength { get; set; } + } + + public class SampleActivityData : IActivityData + { + public SampleActivityParsedData Data { get; set; } + public string Temp { get; set; } + } + + public class SampleActivityParsedData + { + public List Steps { get; set; } = new List(); + } + + + class MultiCtorAndInlinePoco { public int MyInt { get; set; } @@ -499,5 +674,14 @@ sealed record TestSealedRecord() sealed record TestSealedRecordPositional(int X); + + + + + + + + + #endregion TestClasses } diff --git a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs index df4a0f66..f81a1f74 100644 --- a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs +++ b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs @@ -11,6 +11,7 @@ internal class ReadOnlyInterfaceAdapter : ClassAdapter protected override bool CanMap(PreCompileArgument arg) { return arg.DestinationType.IsInterface; + } protected override bool CanInline(Expression source, Expression? destination, CompileArgument arg) @@ -45,5 +46,24 @@ protected override Expression CreateInstantiationExpression(Expression source, E return base.CreateInstantiationExpression(source,destination, arg); } + protected override Expression CreateExpressionBody(Expression source, Expression? destination, CompileArgument arg) + { + if (source.Type.IsInterface) + { + if (!arg.DestinationType.IsAssignableFrom(arg.SourceType)) + return base.CreateExpressionBody(source, destination, arg); + + if (arg.MapType != MapType.MapToTarget) + return Expression.Convert(source, arg.DestinationType); + + if (arg.MapType == MapType.MapToTarget) + return source; + + return base.CreateExpressionBody(source, destination, arg); + } + + return base.CreateExpressionBody(source, destination, arg); + } + } } From 424134258b330b5cb9484d098944c314dd58b893 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 11 Jan 2025 21:34:17 +0500 Subject: [PATCH 263/363] fix comment --- src/Mapster.Tests/WhenMappingRecordRegression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 534eb2dd..e5d719e4 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -345,7 +345,7 @@ public void MappingInterfaceToInterface() ()=> updExt.ActivityData.ShouldBe(targetExtention.ActivityData), () => ((SampleActivityDataExtentions)updExt.ActivityData).Temp.ShouldBe(sourceDerived.ActivityData.Temp), () => ((SampleActivityDataExtentions)updExt.ActivityData).TempLength.ShouldBe(sourceDerived.ActivityData.Temp.Length), - // All implementations of the IActivityData interface do not provide access to the Data property for all implementations of the SampleActivityData class, + // IActivityData interface and all its derivatives do not provide access to the Data property for all implementations of the SampleActivityData class, // so this property will not be changed by mapping () => ((SampleActivityDataExtentions)updExt.ActivityData).Data.ShouldBe(((SampleActivityDataExtentions)targetExtention.ActivityData).Data) From a4bda388d42ef522c9740ae7ad965a3d413006cc Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 13 Jan 2025 15:29:14 +0500 Subject: [PATCH 264/363] fix nullable map to target regression --- src/Mapster/TypeAdapter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 9389a9a2..0ac37924 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -96,8 +96,8 @@ public static TDestination Adapt(this TSource source, TDe /// Adapted destination type. public static TDestination Adapt(this TSource source, TDestination destination, TypeAdapterConfig config) { - var sourceType = source.GetType(); - var destinationType = destination.GetType(); + var sourceType = source?.GetType(); + var destinationType = destination?.GetType(); if (sourceType == typeof(object)) // Infinity loop in ObjectAdapter if Runtime Type of source is Object return destination; From 44b078ce604467126424ddf9def4b88c0d8cbc22 Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Mon, 13 Jan 2025 15:33:42 +0100 Subject: [PATCH 265/363] Update version numbers. --- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index c98231f4..d8b90332 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -7,7 +7,7 @@ Mapster;Async true Mapster.Async.snk - 2.0.3-pre01 + 2.0.3-pre02 diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index a57b880d..c926ae84 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -4,7 +4,7 @@ net9.0;net8.0; Mapster.Core mapster - 1.2.3-pre01 + 1.2.3-pre02 enable true true diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 23cb1b76..1bd37229 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -7,7 +7,7 @@ Mapster;DependencyInjection true Mapster.DependencyInjection.snk - 1.0.3-pre01 + 1.0.3-pre02 diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index 9ce95d04..bb101550 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -8,7 +8,7 @@ True true Mapster.EF6.snk - 2.0.3-pre01 + 2.0.3-pre02 diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index 11e97230..2d846efa 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -8,7 +8,7 @@ True true Mapster.EFCore.snk - 5.1.3-pre01 + 5.1.3-pre02 diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index e7598f83..10d26ff8 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -7,7 +7,7 @@ Mapster;Immutable true Mapster.Immutable.snk - 1.0.3-pre01 + 1.0.3-pre02 enable diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 3eb615ff..97ec21b8 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -7,7 +7,7 @@ Mapster;Json.net true Mapster.JsonNet.snk - 1.1.3-pre01 + 1.1.3-pre02 diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 2e590e02..6ecc3c60 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -10,7 +10,7 @@ Mapster;Tool true Mapster.Tool.snk - 8.4.2-pre01 + 8.4.2-pre02 enable diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index b208e106..acd8f99a 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -16,7 +16,7 @@ true Mapster - 7.4.2-pre01 + 7.4.2-pre02 enable 1701;1702;8618 From 623f9405a6a90e9cb9c3de901ceb116bdddcae2b Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 21 Jan 2025 14:38:39 +0500 Subject: [PATCH 266/363] add Ignor Ctor Param --- src/Mapster/Adapters/BaseClassAdapter.cs | 30 ++++++++++++++++--- src/Mapster/Adapters/ClassAdapter.cs | 6 ++-- .../Adapters/ReadOnlyInterfaceAdapter.cs | 2 +- src/Mapster/Adapters/RecordTypeAdapter.cs | 4 +-- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index faa490ec..c0ee6a46 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -15,7 +15,7 @@ internal abstract class BaseClassAdapter : BaseAdapter #region Build the Adapter Model - protected ClassMapping CreateClassConverter(Expression source, ClassModel classModel, CompileArgument arg, Expression? destination = null) + protected ClassMapping CreateClassConverter(Expression source, ClassModel classModel, CompileArgument arg, Expression? destination = null, bool CtorMapping = false) { var destinationMembers = classModel.Members; var unmappedDestinationMembers = new List(); @@ -29,7 +29,7 @@ src is LambdaExpression lambda : ExpressionEx.PropertyOrFieldPath(source, (string)src))); foreach (var destinationMember in destinationMembers) { - if (ProcessIgnores(arg, destinationMember, out var ignore)) + if (ProcessIgnores(arg, destinationMember, out var ignore) && !CtorMapping) continue; var resolvers = arg.Settings.ValueAccessingStrategies.AsEnumerable(); @@ -80,7 +80,8 @@ select fn(src, destinationMember, arg)) { if (classModel.BreakOnUnmatched) return null!; - unmappedDestinationMembers.Add(destinationMember.Name); + if(!arg.Settings.Ignore.Any(x=>x.Key == destinationMember.Name)) // Don't mark a constructor parameter if it was explicitly ignored + unmappedDestinationMembers.Add(destinationMember.Name); } properties.Add(propertyModel); @@ -128,7 +129,7 @@ protected static bool ProcessIgnores( && ignore.Condition == null; } - protected Expression CreateInstantiationExpression(Expression source, ClassMapping classConverter, CompileArgument arg) + protected Expression CreateInstantiationExpression(Expression source, ClassMapping classConverter, CompileArgument arg, Expression? destination) { var members = classConverter.Members; @@ -156,6 +157,27 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi var condition = ExpressionEx.Not(body); getter = Expression.Condition(condition, getter, defaultConst); } + else + if (arg.Settings.Ignore.Count != 0) + { + if (arg.MapType != MapType.MapToTarget && arg.Settings.Ignore.Any(x => x.Key == member.DestinationMember.Name)) + getter = defaultConst; + + if (arg.MapType == MapType.MapToTarget && arg.DestinationType.IsRecordType()) + { + if (arg.Settings.Ignore.Any(x => x.Key == member.DestinationMember.Name)) + { + var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() + .Where(x => x.Name == member.DestinationMember.Name).FirstOrDefault(); + + if (find != null) + getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); + } + else + getter = defaultConst; + } + + } } arguments.Add(getter); } diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 407c5a5b..d953f11a 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -69,19 +69,19 @@ protected override Expression CreateInstantiationExpression(Expression source, E classConverter = destType.GetConstructors() .OrderByDescending(it => it.GetParameters().Length) .Select(it => GetConstructorModel(it, true)) - .Select(it => CreateClassConverter(source, it, arg)) + .Select(it => CreateClassConverter(source, it, arg, CtorMapping:true)) .FirstOrDefault(it => it != null); } else { var model = GetConstructorModel(ctor, false); - classConverter = CreateClassConverter(source, model, arg); + classConverter = CreateClassConverter(source, model, arg, CtorMapping:true); } if (classConverter == null) return base.CreateInstantiationExpression(source, destination, arg); - return CreateInstantiationExpression(source, classConverter, arg); + return CreateInstantiationExpression(source, classConverter, arg, destination); } protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) diff --git a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs index 3703c281..bf097fb5 100644 --- a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs +++ b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs @@ -39,7 +39,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E var ctor = destType.GetConstructors()[0]; var classModel = GetConstructorModel(ctor, false); var classConverter = CreateClassConverter(source, classModel, arg); - return CreateInstantiationExpression(source, classConverter, arg); + return CreateInstantiationExpression(source, classConverter, arg, destination); } else return base.CreateInstantiationExpression(source,destination, arg); diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 009af932..718000f4 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -31,8 +31,8 @@ protected override Expression CreateInstantiationExpression(Expression source, E var ctor = destType.GetConstructors() .OrderByDescending(it => it.GetParameters().Length).ToArray().FirstOrDefault(); // Will be used public constructor with the maximum number of parameters var classModel = GetConstructorModel(ctor, false); - var classConverter = CreateClassConverter(source, classModel, arg); - var installExpr = CreateInstantiationExpression(source, classConverter, arg); + var classConverter = CreateClassConverter(source, classModel, arg, CtorMapping:true); + var installExpr = CreateInstantiationExpression(source, classConverter, arg, destination); return RecordInlineExpression(source, arg, installExpr); // Activator field when not include in public ctor } From 5e9b8dffedf7cf847e2c35e522676d787686095d Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 21 Jan 2025 16:31:00 +0500 Subject: [PATCH 267/363] add Test --- src/Mapster.Tests/WhenIgnoreMapping.cs | 67 +++++++++++++++++++ .../WhenMappingRecordRegression.cs | 24 +++++++ 2 files changed, 91 insertions(+) diff --git a/src/Mapster.Tests/WhenIgnoreMapping.cs b/src/Mapster.Tests/WhenIgnoreMapping.cs index 585e911c..6398ef35 100644 --- a/src/Mapster.Tests/WhenIgnoreMapping.cs +++ b/src/Mapster.Tests/WhenIgnoreMapping.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Reflection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using Shouldly; @@ -55,6 +57,69 @@ public void TestIgnoreMember() poco2.Name.ShouldBeNull(); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/707 + /// + [TestMethod] + public void WhenClassIgnoreCtorParamGetDefaultValue() + { + var config = new TypeAdapterConfig() + { + RequireDestinationMemberSource = true, + }; + config.Default + .NameMatchingStrategy(new NameMatchingStrategy + { + SourceMemberNameConverter = input => input.ToLowerInvariant(), + DestinationMemberNameConverter = input => input.ToLowerInvariant(), + }) + ; + config + .NewConfig() + .MapToConstructor(GetConstructor()) + .Ignore(e => e.Id); + + var source = new A707 { Text = "test" }; + var dest = new B707(123, "Hello"); + + var docKind = source.Adapt(config); + var mapTotarget = source.Adapt(dest,config); + + docKind.Id.ShouldBe(0); + mapTotarget.Id.ShouldBe(123); + mapTotarget.Text.ShouldBe("test"); + } + + #region TestClasses + static ConstructorInfo? GetConstructor() + { + var parameterlessCtorInfo = typeof(TDestination).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, new Type[0]); + + var ctors = typeof(TDestination).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + var validCandidateCtors = ctors.Except(new[] { parameterlessCtorInfo }).ToArray(); + var ctorToUse = validCandidateCtors.Length == 1 + ? validCandidateCtors.First() + : validCandidateCtors.OrderByDescending(c => c.GetParameters().Length).First(); + + return ctorToUse; + } + public class A707 + { + public string? Text { get; set; } + } + + public class B707 + { + public int Id { get; private set; } + public string Text { get; private set; } + + public B707(int id, string text) + { + Id = id; + Text = text; + } + } + public class Poco { public Guid Id { get; set; } @@ -67,5 +132,7 @@ public class Dto [JsonIgnore] public string Name { get; set; } } + + #endregion TestClasses } } diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index e5d719e4..f4658e85 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -353,6 +353,25 @@ public void MappingInterfaceToInterface() } + /// + /// https://github.com/MapsterMapper/Mapster/issues/456 + /// + [TestMethod] + public void WhenRecordReceivedIgnoreCtorParamProcessing() + { + TypeAdapterConfig.NewConfig() + .Ignore(dest => dest.Name); + + var userDto = new UserDto456("Amichai"); + var user = new UserRecord456("John"); + + var map = userDto.Adapt(); + var maptoTarget = userDto.Adapt(user); + + map.Name.ShouldBeNullOrEmpty(); + maptoTarget.Name.ShouldBe("John"); + } + #region NowNotWorking @@ -382,6 +401,11 @@ public void CollectionUpdate() #region TestClasses + + public record UserRecord456(string Name); + + public record UserDto456(string Name); + public interface IActivityDataExtentions : IActivityData { public int TempLength { get; set; } From 0685bb31ad4175295292761fab993e78cf0db2b7 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 22 Jan 2025 14:13:16 +0500 Subject: [PATCH 268/363] refactoring algorithm --- src/Mapster/Adapters/BaseClassAdapter.cs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index c0ee6a46..67e3caf4 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -158,25 +158,18 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi getter = Expression.Condition(condition, getter, defaultConst); } else - if (arg.Settings.Ignore.Count != 0) + if (arg.Settings.Ignore.Any(x => x.Key == member.DestinationMember.Name)) { - if (arg.MapType != MapType.MapToTarget && arg.Settings.Ignore.Any(x => x.Key == member.DestinationMember.Name)) - getter = defaultConst; + getter = defaultConst; if (arg.MapType == MapType.MapToTarget && arg.DestinationType.IsRecordType()) { - if (arg.Settings.Ignore.Any(x => x.Key == member.DestinationMember.Name)) - { - var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() - .Where(x => x.Name == member.DestinationMember.Name).FirstOrDefault(); - - if (find != null) - getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); - } - else - getter = defaultConst; - } + var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() + .Where(x => x.Name == member.DestinationMember.Name).FirstOrDefault(); + if (find != null) + getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); + } } } arguments.Add(getter); From fdf059ac6347e4e4df1ab282cec42b4f9c4bce73 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 22 Jan 2025 15:20:17 +0500 Subject: [PATCH 269/363] add supporting Ignored() to readonly interface --- src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs index bf097fb5..987cfe77 100644 --- a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs +++ b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs @@ -38,7 +38,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E return base.CreateInstantiationExpression(source, destination, arg); var ctor = destType.GetConstructors()[0]; var classModel = GetConstructorModel(ctor, false); - var classConverter = CreateClassConverter(source, classModel, arg); + var classConverter = CreateClassConverter(source, classModel, arg,CtorMapping:true); return CreateInstantiationExpression(source, classConverter, arg, destination); } else From 6cf1320c4d23e76bf791619e1163e2ca2ff4db1e Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 22 Jan 2025 19:23:23 +0500 Subject: [PATCH 270/363] add test Interface Ctor param Ignored() --- src/Mapster.Tests/WhenIgnoreMapping.cs | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Mapster.Tests/WhenIgnoreMapping.cs b/src/Mapster.Tests/WhenIgnoreMapping.cs index 6398ef35..245c4e63 100644 --- a/src/Mapster.Tests/WhenIgnoreMapping.cs +++ b/src/Mapster.Tests/WhenIgnoreMapping.cs @@ -90,7 +90,47 @@ public void WhenClassIgnoreCtorParamGetDefaultValue() mapTotarget.Text.ShouldBe("test"); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/723 + /// + [TestMethod] + public void MappingToIntefaceWithIgnorePrivateSetProperty() + { + TypeAdapterConfig + .NewConfig() + .TwoWays() + .Ignore(dest => dest.Ignore); + + InterfaceDestination723 dataDestination = new Data723() { Inter = "IterDataDestination", Ignore = "IgnoreDataDestination" }; + + Should.NotThrow(() => + { + var isourse = dataDestination.Adapt(); + var idestination = dataDestination.Adapt(); + }); + + } + #region TestClasses + + public interface InterfaceDestination723 + { + public string Inter { get; set; } + public string Ignore { get; } + } + + public interface InterfaceSource723 + { + public string Inter { get; set; } + } + + private class Data723 : InterfaceSource723, InterfaceDestination723 + { + public string Ignore { get; set; } + + public string Inter { get; set; } + } + static ConstructorInfo? GetConstructor() { var parameterlessCtorInfo = typeof(TDestination).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, new Type[0]); From 7f6cdde318d1233768f88367daf3ec4258c23e02 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 22 Jan 2025 19:28:11 +0500 Subject: [PATCH 271/363] fix misprints --- src/Mapster/Adapters/BaseClassAdapter.cs | 4 ++-- src/Mapster/Adapters/ClassAdapter.cs | 4 ++-- src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs | 2 +- src/Mapster/Adapters/RecordTypeAdapter.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 67e3caf4..966f2fe9 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -15,7 +15,7 @@ internal abstract class BaseClassAdapter : BaseAdapter #region Build the Adapter Model - protected ClassMapping CreateClassConverter(Expression source, ClassModel classModel, CompileArgument arg, Expression? destination = null, bool CtorMapping = false) + protected ClassMapping CreateClassConverter(Expression source, ClassModel classModel, CompileArgument arg, Expression? destination = null, bool ctorMapping = false) { var destinationMembers = classModel.Members; var unmappedDestinationMembers = new List(); @@ -29,7 +29,7 @@ src is LambdaExpression lambda : ExpressionEx.PropertyOrFieldPath(source, (string)src))); foreach (var destinationMember in destinationMembers) { - if (ProcessIgnores(arg, destinationMember, out var ignore) && !CtorMapping) + if (ProcessIgnores(arg, destinationMember, out var ignore) && !ctorMapping) continue; var resolvers = arg.Settings.ValueAccessingStrategies.AsEnumerable(); diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index d953f11a..6ae450fa 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -69,13 +69,13 @@ protected override Expression CreateInstantiationExpression(Expression source, E classConverter = destType.GetConstructors() .OrderByDescending(it => it.GetParameters().Length) .Select(it => GetConstructorModel(it, true)) - .Select(it => CreateClassConverter(source, it, arg, CtorMapping:true)) + .Select(it => CreateClassConverter(source, it, arg, ctorMapping:true)) .FirstOrDefault(it => it != null); } else { var model = GetConstructorModel(ctor, false); - classConverter = CreateClassConverter(source, model, arg, CtorMapping:true); + classConverter = CreateClassConverter(source, model, arg, ctorMapping:true); } if (classConverter == null) diff --git a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs index 987cfe77..ea4aa6e1 100644 --- a/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs +++ b/src/Mapster/Adapters/ReadOnlyInterfaceAdapter.cs @@ -38,7 +38,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E return base.CreateInstantiationExpression(source, destination, arg); var ctor = destType.GetConstructors()[0]; var classModel = GetConstructorModel(ctor, false); - var classConverter = CreateClassConverter(source, classModel, arg,CtorMapping:true); + var classConverter = CreateClassConverter(source, classModel, arg, ctorMapping:true); return CreateInstantiationExpression(source, classConverter, arg, destination); } else diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 718000f4..0ef0532d 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -31,7 +31,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E var ctor = destType.GetConstructors() .OrderByDescending(it => it.GetParameters().Length).ToArray().FirstOrDefault(); // Will be used public constructor with the maximum number of parameters var classModel = GetConstructorModel(ctor, false); - var classConverter = CreateClassConverter(source, classModel, arg, CtorMapping:true); + var classConverter = CreateClassConverter(source, classModel, arg, ctorMapping:true); var installExpr = CreateInstantiationExpression(source, classConverter, arg, destination); return RecordInlineExpression(source, arg, installExpr); // Activator field when not include in public ctor } From 0f2302ab62c81a4118aef3211feb855bd4a1b16f Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 23 Jan 2025 21:17:31 +0500 Subject: [PATCH 272/363] fix maToTarget behavior From RecordType --- src/Mapster/Adapters/BaseClassAdapter.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index faa490ec..2706bb55 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -54,6 +54,15 @@ select fn(src, destinationMember, arg)) Destination = (ParameterExpression?)destination, UseDestinationValue = arg.MapType != MapType.Projection && destinationMember.UseDestinationValue(arg), }; + if (arg.MapType == MapType.MapToTarget && getter == null && arg.DestinationType.IsRecordType()) + { + var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() + .Where(x => x.Name == destinationMember.Name).FirstOrDefault(); + + if (find != null) + getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); + + } if (getter != null) { propertyModel.Getter = arg.MapType == MapType.Projection From f8469e911789669c46d8f745a083278c325eb049 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 23 Jan 2025 21:38:32 +0500 Subject: [PATCH 273/363] add To RecordTypeAdapter --- src/Mapster/Adapters/RecordTypeAdapter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 009af932..12617786 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -33,7 +33,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E var classModel = GetConstructorModel(ctor, false); var classConverter = CreateClassConverter(source, classModel, arg); var installExpr = CreateInstantiationExpression(source, classConverter, arg); - return RecordInlineExpression(source, arg, installExpr); // Activator field when not include in public ctor + return RecordInlineExpression(source, destination, arg, installExpr); // Activator field when not include in public ctor } protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) @@ -46,7 +46,7 @@ protected override Expression CreateInlineExpression(Expression source, CompileA return base.CreateInstantiationExpression(source, arg); } - private Expression? RecordInlineExpression(Expression source, CompileArgument arg, Expression installExpr) + private Expression? RecordInlineExpression(Expression source, Expression? destination, CompileArgument arg, Expression installExpr) { //new TDestination { // Prop1 = convert(src.Prop1), @@ -58,7 +58,7 @@ protected override Expression CreateInlineExpression(Expression source, CompileA var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; var contructorMembers = newInstance.Arguments.OfType().Select(me => me.Member).ToArray(); var classModel = GetSetterModel(arg); - var classConverter = CreateClassConverter(source, classModel, arg); + var classConverter = CreateClassConverter(source, classModel, arg, destination:destination); var members = classConverter.Members; var lines = new List(); From d02756f76f9da73122be63fdd30bfe2264b3e735 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 23 Jan 2025 22:04:21 +0500 Subject: [PATCH 274/363] fix mapToTarget test from Record --- src/Mapster.Tests/WhenMappingRecordRegression.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index e5d719e4..1bd362eb 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -15,10 +15,10 @@ public class WhenMappingRecordRegression public void AdaptRecordToRecord() { var _source = new TestRecord() { X = 700 }; - var _destination = new TestRecord() { X = 500 }; + var _destination = new TestRecordY() { X = 500 , Y = 200 }; var _result = _source.Adapt(_destination); - _result.X.ShouldBe(700); + _result.Y.ShouldBe(200); object.ReferenceEquals(_result, _destination).ShouldBeFalse(); } @@ -382,6 +382,11 @@ public void CollectionUpdate() #region TestClasses + public record TestRecordY() + { + public int X { get; set; } + public int Y { get; set; } + } public interface IActivityDataExtentions : IActivityData { public int TempLength { get; set; } From 564da6d75b32505703e0f6779e99c4db1c693056 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 24 Jan 2025 16:21:29 +0500 Subject: [PATCH 275/363] fix map to target from recordType --- .../WhenIgnoringConditionally.cs | 6 +- .../WhenMappingRecordRegression.cs | 43 ++++++++++-- src/Mapster/Adapters/BaseClassAdapter.cs | 11 +++- src/Mapster/Adapters/RecordTypeAdapter.cs | 66 ++++++++++++++----- 4 files changed, 100 insertions(+), 26 deletions(-) diff --git a/src/Mapster.Tests/WhenIgnoringConditionally.cs b/src/Mapster.Tests/WhenIgnoringConditionally.cs index 471760b8..9014d133 100644 --- a/src/Mapster.Tests/WhenIgnoringConditionally.cs +++ b/src/Mapster.Tests/WhenIgnoringConditionally.cs @@ -164,6 +164,8 @@ public void IgnoreIf_Apply_To_RecordType() .Compile(); var poco = new SimplePoco { Id = 1, Name = "TestName" }; + + var srt = poco.BuildAdapter().CreateMapToTargetExpression(); var dto = TypeAdapter.Adapt(poco); dto.Id.ShouldBe(1); @@ -190,8 +192,8 @@ public class SimpleDto [AdaptWith(AdaptDirectives.DestinationAsRecord)] public class SimpleRecord { - public int Id { get; } - public string Name { get; } + public int Id { get; private set; } + public string Name { get; private set; } public SimpleRecord(int id, string name) { diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index aced8129..e557927c 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -14,12 +14,24 @@ public class WhenMappingRecordRegression [TestMethod] public void AdaptRecordToRecord() { + TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.Y); + + + + var _source = new TestRecord() { X = 700 }; var _destination = new TestRecordY() { X = 500 , Y = 200 }; + + var _destination2 = new TestRecordY() { X = 300, Y = 400 }; var _result = _source.Adapt(_destination); - _result.X.ShouldBe(700); - _result.Y.ShouldBe(200); - object.ReferenceEquals(_result, _destination).ShouldBeFalse(); + + var result2 = _destination.Adapt(_destination2); + + //_result.X.ShouldBe(700); + //_result.Y.ShouldBe(200); + //object.ReferenceEquals(_result, _destination).ShouldBeFalse(); } [TestMethod] @@ -362,14 +374,28 @@ public void WhenRecordReceivedIgnoreCtorParamProcessing() TypeAdapterConfig.NewConfig() .Ignore(dest => dest.Name); + //TypeAdapterConfig.NewConfig() + //.Ignore(dest => dest.User); + var userDto = new UserDto456("Amichai"); var user = new UserRecord456("John"); - var map = userDto.Adapt(); - var maptoTarget = userDto.Adapt(user); + //var map = userDto.Adapt(); - map.Name.ShouldBeNullOrEmpty(); - maptoTarget.Name.ShouldBe("John"); + // var sd = userDto.BuildAdapter().CreateMapToTargetExpression(); + // var maptoTarget = userDto.Adapt(user); + + + var s = new DToInside(userDto); + + var d = new UserInside(user, new UserRecord456( "Skot")); + + var sd = s.BuildAdapter().CreateMapToTargetExpression(); + + var ssd = s.Adapt(d); + + // map.Name.ShouldBeNullOrEmpty(); + // maptoTarget.Name.ShouldBe("John"); } @@ -406,6 +432,9 @@ public record TestRecordY() public int Y { get; set; } } + public record UserInside(UserRecord456 User, UserRecord456 SecondName); + public record DToInside(UserDto456 User); + public record UserRecord456(string Name); public record UserDto456(string Name); diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index d500ca58..09176e62 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -59,7 +59,7 @@ select fn(src, destinationMember, arg)) var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() .Where(x => x.Name == destinationMember.Name).FirstOrDefault(); - if (find != null) + if (find != null && destination != null) getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); } @@ -154,6 +154,15 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi if (member.Getter == null) { getter = defaultConst; + + if (arg.MapType == MapType.MapToTarget && arg.DestinationType.IsRecordType()) + { + var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() + .Where(x => x.Name == member.DestinationMember.Name).FirstOrDefault(); + + if (find != null && destination != null) + getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); + } } else { diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 44b42226..1a282ea1 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -1,8 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Mapster.Utils; +using static Mapster.IgnoreDictionary; namespace Mapster.Adapters { @@ -20,19 +22,18 @@ protected override Expression CreateInstantiationExpression(Expression source, E { //new TDestination(src.Prop1, src.Prop2) - if (arg.GetConstructUsing() != null) - return base.CreateInstantiationExpression(source, destination, arg); - - var destType = arg.DestinationType.GetTypeInfo().IsInterface - ? DynamicTypeGenerator.GetTypeForInterface(arg.DestinationType, arg.Settings.Includes.Count > 0) - : arg.DestinationType; - if (destType == null) - return base.CreateInstantiationExpression(source, destination, arg); - var ctor = destType.GetConstructors() - .OrderByDescending(it => it.GetParameters().Length).ToArray().FirstOrDefault(); // Will be used public constructor with the maximum number of parameters - var classModel = GetConstructorModel(ctor, false); - var classConverter = CreateClassConverter(source, classModel, arg, ctorMapping:true); - var installExpr = CreateInstantiationExpression(source, classConverter, arg, destination); + Expression installExpr; + + if (arg.GetConstructUsing() != null || arg.DestinationType == null) + installExpr = base.CreateInstantiationExpression(source, destination, arg); + else + { + var ctor = arg.DestinationType.GetConstructors() + .OrderByDescending(it => it.GetParameters().Length).ToArray().FirstOrDefault(); // Will be used public constructor with the maximum number of parameters + var classModel = GetConstructorModel(ctor, false); + var classConverter = CreateClassConverter(source, classModel, arg, ctorMapping: true); + installExpr = CreateInstantiationExpression(source, classConverter, arg, destination); + } return RecordInlineExpression(source, destination, arg, installExpr); // Activator field when not include in public ctor } @@ -56,7 +57,9 @@ protected override Expression CreateInlineExpression(Expression source, CompileA var exp = installExpr; var memberInit = exp as MemberInitExpression; var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; - var contructorMembers = newInstance.Arguments.OfType().Select(me => me.Member).ToArray(); + var contructorMembers = arg.DestinationType.GetConstructors() + .OrderByDescending(it => it.GetParameters().Length).FirstOrDefault() + .GetParameters().ToList(); var classModel = GetSetterModel(arg); var classConverter = CreateClassConverter(source, classModel, arg, destination:destination); var members = classConverter.Members; @@ -70,7 +73,7 @@ protected override Expression CreateInlineExpression(Expression source, CompileA return null; if (!arg.Settings.Resolvers.Any(r => r.DestinationMemberName == member.DestinationMember.Name) - && member.Getter is MemberExpression memberExp && contructorMembers.Contains(memberExp.Member)) + && contructorMembers.Any(x=>string.Equals(x.Name, member.DestinationMember.Name, StringComparison.InvariantCultureIgnoreCase))) continue; if (member.DestinationMember.SetterModifier == AccessModifier.None) @@ -93,8 +96,39 @@ protected override Expression CreateInlineExpression(Expression source, CompileA lines.Add(bind); } + if(arg.MapType == MapType.MapToTarget) + lines.AddRange(RecordIngnoredWithoutConditonRestore(destination, arg, contructorMembers)); + return Expression.MemberInit(newInstance, lines); } + + private List RecordIngnoredWithoutConditonRestore(Expression? destination, CompileArgument arg, List contructorMembers) + { + var members = arg.DestinationType + .GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()) + .Where(x=> arg.Settings.Ignore.Any(y=> y.Key == x.Name)); + + var lines = new List(); + + + foreach (var member in members) + { + if(destination == null) + continue; + + IgnoreItem ignore; + ProcessIgnores(arg, member, out ignore); + + if (member.SetterModifier == AccessModifier.None || + ignore.Condition != null || + contructorMembers.Any(x=> string.Equals(x.Name, member.Name, StringComparison.InvariantCultureIgnoreCase))) + continue; + + lines.Add(Expression.Bind((MemberInfo)member.Info, Expression.MakeMemberAccess(destination, (MemberInfo)member.Info))); + } + + return lines; + } } } From f29d93558b4acbd36ee757270b983b1f669f2fe8 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 25 Jan 2025 13:24:01 +0500 Subject: [PATCH 276/363] update RecordType Tests using ignore ctor param and test MapToTarget and not match value --- .../WhenMappingRecordRegression.cs | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index e557927c..2316069b 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -18,9 +18,6 @@ public void AdaptRecordToRecord() .NewConfig() .Ignore(dest => dest.Y); - - - var _source = new TestRecord() { X = 700 }; var _destination = new TestRecordY() { X = 500 , Y = 200 }; @@ -29,9 +26,9 @@ public void AdaptRecordToRecord() var result2 = _destination.Adapt(_destination2); - //_result.X.ShouldBe(700); - //_result.Y.ShouldBe(200); - //object.ReferenceEquals(_result, _destination).ShouldBeFalse(); + _result.X.ShouldBe(700); + _result.Y.ShouldBe(200); + object.ReferenceEquals(_result, _destination).ShouldBeFalse(); } [TestMethod] @@ -374,28 +371,24 @@ public void WhenRecordReceivedIgnoreCtorParamProcessing() TypeAdapterConfig.NewConfig() .Ignore(dest => dest.Name); - //TypeAdapterConfig.NewConfig() - //.Ignore(dest => dest.User); + TypeAdapterConfig.NewConfig() + .Ignore(dest => dest.User); var userDto = new UserDto456("Amichai"); var user = new UserRecord456("John"); + var DtoInsider = new DtoInside(userDto); + var UserInsider = new UserInside(user, new UserRecord456("Skot")); - //var map = userDto.Adapt(); - - // var sd = userDto.BuildAdapter().CreateMapToTargetExpression(); - // var maptoTarget = userDto.Adapt(user); - - - var s = new DToInside(userDto); - - var d = new UserInside(user, new UserRecord456( "Skot")); + var map = userDto.Adapt(); + var maptoTarget = userDto.Adapt(user); - var sd = s.BuildAdapter().CreateMapToTargetExpression(); + var MapToTargetInsider = DtoInsider.Adapt(UserInsider); - var ssd = s.Adapt(d); + map.Name.ShouldBeNullOrEmpty(); // Ignore is work set default value + maptoTarget.Name.ShouldBe("John"); // Ignore is work ignored member save value from Destination + MapToTargetInsider.User.Name.ShouldBe("John"); // Ignore is work member save value from Destination + MapToTargetInsider.SecondName.Name.ShouldBe("Skot"); // Unmached member save value from Destination - // map.Name.ShouldBeNullOrEmpty(); - // maptoTarget.Name.ShouldBe("John"); } @@ -433,7 +426,7 @@ public record TestRecordY() } public record UserInside(UserRecord456 User, UserRecord456 SecondName); - public record DToInside(UserDto456 User); + public record DtoInside(UserDto456 User); public record UserRecord456(string Name); From 0c5384dbaaffe02c75c8a9f0a59329544faf5d86 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 28 Jan 2025 14:49:37 +0500 Subject: [PATCH 277/363] refactoring Map to target record --- src/Mapster/Adapters/BaseClassAdapter.cs | 47 +++++++++++------------ src/Mapster/Adapters/RecordTypeAdapter.cs | 17 ++++---- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 09176e62..9bb5bc17 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -1,10 +1,10 @@ -using System; +using Mapster.Models; +using Mapster.Utils; +using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Mapster.Models; -using Mapster.Utils; namespace Mapster.Adapters { @@ -15,7 +15,7 @@ internal abstract class BaseClassAdapter : BaseAdapter #region Build the Adapter Model - protected ClassMapping CreateClassConverter(Expression source, ClassModel classModel, CompileArgument arg, Expression? destination = null, bool ctorMapping = false) + protected ClassMapping CreateClassConverter(Expression source, ClassModel classModel, CompileArgument arg, Expression? destination = null, bool ctorMapping = false, ClassModel recordRestorMemberModel = null) { var destinationMembers = classModel.Members; var unmappedDestinationMembers = new List(); @@ -56,12 +56,7 @@ select fn(src, destinationMember, arg)) }; if (arg.MapType == MapType.MapToTarget && getter == null && arg.DestinationType.IsRecordType()) { - var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() - .Where(x => x.Name == destinationMember.Name).FirstOrDefault(); - - if (find != null && destination != null) - getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); - + getter = TryRestoreRecordMember(destinationMember, recordRestorMemberModel, destination) ?? getter; } if (getter != null) { @@ -138,7 +133,7 @@ protected static bool ProcessIgnores( && ignore.Condition == null; } - protected Expression CreateInstantiationExpression(Expression source, ClassMapping classConverter, CompileArgument arg, Expression? destination) + protected Expression CreateInstantiationExpression(Expression source, ClassMapping classConverter, CompileArgument arg, Expression? destination, ClassModel recordRestorParamModel = null) { var members = classConverter.Members; @@ -156,13 +151,7 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi getter = defaultConst; if (arg.MapType == MapType.MapToTarget && arg.DestinationType.IsRecordType()) - { - var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() - .Where(x => x.Name == member.DestinationMember.Name).FirstOrDefault(); - - if (find != null && destination != null) - getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); - } + getter = TryRestoreRecordMember(member.DestinationMember,recordRestorParamModel,destination) ?? getter; } else { @@ -181,13 +170,7 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi getter = defaultConst; if (arg.MapType == MapType.MapToTarget && arg.DestinationType.IsRecordType()) - { - var find = arg.DestinationType.GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()).ToArray() - .Where(x => x.Name == member.DestinationMember.Name).FirstOrDefault(); - - if (find != null) - getter = Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); - } + getter = TryRestoreRecordMember(member.DestinationMember, recordRestorParamModel, destination) ?? getter; } } arguments.Add(getter); @@ -214,6 +197,20 @@ protected virtual ClassModel GetSetterModel(CompileArgument arg) }; } + protected MemberExpression? TryRestoreRecordMember(IMemberModelEx member, ClassModel? restorRecordModel, Expression? destination) + { + if (restorRecordModel != null && destination != null) + { + var find = restorRecordModel.Members + .Where(x => x.Name == member.Name).FirstOrDefault(); + + if (find != null) + return Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); + } + + return null; + } + #endregion } } diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 1a282ea1..34edbdd2 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using Mapster.Models; using Mapster.Utils; using static Mapster.IgnoreDictionary; @@ -31,8 +32,9 @@ protected override Expression CreateInstantiationExpression(Expression source, E var ctor = arg.DestinationType.GetConstructors() .OrderByDescending(it => it.GetParameters().Length).ToArray().FirstOrDefault(); // Will be used public constructor with the maximum number of parameters var classModel = GetConstructorModel(ctor, false); + var restorParamModel = GetSetterModel(arg); var classConverter = CreateClassConverter(source, classModel, arg, ctorMapping: true); - installExpr = CreateInstantiationExpression(source, classConverter, arg, destination); + installExpr = CreateInstantiationExpression(source, classConverter, arg, destination, restorParamModel); } return RecordInlineExpression(source, destination, arg, installExpr); // Activator field when not include in public ctor } @@ -57,11 +59,9 @@ protected override Expression CreateInlineExpression(Expression source, CompileA var exp = installExpr; var memberInit = exp as MemberInitExpression; var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; - var contructorMembers = arg.DestinationType.GetConstructors() - .OrderByDescending(it => it.GetParameters().Length).FirstOrDefault() - .GetParameters().ToList(); + var contructorMembers = newInstance.Constructor?.GetParameters().ToList() ?? new(); var classModel = GetSetterModel(arg); - var classConverter = CreateClassConverter(source, classModel, arg, destination:destination); + var classConverter = CreateClassConverter(source, classModel, arg, destination:destination,recordRestorMemberModel:classModel); var members = classConverter.Members; var lines = new List(); @@ -97,15 +97,14 @@ protected override Expression CreateInlineExpression(Expression source, CompileA } if(arg.MapType == MapType.MapToTarget) - lines.AddRange(RecordIngnoredWithoutConditonRestore(destination, arg, contructorMembers)); + lines.AddRange(RecordIngnoredWithoutConditonRestore(destination, arg, contructorMembers, classModel)); return Expression.MemberInit(newInstance, lines); } - private List RecordIngnoredWithoutConditonRestore(Expression? destination, CompileArgument arg, List contructorMembers) + private List RecordIngnoredWithoutConditonRestore(Expression? destination, CompileArgument arg, List contructorMembers, ClassModel restorPropertyModel) { - var members = arg.DestinationType - .GetFieldsAndProperties(arg.Settings.EnableNonPublicMembers.GetValueOrDefault()) + var members = restorPropertyModel.Members .Where(x=> arg.Settings.Ignore.Any(y=> y.Key == x.Name)); var lines = new List(); From f5a3ce6cd315f2bb25b4c1d30e313ec8b3fb6ff5 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 28 Jan 2025 19:14:31 +0500 Subject: [PATCH 278/363] Fix error with Using Destination Value From Record Type --- src/Mapster/Adapters/RecordTypeAdapter.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 34edbdd2..c9887f24 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -69,17 +70,19 @@ protected override Expression CreateInlineExpression(Expression source, CompileA lines.AddRange(memberInit.Bindings); foreach (var member in members) { - if (member.UseDestinationValue) - return null; + Expression? value; if (!arg.Settings.Resolvers.Any(r => r.DestinationMemberName == member.DestinationMember.Name) && contructorMembers.Any(x=>string.Equals(x.Name, member.DestinationMember.Name, StringComparison.InvariantCultureIgnoreCase))) continue; - if (member.DestinationMember.SetterModifier == AccessModifier.None) + if (member.DestinationMember.SetterModifier == AccessModifier.None && member.UseDestinationValue == false) continue; - var value = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); + if (member.DestinationMember.SetterModifier == AccessModifier.None && member.UseDestinationValue) + continue; // work in progress + else + value = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); //special null property check for projection //if we don't set null to property, EF will create empty object From 5c2d4a7c090fc9b8ab624e993239f65bf7e44740 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 2 Feb 2025 08:24:31 +0500 Subject: [PATCH 279/363] add Null check destination from mapToTarget RecordType --- src/Mapster/Adapters/BaseClassAdapter.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 9bb5bc17..309687a5 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -197,7 +197,7 @@ protected virtual ClassModel GetSetterModel(CompileArgument arg) }; } - protected MemberExpression? TryRestoreRecordMember(IMemberModelEx member, ClassModel? restorRecordModel, Expression? destination) + protected Expression? TryRestoreRecordMember(IMemberModelEx member, ClassModel? restorRecordModel, Expression? destination) { if (restorRecordModel != null && destination != null) { @@ -205,7 +205,11 @@ protected virtual ClassModel GetSetterModel(CompileArgument arg) .Where(x => x.Name == member.Name).FirstOrDefault(); if (find != null) - return Expression.MakeMemberAccess(destination, (MemberInfo)find.Info); + { + var compareNull = Expression.Equal(destination, Expression.Constant(null, destination.Type)); + return Expression.Condition(compareNull, member.Type.CreateDefault(), Expression.MakeMemberAccess(destination, (MemberInfo)find.Info)); + } + } return null; From 32f0a3412c9f5687bd90eaeba3e24a8e052ed661 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 2 Feb 2025 08:43:48 +0500 Subject: [PATCH 280/363] add support from RecordType IgnoreNullValue UseDestinationValue Use Destination value from Primitive type AutoProperty --- src/Mapster/Adapters/RecordTypeAdapter.cs | 206 ++++++++++++++++++---- 1 file changed, 175 insertions(+), 31 deletions(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index c9887f24..7411c097 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -1,17 +1,17 @@ -using System; +using Mapster.Models; +using Mapster.Utils; +using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Mapster.Models; -using Mapster.Utils; using static Mapster.IgnoreDictionary; namespace Mapster.Adapters { internal class RecordTypeAdapter : ClassAdapter { + private ClassMapping? ClassConverterContext; protected override int Score => -149; protected override bool UseTargetValue => false; @@ -20,6 +20,15 @@ protected override bool CanMap(PreCompileArgument arg) return arg.DestinationType.IsRecordType(); } + protected override bool CanInline(Expression source, Expression? destination, CompileArgument arg) + { + return false; + } + + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + { + return base.CreateInstantiationExpression(source, arg); + } protected override Expression CreateInstantiationExpression(Expression source, Expression? destination, CompileArgument arg) { //new TDestination(src.Prop1, src.Prop2) @@ -37,20 +46,12 @@ protected override Expression CreateInstantiationExpression(Expression source, E var classConverter = CreateClassConverter(source, classModel, arg, ctorMapping: true); installExpr = CreateInstantiationExpression(source, classConverter, arg, destination, restorParamModel); } - return RecordInlineExpression(source, destination, arg, installExpr); // Activator field when not include in public ctor - } - protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) - { - return Expression.Empty(); - } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) - { - return base.CreateInstantiationExpression(source, arg); + return RecordInlineExpression(source, destination, arg, installExpr); // Activator field when not include in public ctor } - - private Expression? RecordInlineExpression(Expression source, Expression? destination, CompileArgument arg, Expression installExpr) + + private Expression? RecordInlineExpression(Expression source, Expression? destination, CompileArgument arg, Expression installExpr) { //new TDestination { // Prop1 = convert(src.Prop1), @@ -62,27 +63,43 @@ protected override Expression CreateInlineExpression(Expression source, CompileA var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; var contructorMembers = newInstance.Constructor?.GetParameters().ToList() ?? new(); var classModel = GetSetterModel(arg); - var classConverter = CreateClassConverter(source, classModel, arg, destination:destination,recordRestorMemberModel:classModel); + var classConverter = CreateClassConverter(source, classModel, arg, destination: destination, recordRestorMemberModel: classModel); var members = classConverter.Members; + ClassConverterContext = classConverter; + var lines = new List(); if (memberInit != null) lines.AddRange(memberInit.Bindings); foreach (var member in members) { - Expression? value; if (!arg.Settings.Resolvers.Any(r => r.DestinationMemberName == member.DestinationMember.Name) - && contructorMembers.Any(x=>string.Equals(x.Name, member.DestinationMember.Name, StringComparison.InvariantCultureIgnoreCase))) + && contructorMembers.Any(x => string.Equals(x.Name, member.DestinationMember.Name, StringComparison.InvariantCultureIgnoreCase))) continue; - if (member.DestinationMember.SetterModifier == AccessModifier.None && member.UseDestinationValue == false) + if (member.DestinationMember.SetterModifier == AccessModifier.None) continue; - if (member.DestinationMember.SetterModifier == AccessModifier.None && member.UseDestinationValue) - continue; // work in progress - else - value = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); + var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); + + if (arg.MapType == MapType.MapToTarget && arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull()) // add IgnoreNullValues support + { + if (adapt is ConditionalExpression condEx) + { + if (condEx.Test is BinaryExpression { NodeType: ExpressionType.Equal } binEx && + binEx.Left == member.Getter && + binEx.Right is ConstantExpression { Value: null }) + adapt = condEx.IfFalse; + } + var destinationCompareNull = Expression.Equal(destination, Expression.Constant(null, destination.Type)); + var sourceCondition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type)); + var destinationCanbeNull = Expression.Condition(destinationCompareNull, member.DestinationMember.Type.CreateDefault(), member.DestinationMember.GetExpression(destination)); + adapt = Expression.Condition(sourceCondition, adapt, destinationCanbeNull); + } + + + //special null property check for projection //if we don't set null to property, EF will create empty object @@ -93,13 +110,13 @@ protected override Expression CreateInlineExpression(Expression source, CompileA && !member.DestinationMember.Type.IsCollection() && member.Getter.Type.GetTypeInfo().GetCustomAttributesData().All(attr => attr.GetAttributeType().Name != "ComplexTypeAttribute")) { - value = member.Getter.NotNullReturn(value); + adapt = member.Getter.NotNullReturn(adapt); } - var bind = Expression.Bind((MemberInfo)member.DestinationMember.Info!, value); + var bind = Expression.Bind((MemberInfo)member.DestinationMember.Info!, adapt); lines.Add(bind); } - if(arg.MapType == MapType.MapToTarget) + if (arg.MapType == MapType.MapToTarget) lines.AddRange(RecordIngnoredWithoutConditonRestore(destination, arg, contructorMembers, classModel)); return Expression.MemberInit(newInstance, lines); @@ -107,15 +124,15 @@ protected override Expression CreateInlineExpression(Expression source, CompileA private List RecordIngnoredWithoutConditonRestore(Expression? destination, CompileArgument arg, List contructorMembers, ClassModel restorPropertyModel) { - var members = restorPropertyModel.Members - .Where(x=> arg.Settings.Ignore.Any(y=> y.Key == x.Name)); + var members = restorPropertyModel.Members + .Where(x => arg.Settings.Ignore.Any(y => y.Key == x.Name)); var lines = new List(); foreach (var member in members) { - if(destination == null) + if (destination == null) continue; IgnoreItem ignore; @@ -123,14 +140,141 @@ private List RecordIngnoredWithoutConditonRestore(Expression? des if (member.SetterModifier == AccessModifier.None || ignore.Condition != null || - contructorMembers.Any(x=> string.Equals(x.Name, member.Name, StringComparison.InvariantCultureIgnoreCase))) + contructorMembers.Any(x => string.Equals(x.Name, member.Name, StringComparison.InvariantCultureIgnoreCase))) continue; - lines.Add(Expression.Bind((MemberInfo)member.Info, Expression.MakeMemberAccess(destination, (MemberInfo)member.Info))); + lines.Add(Expression.Bind((MemberInfo)member.Info, Expression.MakeMemberAccess(destination, (MemberInfo)member.Info))); } return lines; } + + protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) + { + // Mapping property Without setter when UseDestinationValue == true + + var result = destination; + var classModel = GetSetterModel(arg); + var classConverter = CreateClassConverter(source, classModel, arg, result); + var members = classConverter.Members; + + var lines = new List(); + + foreach (var member in members) + { + if (member.DestinationMember.SetterModifier == AccessModifier.None && member.UseDestinationValue) + { + + if (member.DestinationMember is PropertyModel && member.DestinationMember.Type.IsValueType + || member.DestinationMember.Type.IsMapsterPrimitive() + || member.DestinationMember.Type.IsRecordType()) + { + + Expression adapt; + if (member.DestinationMember.Type.IsRecordType()) + adapt = arg.Context.Config.CreateMapInvokeExpressionBody(member.Getter.Type, member.DestinationMember.Type, member.Getter); + else + adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member, result); + + var blocks = Expression.Block(SetValueTypeAutoPropertyByReflection(member, adapt, classModel)); + var lambda = Expression.Lambda(blocks, parameters: new[] { (ParameterExpression)source, (ParameterExpression)destination }); + + if (arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull()) + { + + if (arg.MapType != MapType.MapToTarget) + { + var condition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type)); + lines.Add(Expression.IfThen(condition, Expression.Invoke(lambda, source, destination))); + continue; + } + + if (arg.MapType == MapType.MapToTarget) + { + var var2Param = ClassConverterContext.Members.Where(x => x.DestinationMember.Name == member.DestinationMember.Name).FirstOrDefault(); + + Expression destMemberVar2 = var2Param.DestinationMember.GetExpression(var2Param.Destination); + if(member.DestinationMember.Type.IsRecordType()) + destMemberVar2 = arg.Context.Config.CreateMapInvokeExpressionBody(member.Getter.Type, member.DestinationMember.Type, destMemberVar2); + var blocksVar2 = Expression.Block(SetValueTypeAutoPropertyByReflection(member, destMemberVar2, classModel)); + var lambdaVar2 = Expression.Lambda(blocksVar2, parameters: new[] { (ParameterExpression)var2Param.Destination, (ParameterExpression)destination }); + var adaptVar2 = Expression.Invoke(lambdaVar2, var2Param.Destination, destination); + + + Expression conditionVar2; + if (destMemberVar2.CanBeNull()) + { + var complexcheck = Expression.AndAlso(Expression.NotEqual(var2Param.Destination, Expression.Constant(null, var2Param.Destination.Type)), // if(var2 != null && var2.Prop != null) + Expression.NotEqual(destMemberVar2, Expression.Constant(null, var2Param.Getter.Type))); + conditionVar2 = Expression.IfThen(complexcheck, adaptVar2); + } + else + conditionVar2 = Expression.IfThen(Expression.NotEqual(var2Param.Destination, Expression.Constant(null, var2Param.Destination.Type)), adaptVar2); + + var condition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type)); + lines.Add(Expression.IfThenElse(condition, Expression.Invoke(lambda, source, destination), conditionVar2)); + continue; + } + } + + lines.Add(Expression.Invoke(lambda, source, destination)); + } + else + { + var destMember = member.DestinationMember.GetExpression(destination); + var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member, destMember); + + if (arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull()) + { + if (arg.MapType != MapType.MapToTarget) + { + var condition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type)); + lines.Add(Expression.IfThen(condition, adapt)); + continue; + } + if (arg.MapType == MapType.MapToTarget) + { + var var2Param = ClassConverterContext.Members.Where(x => x.DestinationMember.Name == member.DestinationMember.Name).FirstOrDefault(); + + var destMemberVar2 = var2Param.DestinationMember.GetExpression(var2Param.Destination); + var adaptVar2 = CreateAdaptExpression(destMemberVar2, member.DestinationMember.Type, arg, var2Param, destMember); + + var complexcheck = Expression.AndAlso(Expression.NotEqual(var2Param.Destination, Expression.Constant(null, var2Param.Destination.Type)), // if(var2 != null && var2.Prop != null) + Expression.NotEqual(destMemberVar2, Expression.Constant(null, var2Param.Getter.Type))); + var conditionVar2 = Expression.IfThen(complexcheck, adaptVar2); + + var condition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type)); + lines.Add(Expression.IfThenElse(condition, adapt, conditionVar2)); + continue; + } + + + } + + lines.Add(adapt); + } + + } + } + + return lines.Count > 0 ? (Expression)Expression.Block(lines) : Expression.Empty(); + } + + protected static Expression SetValueTypeAutoPropertyByReflection(MemberMapping member, Expression adapt, ClassModel checkmodel) + { + var modDesinationMemeberName = $"<{member.DestinationMember.Name}>k__BackingField"; + if (checkmodel.Members.Any(x => x.Name == modDesinationMemeberName) == false) // Property is not autoproperty + return Expression.Empty(); + var typeofExpression = Expression.Constant(member.Destination!.Type); + var getPropertyMethod = typeof(Type).GetMethod("GetField", new[] { typeof(string), typeof(BindingFlags) })!; + var getPropertyExpression = Expression.Call(typeofExpression, getPropertyMethod, + Expression.Constant(modDesinationMemeberName), Expression.Constant(BindingFlags.Instance | BindingFlags.NonPublic)); + var setValueMethod = + typeof(FieldInfo).GetMethod("SetValue", new[] { typeof(object), typeof(object) })!; + var memberAsObject = adapt.To(typeof(object)); + return Expression.Call(getPropertyExpression, setValueMethod, + new[] { member.Destination, memberAsObject }); + } } } From 0711eb8751696d5b670758afe9027fd1c8ea8086 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 2 Feb 2025 18:18:02 +0500 Subject: [PATCH 281/363] add Test for RecordType check IgnoreNullValue UseDestinationValue Use Destination value from Primitive type AutoProperty --- .../WhenMappingRecordRegression.cs | 69 ++++++++++++++++--- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 2316069b..b1c41f92 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -391,7 +391,36 @@ public void WhenRecordReceivedIgnoreCtorParamProcessing() } + [TestMethod] + public void WhenRecordTypeWorksWithUseDestinationValueAndIgnoreNullValues() + { + + TypeAdapterConfig + .NewConfig() + .IgnoreNullValues(true); + + var _source = new SourceFromTestUseDestValue() { X = 300, Y = 200, Name = new StudentNameRecord() { Name = "John" } }; + var result = _source.Adapt(); + + var _sourceFromMapToTarget = new SourceFromTestUseDestValue() { A = 100, X = null, Y = null, Name = null }; + + var txt1 = _sourceFromMapToTarget.BuildAdapter().CreateMapExpression(); + + var txt = _sourceFromMapToTarget.BuildAdapter().CreateMapToTargetExpression(); + + var _resultMapToTarget = _sourceFromMapToTarget.Adapt(result); + + result.A.ShouldBe(0); // default Value - not match + result.S.ShouldBe("Inside Data"); // is not AutoProperty not mod by source + result.Y.ShouldBe(200); // Y is AutoProperty value transmitted from source + result.Name.Name.ShouldBe("John"); // transmitted from source standart method + _resultMapToTarget.A.ShouldBe(100); + _resultMapToTarget.X.ShouldBe(300); // Ignore NullValues work + _resultMapToTarget.Y.ShouldBe(200); // Ignore NullValues work + _resultMapToTarget.Name.Name.ShouldBe("John"); // Ignore NullValues work + + } #region NowNotWorking @@ -419,6 +448,37 @@ public void CollectionUpdate() #region TestClasses + public class SourceFromTestUseDestValue + { + public int? A { get; set; } + public int? X { get; set; } + public int? Y { get; set; } + public StudentNameRecord Name { get; set; } + } + + + public record TestRecordUseDestValue() + { + private string _s = "Inside Data"; + + public int A { get; set; } + public int X { get; set; } + + [UseDestinationValue] + public int Y { get; } + + [UseDestinationValue] + public string S { get => _s; } + + [UseDestinationValue] + public StudentNameRecord Name { get; } = new StudentNameRecord() { Name = "Marta" }; + } + + public record StudentNameRecord + { + public string Name { get; set; } + } + public record TestRecordY() { public int X { get; set; } @@ -724,14 +784,5 @@ sealed record TestSealedRecord() sealed record TestSealedRecordPositional(int X); - - - - - - - - - #endregion TestClasses } From 892c42f7947cd81d2b72fd9da833e7c6abfc689c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 3 Feb 2025 11:07:43 +0500 Subject: [PATCH 282/363] fix null Check --- src/Mapster/Adapters/RecordTypeAdapter.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 7411c097..66cb38a7 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -194,9 +194,11 @@ protected override Expression CreateBlockExpression(Expression source, Expressio var var2Param = ClassConverterContext.Members.Where(x => x.DestinationMember.Name == member.DestinationMember.Name).FirstOrDefault(); Expression destMemberVar2 = var2Param.DestinationMember.GetExpression(var2Param.Destination); + var ParamLambdaVar2 = destMemberVar2; if(member.DestinationMember.Type.IsRecordType()) - destMemberVar2 = arg.Context.Config.CreateMapInvokeExpressionBody(member.Getter.Type, member.DestinationMember.Type, destMemberVar2); - var blocksVar2 = Expression.Block(SetValueTypeAutoPropertyByReflection(member, destMemberVar2, classModel)); + ParamLambdaVar2 = arg.Context.Config.CreateMapInvokeExpressionBody(member.Getter.Type, member.DestinationMember.Type, destMemberVar2); + + var blocksVar2 = Expression.Block(SetValueTypeAutoPropertyByReflection(member, ParamLambdaVar2, classModel)); var lambdaVar2 = Expression.Lambda(blocksVar2, parameters: new[] { (ParameterExpression)var2Param.Destination, (ParameterExpression)destination }); var adaptVar2 = Expression.Invoke(lambdaVar2, var2Param.Destination, destination); From 23a508973a2f31fa1f0dee7a84a577d93a2d3b76 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 3 Feb 2025 19:57:25 +0500 Subject: [PATCH 283/363] fix IgnoreNullValues for RecordType when used Map mapping --- src/Mapster/Adapters/RecordTypeAdapter.cs | 34 ++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 66cb38a7..81c1193e 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -15,6 +15,8 @@ internal class RecordTypeAdapter : ClassAdapter protected override int Score => -149; protected override bool UseTargetValue => false; + private List SkipIgnoreNullValuesMemberMap = new List(); + protected override bool CanMap(PreCompileArgument arg) { return arg.DestinationType.IsRecordType(); @@ -33,6 +35,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E { //new TDestination(src.Prop1, src.Prop2) + SkipIgnoreNullValuesMemberMap.Clear(); Expression installExpr; if (arg.GetConstructUsing() != null || arg.DestinationType == null) @@ -83,8 +86,14 @@ protected override Expression CreateInstantiationExpression(Expression source, E var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); - if (arg.MapType == MapType.MapToTarget && arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull()) // add IgnoreNullValues support + if (arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull()) // add IgnoreNullValues support { + if (arg.MapType != MapType.MapToTarget) + { + SkipIgnoreNullValuesMemberMap.Add(member); + continue; + } + if (adapt is ConditionalExpression condEx) { if (condEx.Test is BinaryExpression { NodeType: ExpressionType.Equal } binEx && @@ -160,6 +169,29 @@ protected override Expression CreateBlockExpression(Expression source, Expressio var lines = new List(); + if (arg.MapType != MapType.MapToTarget) + { + foreach (var member in SkipIgnoreNullValuesMemberMap) + { + + var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); + + if (adapt is ConditionalExpression condEx) + { + if (condEx.Test is BinaryExpression { NodeType: ExpressionType.Equal } binEx && + binEx.Left == member.Getter && + binEx.Right is ConstantExpression { Value: null }) + adapt = condEx.IfFalse; + } + adapt = member.DestinationMember.SetExpression(destination, adapt); + var sourceCondition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type)); + + + lines.Add(Expression.IfThen(sourceCondition, adapt)); + } + } + + foreach (var member in members) { if (member.DestinationMember.SetterModifier == AccessModifier.None && member.UseDestinationValue) From 5a5623b781d2e6c4ae111b2046f135723ad7a8ed Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 26 Feb 2025 08:32:40 +0500 Subject: [PATCH 284/363] FIx RequiredProperty --- src/Mapster.Immutable/ImmutableAdapter.cs | 2 +- src/Mapster.JsonNet/JsonAdapter.cs | 2 +- src/Mapster.Tests/Mapster.Tests.csproj | 1 + .../WhenMappingRecordRegression.cs | 35 +++++++++++++++++++ src/Mapster/Adapters/ArrayAdapter.cs | 2 +- src/Mapster/Adapters/BaseAdapter.cs | 15 ++++++-- src/Mapster/Adapters/BaseClassAdapter.cs | 20 +++++++++++ src/Mapster/Adapters/ClassAdapter.cs | 16 +++++++-- src/Mapster/Adapters/CollectionAdapter.cs | 2 +- src/Mapster/Adapters/DelegateAdapter.cs | 2 +- src/Mapster/Adapters/DictionaryAdapter.cs | 2 +- .../Adapters/MultiDimensionalArrayAdapter.cs | 2 +- src/Mapster/Adapters/ObjectAdapter.cs | 2 +- src/Mapster/Adapters/PrimitiveAdapter.cs | 2 +- src/Mapster/Adapters/RecordTypeAdapter.cs | 2 +- 15 files changed, 92 insertions(+), 15 deletions(-) diff --git a/src/Mapster.Immutable/ImmutableAdapter.cs b/src/Mapster.Immutable/ImmutableAdapter.cs index 9ab7a3a5..596cac4d 100644 --- a/src/Mapster.Immutable/ImmutableAdapter.cs +++ b/src/Mapster.Immutable/ImmutableAdapter.cs @@ -69,7 +69,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio return Expression.Empty(); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { return CreateInstantiationExpression(source, arg); } diff --git a/src/Mapster.JsonNet/JsonAdapter.cs b/src/Mapster.JsonNet/JsonAdapter.cs index c44fe773..75691f7a 100644 --- a/src/Mapster.JsonNet/JsonAdapter.cs +++ b/src/Mapster.JsonNet/JsonAdapter.cs @@ -51,7 +51,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio throw new System.NotImplementedException(); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { throw new System.NotImplementedException(); } diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index 1fd66a01..438405ee 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -7,6 +7,7 @@ Mapster.Tests.snk true true + 11.0 diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index b1c41f92..90cb7773 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -422,6 +422,25 @@ public void WhenRecordTypeWorksWithUseDestinationValueAndIgnoreNullValues() } + [TestMethod] + public void RequiredProperty() + { + var source = new Person553 { FirstMidName = "John", LastName = "Dow" }; + var destination = new Person554 { ID = 245, FirstMidName = "Mary", LastName = "Dow" }; + + TypeAdapterConfig.NewConfig() + //.Map(dest => dest.ID, source => 0) + .Ignore(x => x.ID); + + var s = source.BuildAdapter().CreateMapToTargetExpression(); + + var result = source.Adapt(destination); + + result.ID.ShouldBe(245); + result.FirstMidName.ShouldBe(source.FirstMidName); + result.LastName.ShouldBe(source.LastName); + } + #region NowNotWorking /// @@ -448,6 +467,22 @@ public void CollectionUpdate() #region TestClasses + + public class Person553 + { + + public string LastName { get; set; } + public string FirstMidName { get; set; } + } + + public class Person554 + { + public required int ID { get; set; } + public string LastName { get; set; } + public string FirstMidName { get; set; } + } + + public class SourceFromTestUseDestValue { public int? A { get; set; } diff --git a/src/Mapster/Adapters/ArrayAdapter.cs b/src/Mapster/Adapters/ArrayAdapter.cs index b2f4eaed..37f4c5e6 100644 --- a/src/Mapster/Adapters/ArrayAdapter.cs +++ b/src/Mapster/Adapters/ArrayAdapter.cs @@ -70,7 +70,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio return CreateArraySet(source, destination, arg); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { if (arg.DestinationType.GetTypeInfo().IsAssignableFrom(source.Type.GetTypeInfo())) return source; diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 366f1eeb..ade2a62f 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -221,7 +221,17 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de vars.Add(src); transformedSource = src; } - var set = CreateInstantiationExpression(transformedSource, destination, arg); + + Expression? set; + var requiremembers = arg.DestinationType.GetProperties() + .Where(x => x.GetCustomAttributes() + .Any(y => y.GetType() == typeof(System.Runtime.CompilerServices.RequiredMemberAttribute))); + + if (requiremembers.Count() != 0) + set = CreateInlineExpression(source, arg, true); + else + set = CreateInstantiationExpression(transformedSource, destination, arg); + if (destination != null && (UseTargetValue || arg.UseDestinationValue) && arg.GetConstructUsing()?.Parameters.Count != 2) { if (destination.CanBeNull()) @@ -388,7 +398,8 @@ private static Expression InvokeMapping( } protected abstract Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg); - protected abstract Expression? CreateInlineExpression(Expression source, CompileArgument arg); + protected abstract Expression? CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false); + protected Expression CreateInstantiationExpression(Expression source, CompileArgument arg) { diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 309687a5..24fd8e99 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -54,6 +54,15 @@ select fn(src, destinationMember, arg)) Destination = (ParameterExpression?)destination, UseDestinationValue = arg.MapType != MapType.Projection && destinationMember.UseDestinationValue(arg), }; + if(getter == null && destinationMember.Info is PropertyInfo propinfo) + { + if (propinfo.GetCustomAttributes() + .Any(y => y.GetType() == typeof(System.Runtime.CompilerServices.RequiredMemberAttribute))) + { + getter = destinationMember.Type.CreateDefault(); + } + } + if (arg.MapType == MapType.MapToTarget && getter == null && arg.DestinationType.IsRecordType()) { getter = TryRestoreRecordMember(destinationMember, recordRestorMemberModel, destination) ?? getter; @@ -197,6 +206,17 @@ protected virtual ClassModel GetSetterModel(CompileArgument arg) }; } + protected virtual ClassModel GetOnlyRequiredPropertySetterModel(CompileArgument arg) + { + return new ClassModel + { + Members = arg.DestinationType.GetFieldsAndProperties(true) + .Where(x => x.GetType() == typeof(PropertyModel)) + .Where(y => ((PropertyInfo)y.Info).GetCustomAttributes() + .Any(y => y.GetType() == typeof(System.Runtime.CompilerServices.RequiredMemberAttribute))) + }; + } + protected Expression? TryRestoreRecordMember(IMemberModelEx member, ClassModel? restorRecordModel, Expression? destination) { if (restorRecordModel != null && destination != null) diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 6ae450fa..5e20f0a2 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -191,7 +191,7 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre new[] { member.Destination, memberAsObject }); } - protected override Expression? CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression? CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { //new TDestination { // Prop1 = convert(src.Prop1), @@ -202,8 +202,18 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre var memberInit = exp as MemberInitExpression; var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; var contructorMembers = newInstance.Arguments.OfType().Select(me => me.Member).ToArray(); - var classModel = GetSetterModel(arg); - var classConverter = CreateClassConverter(source, classModel, arg); + ClassModel? classModel; + ClassMapping? classConverter; + if (IsRequiredOnly) + { + classModel = GetOnlyRequiredPropertySetterModel(arg); + classConverter = CreateClassConverter(source, classModel, arg, ctorMapping: true); + } + else + { + classModel = GetSetterModel(arg); + classConverter = CreateClassConverter(source, classModel, arg); + } var members = classConverter.Members; var lines = new List(); diff --git a/src/Mapster/Adapters/CollectionAdapter.cs b/src/Mapster/Adapters/CollectionAdapter.cs index 861b1383..01189614 100644 --- a/src/Mapster/Adapters/CollectionAdapter.cs +++ b/src/Mapster/Adapters/CollectionAdapter.cs @@ -115,7 +115,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio return Expression.Block(actions); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { if (arg.DestinationType.GetTypeInfo().IsAssignableFrom(source.Type.GetTypeInfo()) && (arg.Settings.ShallowCopyForSameType == true || arg.MapType == MapType.Projection)) diff --git a/src/Mapster/Adapters/DelegateAdapter.cs b/src/Mapster/Adapters/DelegateAdapter.cs index 0c16e315..2eb33ab3 100644 --- a/src/Mapster/Adapters/DelegateAdapter.cs +++ b/src/Mapster/Adapters/DelegateAdapter.cs @@ -30,7 +30,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio return Expression.Empty(); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { return CreateInstantiationExpression(source, arg); } diff --git a/src/Mapster/Adapters/DictionaryAdapter.cs b/src/Mapster/Adapters/DictionaryAdapter.cs index 25af8e3b..8d93aabd 100644 --- a/src/Mapster/Adapters/DictionaryAdapter.cs +++ b/src/Mapster/Adapters/DictionaryAdapter.cs @@ -181,7 +181,7 @@ private Expression CreateSetFromKvp(Expression kvp, Expression key, Expression d return destSetFn(destination, key, value); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { //new TDestination { // { "Prop1", convert(src.Prop1) }, diff --git a/src/Mapster/Adapters/MultiDimensionalArrayAdapter.cs b/src/Mapster/Adapters/MultiDimensionalArrayAdapter.cs index 01fa6931..8c44d04a 100644 --- a/src/Mapster/Adapters/MultiDimensionalArrayAdapter.cs +++ b/src/Mapster/Adapters/MultiDimensionalArrayAdapter.cs @@ -91,7 +91,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio return CreateArraySet(source, destination, arg); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { throw new NotImplementedException(); } diff --git a/src/Mapster/Adapters/ObjectAdapter.cs b/src/Mapster/Adapters/ObjectAdapter.cs index 71817886..3140549a 100644 --- a/src/Mapster/Adapters/ObjectAdapter.cs +++ b/src/Mapster/Adapters/ObjectAdapter.cs @@ -27,7 +27,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio return Expression.Empty(); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { return CreateInstantiationExpression(source, arg); } diff --git a/src/Mapster/Adapters/PrimitiveAdapter.cs b/src/Mapster/Adapters/PrimitiveAdapter.cs index cfc7c809..320dd0bc 100644 --- a/src/Mapster/Adapters/PrimitiveAdapter.cs +++ b/src/Mapster/Adapters/PrimitiveAdapter.cs @@ -92,7 +92,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio throw new NotImplementedException(); } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { throw new NotImplementedException(); } diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 81c1193e..77f75f92 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -27,7 +27,7 @@ protected override bool CanInline(Expression source, Expression? destination, Co return false; } - protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) + protected override Expression CreateInlineExpression(Expression source, CompileArgument arg, bool IsRequiredOnly = false) { return base.CreateInstantiationExpression(source, arg); } From 1cba2fd36f1136f4b45803057f7c02ddd32bac77 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 26 Feb 2025 09:50:40 +0500 Subject: [PATCH 285/363] Temporary disable for RecordType --- src/Mapster/Adapters/BaseClassAdapter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 24fd8e99..1806f7bd 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -54,7 +54,8 @@ select fn(src, destinationMember, arg)) Destination = (ParameterExpression?)destination, UseDestinationValue = arg.MapType != MapType.Projection && destinationMember.UseDestinationValue(arg), }; - if(getter == null && destinationMember.Info is PropertyInfo propinfo) + if(getter == null && !arg.DestinationType.IsRecordType() + && destinationMember.Info is PropertyInfo propinfo) { if (propinfo.GetCustomAttributes() .Any(y => y.GetType() == typeof(System.Runtime.CompilerServices.RequiredMemberAttribute))) From cecfa4fda4ed7b63189ca9136e2bafa54c5339f3 Mon Sep 17 00:00:00 2001 From: Joost Molenkamp Date: Thu, 27 Feb 2025 17:43:38 +0100 Subject: [PATCH 286/363] Use actual types for mapping when configuring --- src/Mapster.Tests/WhenHandlingUnmappedMembers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenHandlingUnmappedMembers.cs b/src/Mapster.Tests/WhenHandlingUnmappedMembers.cs index f6bcf0fe..6c0b3572 100644 --- a/src/Mapster.Tests/WhenHandlingUnmappedMembers.cs +++ b/src/Mapster.Tests/WhenHandlingUnmappedMembers.cs @@ -19,7 +19,7 @@ public void TestCleanup() public void No_Errors_Thrown_With_Default_Configuration_On_Unmapped_Primitive() { TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = false; - TypeAdapterConfig.NewConfig().Compile(); + TypeAdapterConfig.NewConfig().Compile(); var source = new SimplePoco {Id = Guid.NewGuid(), Name = "TestName"}; From 2f8d20beeb38d2eb0653406eb20dc3e382095b22 Mon Sep 17 00:00:00 2001 From: Joost Molenkamp Date: Thu, 27 Feb 2025 18:55:42 +0100 Subject: [PATCH 287/363] Add test for mapping to dictionary with key containing periods --- .../WhenMappingWithDictionary.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingWithDictionary.cs b/src/Mapster.Tests/WhenMappingWithDictionary.cs index df38030b..8d8eff42 100644 --- a/src/Mapster.Tests/WhenMappingWithDictionary.cs +++ b/src/Mapster.Tests/WhenMappingWithDictionary.cs @@ -37,7 +37,6 @@ public void Object_To_Dictionary() dict["Name"].ShouldBe(poco.Name); } - [TestMethod] public void Object_To_Dictionary_Map() { @@ -57,6 +56,25 @@ public void Object_To_Dictionary_Map() dict["Name"].ShouldBe(poco.Name); } + [TestMethod] + public void Object_To_Dictionary_Map_With_Periods() + { + var poco = new SimplePoco + { + Id = Guid.NewGuid(), + Name = "test", + }; + + var config = new TypeAdapterConfig(); + config.NewConfig>() + .Map("Key.With.Periods", c => c.Id); + var dict = poco.Adapt>(config); + + dict.Count.ShouldBe(2); + dict["Key.With.Periods"].ShouldBe(poco.Id); + dict["Name"].ShouldBe(poco.Name); + } + [TestMethod] public void Object_To_Dictionary_CamelCase() { From 851d491851a1ed9c29bcff3f88000d3999b37470 Mon Sep 17 00:00:00 2001 From: Joost Molenkamp Date: Thu, 27 Feb 2025 19:11:44 +0100 Subject: [PATCH 288/363] Allow mapping to dictionary key containing periods --- src/Mapster.Tests/WhenMappingWithDictionary.cs | 17 +++++++++++++++++ src/Mapster/Adapters/DictionaryAdapter.cs | 2 +- src/Mapster/Compile/CompileArgument.cs | 13 +++++++------ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingWithDictionary.cs b/src/Mapster.Tests/WhenMappingWithDictionary.cs index 8d8eff42..8b53a3f6 100644 --- a/src/Mapster.Tests/WhenMappingWithDictionary.cs +++ b/src/Mapster.Tests/WhenMappingWithDictionary.cs @@ -172,6 +172,23 @@ public void Dictionary_To_Object_Map() poco.Name.ShouldBeNull(); } + [TestMethod] + public void Dictionary_To_Object_Map_With_Periods() + { + var dict = new Dictionary + { + ["Key.With.Periods"] = Guid.NewGuid(), + ["Foo"] = "test", + }; + + TypeAdapterConfig, SimplePoco>.NewConfig() + .Map(c => c.Id, "Key.With.Periods"); + + var poco = TypeAdapter.Adapt(dict); + poco.Id.ShouldBe(dict["Key.With.Periods"]); + poco.Name.ShouldBeNull(); + } + [TestMethod] public void Dictionary_To_Object_CamelCase() { diff --git a/src/Mapster/Adapters/DictionaryAdapter.cs b/src/Mapster/Adapters/DictionaryAdapter.cs index 25af8e3b..f81249b4 100644 --- a/src/Mapster/Adapters/DictionaryAdapter.cs +++ b/src/Mapster/Adapters/DictionaryAdapter.cs @@ -219,7 +219,7 @@ protected override Expression CreateInlineExpression(Expression source, CompileA protected override ClassModel GetSetterModel(CompileArgument arg) { //get member name from map - var destNames = arg.GetDestinationNames().AsEnumerable(); + var destNames = arg.GetDestinationNames(false).AsEnumerable(); //get member name from properties if (arg.SourceType.GetDictionaryType() == null) diff --git a/src/Mapster/Compile/CompileArgument.cs b/src/Mapster/Compile/CompileArgument.cs index 2e15637a..5dd90b4b 100644 --- a/src/Mapster/Compile/CompileArgument.cs +++ b/src/Mapster/Compile/CompileArgument.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using Mapster.Utils; namespace Mapster { @@ -20,16 +19,18 @@ public class CompileArgument internal HashSet GetSourceNames() { return _srcNames ??= (from it in Settings.Resolvers - where it.SourceMemberName != null - select it.SourceMemberName!.Split('.').First()).ToHashSet(); + where it.SourceMemberName != null + select it.SourceMemberName!.Split('.').First()).ToHashSet(); } private HashSet? _destNames; - internal HashSet GetDestinationNames() + internal HashSet GetDestinationNames(bool split = true) { return _destNames ??= (from it in Settings.Resolvers - where it.DestinationMemberName != null - select it.DestinationMemberName.Split('.').First()).ToHashSet(); + where it.DestinationMemberName != null + select split + ? it.DestinationMemberName.Split('.').First() + : it.DestinationMemberName).ToHashSet(); } private bool _fetchConstructUsing; From b45ef61ad93ae4f56bc3cea5f11797916fc2fe4f Mon Sep 17 00:00:00 2001 From: Joost Molenkamp Date: Thu, 27 Feb 2025 17:05:02 +0100 Subject: [PATCH 289/363] Add tests for mapping members containing periods --- .../WhenMappingMemberNameContainingPeriod.cs | 277 ++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 src/Mapster.Tests/WhenMappingMemberNameContainingPeriod.cs diff --git a/src/Mapster.Tests/WhenMappingMemberNameContainingPeriod.cs b/src/Mapster.Tests/WhenMappingMemberNameContainingPeriod.cs new file mode 100644 index 00000000..d86ee07d --- /dev/null +++ b/src/Mapster.Tests/WhenMappingMemberNameContainingPeriod.cs @@ -0,0 +1,277 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Mapster.Tests; + +[TestClass] +public class WhenMappingMemberNameContainingPeriod +{ + private const string MemberName = "Some.Property.With.Periods"; + + [TestMethod] + public void Property_Name_Containing_Periods_Is_Supported() + { + // Create a target type with a property that contains periods + Type targetType = new TestTypeBuilder() + .AddProperty(MemberName) + .CreateType(); + + // Call the local function defined below, the actual test method + CallStaticLocalTestMethod( + nameof(Test), + new Type[] { targetType }); + + // The actual test method adapting Source to the target type and back to the source to verify mapping the property with periods + static void Test() + { + // Get expression for mapping the property with periods + Expression> getPropertyExpression = BuildGetPropertyExpression(MemberName); + + // Create the config + TypeAdapterConfig + .NewConfig() + .TwoWays() + .Map(getPropertyExpression, src => src.Value); + + // Execute the mapping both ways + Source source = new() { Value = 551 }; + TTarget target = source.Adapt(); + Source adaptedSource = target.Adapt(); + + Assert.AreEqual(source.Value, adaptedSource.Value); + } + } + + [TestMethod] + public void Constructor_Parameter_Name_Containing_Periods_Is_Supported() + { + // Create a target type with a property that contains periods + Type targetTypeWithProperty = new TestTypeBuilder() + .AddProperty(MemberName) + .CreateType(); + + // Create a target type with a constructor parameter that contains periods + Type targetTypeWithConstructor = new TestTypeBuilder() + .AddConstructorWithReadOnlyProperty(MemberName) + .CreateType(); + + // Call the local function defined below, the actual test method + CallStaticLocalTestMethod( + nameof(Test), + new Type[] { targetTypeWithProperty, targetTypeWithConstructor }); + + // The actual test method + static void Test() + where TWithProperty : new() + { + // Create the config + TypeAdapterConfig + .NewConfig() + .TwoWays() + .MapToConstructor(true); + + // Create delegate for setting the property value on TWithProperty + Expression> setPropertyExpression = BuildSetPropertyExpression(MemberName); + Action setProperty = setPropertyExpression.Compile(); + + // Create the source object + int value = 551; + TWithProperty source = new(); + setProperty.Invoke(source, value); + + // Map + TWithConstructor target = source.Adapt(); + TWithProperty adaptedSource = target.Adapt(); + + // Create delegate for getting the property from TWithProperty + Expression> getPropertyExpression = BuildGetPropertyExpression(MemberName); + Func getProperty = getPropertyExpression.Compile(); + + // Verify + Assert.AreEqual(value, getProperty.Invoke(adaptedSource)); + } + } + + [TestMethod] + public void Using_Property_Path_String_Is_Supported() + { + // Create a target type with a property that contains periods + Type targetType = new TestTypeBuilder() + .AddProperty(MemberName) + .CreateType(); + + // Create the config, both ways + TypeAdapterConfig + .GlobalSettings + .NewConfig(typeof(Source), targetType) + .Map(MemberName, nameof(Source.Value)); + TypeAdapterConfig + .GlobalSettings + .NewConfig(targetType, typeof(Source)) + .Map(nameof(Source.Value), MemberName); + + // Execute the mapping both ways + Source source = new() { Value = 551 }; + object target = source.Adapt(typeof(Source), targetType); + Source adaptedSource = target.Adapt(); + + Assert.AreEqual(source.Value, adaptedSource.Value); + } + + private static void CallStaticLocalTestMethod(string methodName, Type[] genericArguments, [CallerMemberName] string caller = "Unknown") + { + MethodInfo genericMethodInfo = typeof(WhenMappingMemberNameContainingPeriod) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) + .Single(x => x.Name.Contains($"<{caller}>") && x.Name.Contains(methodName)); + + MethodInfo method = genericMethodInfo.MakeGenericMethod(genericArguments); + + method.Invoke(null, null); + } + + private static Expression> BuildGetPropertyExpression(string propertyName) + { + ParameterExpression param = Expression.Parameter(typeof(T), "x"); + MemberExpression property = Expression.Property(param, propertyName); + return Expression.Lambda>(property, param); + } + + private static Expression> BuildSetPropertyExpression(string propertyName) + { + ParameterExpression param = Expression.Parameter(typeof(T), "x"); + ParameterExpression value = Expression.Parameter(typeof(TProperty), "value"); + MemberExpression property = Expression.Property(param, propertyName); + BinaryExpression assign = Expression.Assign(property, value); + return Expression.Lambda>(assign, param, value); + } + + private class Source + { + public int Value { get; set; } + } + + private class TestTypeBuilder + { + private readonly TypeBuilder _typeBuilder; + + public TestTypeBuilder() + { + AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly( + new AssemblyName("Types"), + AssemblyBuilderAccess.Run); + ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(""); + _typeBuilder = moduleBuilder.DefineType( + "Types.Target", + TypeAttributes.Public | + TypeAttributes.Class | + TypeAttributes.Sealed | + TypeAttributes.AutoClass | + TypeAttributes.AnsiClass | + TypeAttributes.BeforeFieldInit | + TypeAttributes.AutoLayout, + null); + } + + public TestTypeBuilder AddConstructorWithReadOnlyProperty(string parameterName) + { + // Add read-only property + FieldBuilder fieldBuilder = AddProperty(parameterName, false); + + // Build the constructor with the parameter for the property + ConstructorBuilder constructorBuilder = _typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + new Type[] { typeof(TParameter) }); + + // Define the parameter name + constructorBuilder.DefineParameter(1, ParameterAttributes.None, MemberName); + + ILGenerator constructorIL = constructorBuilder.GetILGenerator(); + + // Call the base class constructor + constructorIL.Emit(OpCodes.Ldarg_0); + constructorIL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); + + // Set the property value + constructorIL.Emit(OpCodes.Ldarg_0); + constructorIL.Emit(OpCodes.Ldarg_1); + constructorIL.Emit(OpCodes.Stfld, fieldBuilder); + + constructorIL.Emit(OpCodes.Ret); + + return this; + } + + public TestTypeBuilder AddProperty(string propertyName) + { + AddProperty(propertyName, true); + return this; + } + + private FieldBuilder AddProperty(string propertyName, bool addSetter) + { + Type propertyType = typeof(T); + FieldBuilder fieldBuilder = _typeBuilder.DefineField($"_{propertyName}", propertyType, FieldAttributes.Private); + PropertyBuilder propertyBuilder = _typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null); + + AddGetMethod(_typeBuilder, propertyBuilder, fieldBuilder, propertyName, propertyType); + if (addSetter) + { + AddSetMethod(_typeBuilder, propertyBuilder, fieldBuilder, propertyName, propertyType); + } + + return fieldBuilder; + } + + public Type CreateType() => _typeBuilder.CreateType(); + + private static PropertyBuilder AddGetMethod(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, FieldBuilder fieldBuilder, string propertyName, Type propertyType) + { + MethodBuilder getMethodBuilder = typeBuilder.DefineMethod( + "get_" + propertyName, + MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, + propertyType, + Type.EmptyTypes); + ILGenerator getMethodGenerator = getMethodBuilder.GetILGenerator(); + + getMethodGenerator.Emit(OpCodes.Ldarg_0); + getMethodGenerator.Emit(OpCodes.Ldfld, fieldBuilder); + getMethodGenerator.Emit(OpCodes.Ret); + + propertyBuilder.SetGetMethod(getMethodBuilder); + + return propertyBuilder; + } + + private static PropertyBuilder AddSetMethod(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, FieldBuilder fieldBuilder, string propertyName, Type propertyType) + { + MethodBuilder setMethodBuilder = typeBuilder.DefineMethod( + $"set_{propertyName}", + MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, + null, + new Type[] { propertyType }); + + ILGenerator setMethodGenerator = setMethodBuilder.GetILGenerator(); + Label modifyProperty = setMethodGenerator.DefineLabel(); + Label exitSet = setMethodGenerator.DefineLabel(); + + setMethodGenerator.MarkLabel(modifyProperty); + setMethodGenerator.Emit(OpCodes.Ldarg_0); + setMethodGenerator.Emit(OpCodes.Ldarg_1); + setMethodGenerator.Emit(OpCodes.Stfld, fieldBuilder); + + setMethodGenerator.Emit(OpCodes.Nop); + setMethodGenerator.MarkLabel(exitSet); + setMethodGenerator.Emit(OpCodes.Ret); + + propertyBuilder.SetSetMethod(setMethodBuilder); + + return propertyBuilder; + } + } +} From d8f7693ea02f5e30179e59512072bdd454f533e2 Mon Sep 17 00:00:00 2001 From: Joost Molenkamp Date: Tue, 4 Mar 2025 21:03:15 +0100 Subject: [PATCH 290/363] Allow properties or fields to contain periods --- src/Mapster/Utils/ExpressionEx.cs | 94 +++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/src/Mapster/Utils/ExpressionEx.cs b/src/Mapster/Utils/ExpressionEx.cs index a1ab8550..f8011b5b 100644 --- a/src/Mapster/Utils/ExpressionEx.cs +++ b/src/Mapster/Utils/ExpressionEx.cs @@ -2,7 +2,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Linq.Expressions; using System.Reflection; namespace Mapster.Utils @@ -19,32 +21,112 @@ public static Expression Assign(Expression left, Expression right) public static Expression PropertyOrFieldPath(Expression expr, string path) { - var props = path.Split('.'); - return props.Aggregate(expr, PropertyOrField); + Expression current = expr; + string[] props = path.Split('.'); + + for (int i = 0; i < props.Length; i++) + { + if (IsDictionaryKey(current, props[i], out Expression? next)) + { + current = next; + continue; + } + + if (IsPropertyOrField(current, props[i], out next)) + { + current = next; + continue; + } + + // For dynamically built types, it is possible to have periods in the property name. + // Rejoin an incrementing number of parts with periods to try and find a property match. + if (IsPropertyOrFieldPathWithPeriods(current, props[i..], out next, out int combinationLength)) + { + current = next; + i += combinationLength - 1; + continue; + } + + throw new ArgumentException($"'{props[i]}' is not a member of type '{current.Type.FullName}'", nameof(path)); + } + + return current; } - private static Expression PropertyOrField(Expression expr, string prop) + private static bool IsPropertyOrFieldPathWithPeriods(Expression expr, string[] path, [NotNullWhen(true)] out Expression? propExpr, out int combinationLength) + { + if (path.Length < 2) + { + propExpr = null; + combinationLength = 0; + return false; + } + + for (int count = 2; count <= path.Length; count++) + { + string prop = string.Join('.', path[..count]); + if (IsPropertyOrField(expr, prop, out propExpr)) + { + combinationLength = count; + return true; + } + } + + propExpr = null; + combinationLength = 0; + return false; + } + + private static bool IsDictionaryKey(Expression expr, string prop, [NotNullWhen(true)] out Expression? propExpr) { var type = expr.Type; var dictType = type.GetDictionaryType(); - if (dictType?.GetGenericArguments()[0] == typeof(string)) + + if (dictType?.GetGenericArguments()[0] != typeof(string)) { + propExpr = null; + return false; + } + var method = typeof(MapsterHelper).GetMethods() .First(m => m.Name == nameof(MapsterHelper.GetValueOrDefault) && m.GetParameters()[0].ParameterType.Name == dictType.Name) .MakeGenericMethod(dictType.GetGenericArguments()); - return Expression.Call(method, expr.To(type), Expression.Constant(prop)); + propExpr = Expression.Call(method, expr.To(type), Expression.Constant(prop)); + return true; } + private static bool IsPropertyOrField(Expression expr, string prop, [NotNullWhen(true)] out Expression? propExpr) + { + Type type = expr.Type; + if (type.GetTypeInfo().IsInterface) { var allTypes = type.GetAllInterfaces(); var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var interfaceType = allTypes.FirstOrDefault(it => it.GetProperty(prop, flags) != null || it.GetField(prop, flags) != null); if (interfaceType != null) + { expr = Expression.Convert(expr, interfaceType); + type = expr.Type; + } } - return Expression.PropertyOrField(expr, prop); + + MemberInfo? propertyOrField = type + .GetMember( + prop, + MemberTypes.Field | MemberTypes.Property, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy) + .FirstOrDefault(); + + propExpr = propertyOrField?.MemberType switch + { + MemberTypes.Property => Expression.Property(expr, (PropertyInfo)propertyOrField), + MemberTypes.Field => Expression.Field(expr, (FieldInfo)propertyOrField), + _ => null + }; + + return propExpr != null; } private static bool IsReferenceAssignableFrom(this Type destType, Type srcType) From 8d1c7cb87c4b05b8c86d3633833f2d28679404df Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 2 May 2025 07:28:02 +0500 Subject: [PATCH 291/363] fix MapToTarget Polymorphic mapping to null value Property --- src/Mapster.Tests/WhenMappingDerived.cs | 76 +++++++++++++++++++++++++ src/Mapster/Adapters/BaseAdapter.cs | 9 ++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingDerived.cs b/src/Mapster.Tests/WhenMappingDerived.cs index 01bfac41..e6399389 100644 --- a/src/Mapster.Tests/WhenMappingDerived.cs +++ b/src/Mapster.Tests/WhenMappingDerived.cs @@ -44,6 +44,82 @@ public void WhenMappingDerivedWithoutMembers() Assert.AreEqual(inputEntity.Id, result.Id); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/794 + /// + [TestMethod] + public void WhenMapToTargetDerivedWithNullRegression() + { + var config = new TypeAdapterConfig(); + + config + .NewConfig() + .Map(dest => dest.Nested, src => src.NestedDTO) + .IgnoreNonMapped(true) + .IgnoreNullValues(true); + config + .NewConfig() + .Map(dest => dest.SomeBaseProperty, src => src.SomeBasePropertyDTO) + .Include() + .IgnoreNonMapped(true) + .IgnoreNullValues(true); + + config + .NewConfig() + .Map(dest => dest.SomeDerivedProperty, src => src.SomeDerivedPropertyDTO) + .IgnoreNonMapped(true) + .IgnoreNullValues(true); + config + .NewConfig() + .MapWith(src => src.Adapt()); + + + var container = new Container794(); + var containerDTO = new ContainerDTO794(); + + container.Nested = null; + containerDTO.NestedDTO = new DerivedDTO794(); + + containerDTO.Adapt(container, config); + + (container.Nested is Derived794E).ShouldBeTrue(); // is not Base794 type, MapWith is working when Polymorphic mapping to null + } + + internal class Derived794E : Derived794 + { + + } + + internal class Base794 + { + public string SomeBaseProperty { get; set; } + } + + internal class BaseDTO794 + { + public string SomeBasePropertyDTO { get; set; } + } + + internal class Derived794 : Base794 + { + public string SomeDerivedProperty { get; set; } + } + + internal class DerivedDTO794 : BaseDTO794 + { + public string SomeDerivedPropertyDTO { get; set; } + } + + internal class Container794 + { + public Base794 Nested { get; set; } + } + + internal class ContainerDTO794 + { + public BaseDTO794 NestedDTO { get; set; } + } + internal class BaseDto { public long Id { get; set; } diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 366f1eeb..cf3e5cb5 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -201,9 +201,12 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de drvdDest, Expression.TypeAs(destination, tuple.Destination)); blocks.Add(drvdDestAssign); - cond = Expression.AndAlso( - cond, - Expression.NotEqual(drvdDest, Expression.Constant(null, tuple.Destination))); + + // fix by https://github.com/MapsterMapper/Mapster/issues/794 + // This can be removed if it does not cause any other bugs. + // cond = Expression.AndAlso( + // cond, + // Expression.NotEqual(drvdDest, Expression.Constant(null, tuple.Destination))); } var adaptExpr = CreateAdaptExpressionCore(drvdSource, tuple.Destination, arg, destination: drvdDest); From e64dd8adcd1311c1cb9942756eedafe4200d8290 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 2 May 2025 08:09:28 +0500 Subject: [PATCH 292/363] Fix when using applySettings: true in MapWith and MapToTargetWith from mapping to primitive type --- src/Mapster/Adapters/BaseAdapter.cs | 43 +++++++++-------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 366f1eeb..1d0047ad 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -85,32 +85,6 @@ protected virtual Expression CreateExpressionBody(Expression source, Expression? if (CheckExplicitMapping && arg.Context.Config.RequireExplicitMapping && !arg.ExplicitMapping) throw new InvalidOperationException("Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists"); - #region CustomMappingPrimitiveImplimentation - - if (arg.Settings.MapToTargetPrimitive == true) - { - Expression dest; - - if (destination == null) - { - dest = arg.DestinationType.CreateDefault(); - } - else - dest = destination; - - var customConvert = arg.Context.Config.CreateMapToTargetInvokeExpressionBody(source.Type, arg.DestinationType, source, dest); - - arg.MapType = MapType.MapToTarget; - return customConvert; - } - - if (arg.Settings.MapWithToPrimitive == true) - { - return arg.Context.Config.CreateMapInvokeExpressionBody(source.Type, arg.DestinationType, source); - } - - #endregion CustomMappingPrimitiveImplimentation - var oldMaxDepth = arg.Context.MaxDepth; var oldDepth = arg.Context.Depth; try @@ -128,12 +102,21 @@ protected virtual Expression CreateExpressionBody(Expression source, Expression? arg.Context.Depth++; } - if (CanInline(source, destination, arg)) + if(arg.Settings.MapToTargetPrimitive == true + || arg.Settings.MapWithToPrimitive == true) + { + // skip inline mapping + } + else { - var exp = CreateInlineExpressionBody(source, arg); - if (exp != null) - return exp.To(arg.DestinationType, true); + if (CanInline(source, destination, arg)) + { + var exp = CreateInlineExpressionBody(source, arg); + if (exp != null) + return exp.To(arg.DestinationType, true); + } } + if (arg.Context.Running.Count > 1 && !arg.Context.Config.SelfContainedCodeGeneration && From 0ea08a311d67053a0e7c0c6e68f71e222ae61faf Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 2 May 2025 17:54:33 +0500 Subject: [PATCH 293/363] refactoring and drop MapWithPrimitive it working without special settings --- src/Mapster/Adapters/BaseAdapter.cs | 4 +--- src/Mapster/TypeAdapterSetter.cs | 5 ----- src/Mapster/TypeAdapterSettings.cs | 6 ------ 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 1d0047ad..7019f48d 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -102,8 +102,7 @@ protected virtual Expression CreateExpressionBody(Expression source, Expression? arg.Context.Depth++; } - if(arg.Settings.MapToTargetPrimitive == true - || arg.Settings.MapWithToPrimitive == true) + if(arg.Settings.MapToTargetPrimitive == true) { // skip inline mapping } @@ -116,7 +115,6 @@ protected virtual Expression CreateExpressionBody(Expression source, Expression? return exp.To(arg.DestinationType, true); } } - if (arg.Context.Running.Count > 1 && !arg.Context.Config.SelfContainedCodeGeneration && diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index a0581845..7e06d764 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -612,11 +612,6 @@ public TypeAdapterSetter MapWith(Expression Set(nameof(GenerateMapper), value); } - public bool? MapWithToPrimitive - { - get => Get(nameof(MapWithToPrimitive)); - set => Set(nameof(MapWithToPrimitive), value); - } - /// /// Not implemented /// From 0aa92903c7230159f311a300ec12e507caeecd57 Mon Sep 17 00:00:00 2001 From: hmoratopcs Date: Mon, 5 May 2025 19:20:51 +0200 Subject: [PATCH 294/363] Improve nullability annotations on the Adapt extension method A warning will appear if you try to map a nullable input to a non-nullable result: ` ProductDTO dto = ((Product?)null).Adapt();` --- src/Mapster/TypeAdapter.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 0ac37924..4c107adf 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Mapster.Models; @@ -24,7 +25,8 @@ public static ITypeAdapterBuilder BuildAdapter(this TSource so /// Destination type. /// Source object to adapt. /// Adapted destination type. - public static TDestination Adapt(this object? source) + [return: NotNullIfNotNull(nameof(source))] + public static TDestination? Adapt(this object? source) { return Adapt(source, TypeAdapterConfig.GlobalSettings); } @@ -36,14 +38,15 @@ public static TDestination Adapt(this object? source) /// Source object to adapt. /// Configuration /// Adapted destination type. - public static TDestination Adapt(this object? source, TypeAdapterConfig config) + [return: NotNullIfNotNull(nameof(source))] + public static TDestination? Adapt(this object? source, TypeAdapterConfig config) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (source == null) - return default!; + return default; var type = source.GetType(); var fn = config.GetDynamicMapFunction(type); - return fn(source); + return fn(source)!; } /// From d198ef27f7ab3727d672216751f2193bda81c935 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 19 Jan 2025 09:38:54 +0500 Subject: [PATCH 295/363] add support using MapWith as param in UseDestinatonValue --- ...WhenUseDestinatonValueMappingRegression.cs | 112 ++++++++++++++++++ src/Mapster/Adapters/BaseAdapter.cs | 15 +++ 2 files changed, 127 insertions(+) create mode 100644 src/Mapster.Tests/WhenUseDestinatonValueMappingRegression.cs diff --git a/src/Mapster.Tests/WhenUseDestinatonValueMappingRegression.cs b/src/Mapster.Tests/WhenUseDestinatonValueMappingRegression.cs new file mode 100644 index 00000000..5dea9da6 --- /dev/null +++ b/src/Mapster.Tests/WhenUseDestinatonValueMappingRegression.cs @@ -0,0 +1,112 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System.Collections.Generic; +using System.Linq; + +namespace Mapster.Tests; + +[TestClass] +public class WhenUseDestinatonValueMappingRegression +{ + [TestClass] + public class WhenUseDestinatonMappingRegression + { + [TestMethod] + public void UseDestinatonValueUsingMapWithasParam() + { + TypeAdapterConfig.GlobalSettings.Default.UseDestinationValue(m => m.SetterModifier == AccessModifier.None && + m.Type.IsGenericType && + m.Type.GetGenericTypeDefinition() == typeof(ICollection<>)); + + TypeAdapterConfig> + .NewConfig() + .MapWith(src => MapThumbnailDetailsData(src).ToList()); + + var channelSrc = new ChannelSource + { + ChannelId = "123", + Thumbnails = new ThumbnailDetailsSource + { + Default = new ThumbnailSource + { + Url = "https://www.youtube.com/default.jpg" + }, + Medium = new ThumbnailSource + { + Url = "https://www.youtube.com/medium.jpg" + }, + High = new ThumbnailSource + { + Url = "https://www.youtube.com/high.jpg" + } + }, + + TempThumbnails = new List() { 1, 2, 3 } + }; + + var script = channelSrc.BuildAdapter() + .CreateMapExpression(); + + var channelDest = channelSrc.Adapt(); + + channelDest.Thumbnails.Count.ShouldBe(3); + channelDest.TempThumbnails.Count.ShouldBe(3); + } + + + #region TestClasses + private static IEnumerable MapThumbnailDetailsData(ThumbnailDetailsSource thumbnailDetails) + { + + yield return MapThumbnail(thumbnailDetails.Default, "Default"); + yield return MapThumbnail(thumbnailDetails.Medium, "Medium"); + yield return MapThumbnail(thumbnailDetails.High, "High"); + } + + private static ThumbnailDestination MapThumbnail( + ThumbnailSource thumbnail, + string thumbnailType) => + new() + { + Type = thumbnailType, + Url = thumbnail.Url.Trim(), + }; + + + + public class ChannelDestination + { + public string ChannelId { get; set; } = default!; + public ICollection Thumbnails { get; } = new List(); + public ICollection TempThumbnails { get; } = new List(); + } + + public class ThumbnailDestination + { + public string Type { get; set; } = default!; + public string Url { get; set; } = default!; + } + + public class ChannelSource + { + public string ChannelId { get; set; } = default!; + public ThumbnailDetailsSource Thumbnails { get; set; } = default!; + public ICollection TempThumbnails { get; set; } = new List(); + + } + + public class ThumbnailDetailsSource + { + public ThumbnailSource? Default { get; set; } + public ThumbnailSource? Medium { get; set; } + public ThumbnailSource? High { get; set; } + } + + public class ThumbnailSource + { + public string Url { get; set; } = default!; + } + + #endregion TestClasses + } +} diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 366f1eeb..2fbcf32c 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -495,6 +495,21 @@ internal Expression CreateAdaptExpression(Expression source, Type destinationTyp if (transform != null) exp = transform.TransformFunc(exp.Type).Apply(arg.MapType, exp); } + else + { + if (exp.NodeType == ExpressionType.Call) + { + var argExt = new CompileArgument + { + DestinationType = arg.DestinationType, + SourceType = arg.DestinationType, + MapType = MapType.MapToTarget, + Context = arg.Context, + }; + + return CreateAdaptExpressionCore(exp, destinationType, argExt, mapping, destination); + } + } return exp.To(destinationType); } From 49576a5602a6c5bc76af931cac8b6bd541ad2d7c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 19 Jan 2025 19:47:04 +0500 Subject: [PATCH 296/363] refactoring test --- .../WhenUseDestinatonValueMappingRegression.cs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Mapster.Tests/WhenUseDestinatonValueMappingRegression.cs b/src/Mapster.Tests/WhenUseDestinatonValueMappingRegression.cs index 5dea9da6..4200ada6 100644 --- a/src/Mapster.Tests/WhenUseDestinatonValueMappingRegression.cs +++ b/src/Mapster.Tests/WhenUseDestinatonValueMappingRegression.cs @@ -14,10 +14,6 @@ public class WhenUseDestinatonMappingRegression [TestMethod] public void UseDestinatonValueUsingMapWithasParam() { - TypeAdapterConfig.GlobalSettings.Default.UseDestinationValue(m => m.SetterModifier == AccessModifier.None && - m.Type.IsGenericType && - m.Type.GetGenericTypeDefinition() == typeof(ICollection<>)); - TypeAdapterConfig> .NewConfig() .MapWith(src => MapThumbnailDetailsData(src).ToList()); @@ -43,10 +39,7 @@ public void UseDestinatonValueUsingMapWithasParam() TempThumbnails = new List() { 1, 2, 3 } }; - - var script = channelSrc.BuildAdapter() - .CreateMapExpression(); - + var channelDest = channelSrc.Adapt(); channelDest.Thumbnails.Count.ShouldBe(3); @@ -57,7 +50,6 @@ public void UseDestinatonValueUsingMapWithasParam() #region TestClasses private static IEnumerable MapThumbnailDetailsData(ThumbnailDetailsSource thumbnailDetails) { - yield return MapThumbnail(thumbnailDetails.Default, "Default"); yield return MapThumbnail(thumbnailDetails.Medium, "Medium"); yield return MapThumbnail(thumbnailDetails.High, "High"); @@ -73,11 +65,14 @@ private static ThumbnailDestination MapThumbnail( }; - public class ChannelDestination { public string ChannelId { get; set; } = default!; + + [UseDestinationValue] public ICollection Thumbnails { get; } = new List(); + + [UseDestinationValue] public ICollection TempThumbnails { get; } = new List(); } @@ -92,7 +87,6 @@ public class ChannelSource public string ChannelId { get; set; } = default!; public ThumbnailDetailsSource Thumbnails { get; set; } = default!; public ICollection TempThumbnails { get; set; } = new List(); - } public class ThumbnailDetailsSource From dbd6f370aa5acfe43232269cc66e8a83d72847ba Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 20 Jan 2025 16:03:42 +0500 Subject: [PATCH 297/363] update algoritm --- src/Mapster/Adapters/BaseAdapter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 2fbcf32c..0c3e4fba 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -497,7 +497,7 @@ internal Expression CreateAdaptExpression(Expression source, Type destinationTyp } else { - if (exp.NodeType == ExpressionType.Call) + if (exp.NodeType != ExpressionType.Invoke) { var argExt = new CompileArgument { @@ -507,7 +507,7 @@ internal Expression CreateAdaptExpression(Expression source, Type destinationTyp Context = arg.Context, }; - return CreateAdaptExpressionCore(exp, destinationType, argExt, mapping, destination); + return CreateAdaptExpressionCore(exp, destinationType, argExt, mapping, destination).To(destinationType); } } From 343874f810ac5b1e9bd5300875dfd18e5e398bb0 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 26 Jan 2025 14:20:28 +0500 Subject: [PATCH 298/363] Fix Poco Detection for Class --- .../WhenUsingDestinationValue.cs | 26 +++++++++++++++++++ src/Mapster/Utils/ReflectionUtils.cs | 6 +++++ 2 files changed, 32 insertions(+) diff --git a/src/Mapster.Tests/WhenUsingDestinationValue.cs b/src/Mapster.Tests/WhenUsingDestinationValue.cs index 086bf938..f3f7d3a8 100644 --- a/src/Mapster.Tests/WhenUsingDestinationValue.cs +++ b/src/Mapster.Tests/WhenUsingDestinationValue.cs @@ -35,6 +35,32 @@ public void MapUsingDestinationValue() poco.Strings.ShouldBe(strings); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/410 + /// + [TestMethod] + public void MappingToReadonlyPropertyWhenPocoDetectRegression() + { + var studentDto = new StudentDtoOrigin { Name = "Marta" }; + var student = studentDto.Adapt(); // No exception. + + student.Name.ShouldBe("John"); + } + + + public class StudentOrigin + { + [UseDestinationValue] + public string Name { get; } = "John"; // only readonly + } + + public class StudentDtoOrigin + { + + public string Name { get; set; } + } + + public class ContractingParty { public string Name { get; set; } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index dae413d8..ae128158 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -61,6 +61,12 @@ public static bool IsPoco(this Type type) if (type.IsConvertible()) return false; + if (type == typeof(Type) || type.BaseType == typeof(MulticastDelegate)) + return false; + + if (type.IsClass && type.GetProperties().Count() != 0) + return true; + return type.GetFieldsAndProperties().Any(it => (it.SetterModifier & (AccessModifier.Public | AccessModifier.NonPublic)) != 0); } From 9a210bda646cbe0a0db49c2fcffaf1e227049a49 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 22 Mar 2025 05:55:40 +0500 Subject: [PATCH 299/363] add RequireExplicitMappingPrimitive config --- src/Mapster/Adapters/PrimitiveAdapter.cs | 2 ++ src/Mapster/TypeAdapterConfig.cs | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Mapster/Adapters/PrimitiveAdapter.cs b/src/Mapster/Adapters/PrimitiveAdapter.cs index cfc7c809..ca8c861c 100644 --- a/src/Mapster/Adapters/PrimitiveAdapter.cs +++ b/src/Mapster/Adapters/PrimitiveAdapter.cs @@ -18,6 +18,8 @@ protected override bool CanMap(PreCompileArgument arg) protected override Expression CreateExpressionBody(Expression source, Expression? destination, CompileArgument arg) { + if (arg.SourceType != arg.DestinationType && arg.Context.Config.RequireExplicitMappingPrimitive && !arg.ExplicitMapping) + throw new InvalidOperationException("Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists"); if (arg.Settings.MapToTargetPrimitive == true) { diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index cb8ac881..5056bd42 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -82,6 +82,7 @@ private static List CreateRuleTemplate() public bool RequireDestinationMemberSource { get; set; } public bool RequireExplicitMapping { get; set; } + public bool RequireExplicitMappingPrimitive { get; set; } public bool AllowImplicitDestinationInheritance { get; set; } public bool AllowImplicitSourceInheritance { get; set; } = true; public bool SelfContainedCodeGeneration { get; set; } @@ -495,7 +496,7 @@ private LambdaExpression CreateMapInvokeExpression(Type sourceType, Type destina internal Expression CreateMapInvokeExpressionBody(Type sourceType, Type destinationType, Expression p) { - if (RequireExplicitMapping) + if (RequireExplicitMapping || RequireExplicitMappingPrimitive) { var key = new TypeTuple(sourceType, destinationType); _mapDict[key] = Compiler(CreateMapExpression(key, MapType.Map)); @@ -518,7 +519,7 @@ internal Expression CreateMapInvokeExpressionBody(Type sourceType, Type destinat internal Expression CreateMapToTargetInvokeExpressionBody(Type sourceType, Type destinationType, Expression p1, Expression p2) { - if (RequireExplicitMapping) + if (RequireExplicitMapping || RequireExplicitMappingPrimitive) { var key = new TypeTuple(sourceType, destinationType); _mapToTargetDict[key] = Compiler(CreateMapExpression(key, MapType.MapToTarget)); From ea2da89a135bd119389ae93a799a70f99e5bf34f Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 22 Mar 2025 06:35:16 +0500 Subject: [PATCH 300/363] add Test for RequireExplicitMappingPrimitive --- .../WhenExplicitMappingRequired.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/Mapster.Tests/WhenExplicitMappingRequired.cs b/src/Mapster.Tests/WhenExplicitMappingRequired.cs index b5d89492..25b43474 100644 --- a/src/Mapster.Tests/WhenExplicitMappingRequired.cs +++ b/src/Mapster.Tests/WhenExplicitMappingRequired.cs @@ -13,6 +13,7 @@ public class WhenExplicitMappingRequired public void TestCleanup() { TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = false; + TypeAdapterConfig.GlobalSettings.RequireExplicitMappingPrimitive = false; TypeAdapterConfig.GlobalSettings.Clear(); } @@ -140,8 +141,60 @@ public void UnmappedChildPocoShouldFailed() setter.Compile(); // Should fail here } + [TestMethod] + public void RequireExplicitMappingPrimitiveWork() + { + TypeAdapterConfig.GlobalSettings.RequireExplicitMappingPrimitive = true; + + TypeAdapterConfig.NewConfig(); + + Should.Throw(() => + { + TypeAdapterConfig.GlobalSettings.Compile(); // throw CompileException + }); + + byte byteSource = 10; + + byteSource.Adapt(); // Should work when the type is mapped to itself + + Should.Throw(() => + { + byteSource.Adapt(); // throw CompileException, Do not map to another primitive type without registering the configuration + }); + + Should.NotThrow(() => + { + TypeAdapterConfig.NewConfig(); + + byteSource.Adapt(); // Not throw CompileException when config is registering + }); + + Should.NotThrow(() => + { + TypeAdapterConfig.NewConfig() + .Map(dest=> dest.MyProperty, src=> int.Parse(src.MyProperty)); + // it work works because int.Parse return Type Int. Type is mapped to itself (int -> int) without config. + + var sourceMapconfig = new Source783() { MyProperty = "128" }; + var resultMapconfig = sourceMapconfig.Adapt(); + + resultMapconfig.MyProperty.ShouldBe(128); + }); + + } + + #region TestClasses + + public class Source783 + { + public string MyProperty { get; set; } = ""; + } + public class Destination783 + { + public int MyProperty { get; set; } + } public enum NameEnum { From 298614094d79afa933d21932d86a16ada5fde02a Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 7 Apr 2025 11:46:39 +0500 Subject: [PATCH 301/363] add EFCoreProjectToType and base test --- .../Enums/ProjectToTypeAutoMapping.cs | 11 +++ .../Utils/ProjectToTypeVisitors.cs | 49 ++++++++++ src/Mapster.EFCore.Tests/EFCoreTest.cs | 27 +++++- src/Mapster.EFCore/EFCoreExtensions.cs | 96 +++++++++++++++++++ src/Mapster/Adapters/BaseClassAdapter.cs | 57 +++++++++++ src/Mapster/Settings/SettingStore.cs | 6 ++ src/Mapster/TypeAdapterSettings.cs | 16 +++- src/Mapster/Utils/ReflectionUtils.cs | 2 +- 8 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 src/Mapster.Core/Enums/ProjectToTypeAutoMapping.cs create mode 100644 src/Mapster.Core/Utils/ProjectToTypeVisitors.cs create mode 100644 src/Mapster.EFCore/EFCoreExtensions.cs diff --git a/src/Mapster.Core/Enums/ProjectToTypeAutoMapping.cs b/src/Mapster.Core/Enums/ProjectToTypeAutoMapping.cs new file mode 100644 index 00000000..4e95caa0 --- /dev/null +++ b/src/Mapster.Core/Enums/ProjectToTypeAutoMapping.cs @@ -0,0 +1,11 @@ +using System; + +namespace Mapster.Enums +{ + public enum ProjectToTypeAutoMapping + { + AllTypes = 0, + WithoutCollections = 1, + OnlyPrimitiveTypes = 2, + } +} diff --git a/src/Mapster.Core/Utils/ProjectToTypeVisitors.cs b/src/Mapster.Core/Utils/ProjectToTypeVisitors.cs new file mode 100644 index 00000000..5ee11e7a --- /dev/null +++ b/src/Mapster.Core/Utils/ProjectToTypeVisitors.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Mapster.Utils +{ + public sealed class TopLevelMemberNameVisitor : ExpressionVisitor + { + public string? MemeberName { get; private set; } + + public override Expression Visit(Expression node) + { + if (node == null) + return null; + switch (node.NodeType) + { + case ExpressionType.MemberAccess: + { + if (string.IsNullOrEmpty(MemeberName)) + MemeberName = ((MemberExpression)node).Member.Name; + + return base.Visit(node); + } + } + + return base.Visit(node); + } + } + + public sealed class QuoteVisitor : ExpressionVisitor + { + public List Quotes { get; private set; } = new(); + + public override Expression Visit(Expression node) + { + if (node == null) + return null; + switch (node.NodeType) + { + case ExpressionType.Quote: + { + Quotes.Add((UnaryExpression)node); + return base.Visit(node); + } + } + + return base.Visit(node); + } + } +} diff --git a/src/Mapster.EFCore.Tests/EFCoreTest.cs b/src/Mapster.EFCore.Tests/EFCoreTest.cs index 10ad5507..eec16e14 100644 --- a/src/Mapster.EFCore.Tests/EFCoreTest.cs +++ b/src/Mapster.EFCore.Tests/EFCoreTest.cs @@ -1,11 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Mapster.EFCore.Tests.Models; using MapsterMapper; using Microsoft.EntityFrameworkCore; using Microsoft.VisualStudio.TestTools.UnitTesting; using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; namespace Mapster.EFCore.Tests { @@ -67,6 +67,27 @@ public void MapperInstance_From_OrderBy() var last = orderedQuery.Last(); last.LastName.ShouldBe("Olivetto"); } + + [TestMethod] + public void MergeIncludeWhenUsingEFCoreProjectToType() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(Guid.NewGuid().ToString("N")) + .Options; + var context = new SchoolContext(options); + DbInitializer.Initialize(context); + + var mapsterInstance = new Mapper(); + + var query = context.Students + .Include(x => x.Enrollments.OrderByDescending(x => x.StudentID).Take(1)) + .EFCoreProjectToType(); + + var first = query.First(); + + first.Enrollments.Count.ShouldBe(1); + first.LastName.ShouldBe("Alexander"); + } } public class StudentDto diff --git a/src/Mapster.EFCore/EFCoreExtensions.cs b/src/Mapster.EFCore/EFCoreExtensions.cs new file mode 100644 index 00000000..4ed597f0 --- /dev/null +++ b/src/Mapster.EFCore/EFCoreExtensions.cs @@ -0,0 +1,96 @@ +using Mapster.Enums; +using Mapster.Models; +using Mapster.Utils; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Linq.Expressions; + +namespace Mapster.EFCore +{ + public static class EFCoreExtensions + { + public static IQueryable EFCoreProjectToType(this IQueryable source, + TypeAdapterConfig? config = null, ProjectToTypeAutoMapping autoMapConfig = ProjectToTypeAutoMapping.WithoutCollections) + { + var allInclude = new IncludeVisitor(); + allInclude.Visit(source.Expression); + + if (config == null) + { + config = TypeAdapterConfig.GlobalSettings + .Clone() + .ForType(source.ElementType, typeof(TDestination)) + .Config; + + var mapTuple = new TypeTuple(source.ElementType, typeof(TDestination)); + + TypeAdapterRule rule; + config.RuleMap.TryGetValue(mapTuple, out rule); + + if(rule != null) + { + rule.Settings.ProjectToTypeMapConfig = autoMapConfig; + + foreach (var item in allInclude.IncludeExpression) + { + var find = rule.Settings.Resolvers.Find(x => x.SourceMemberName == item.Key); + if (find != null) + { + find.Invoker = (LambdaExpression)item.Value.Operand; + find.SourceMemberName = null; + } + else + rule.Settings.ProjectToTypeResolvers.TryAdd(item.Key, item.Value); + } + } + } + else + { + config = config.Clone() + .ForType(source.ElementType, typeof(TDestination)) + .Config; + } + + return source.ProjectToType(config); + } + } + + + internal class IncludeVisitor : ExpressionVisitor + { + public Dictionary IncludeExpression { get; protected set; } = new(); + private bool IsInclude(Expression node) => node.Type.Name.StartsWith("IIncludableQueryable"); + + [return: NotNullIfNotNull("node")] + public override Expression Visit(Expression node) + { + if (node == null) + return null; + + switch (node.NodeType) + { + case ExpressionType.Call: + { + if (IsInclude(node)) + { + var QuoteVisiter = new QuoteVisitor(); + QuoteVisiter.Visit(node); + + foreach (var item in QuoteVisiter.Quotes) + { + var memberv = new TopLevelMemberNameVisitor(); + memberv.Visit(item); + + IncludeExpression.TryAdd(memberv.MemeberName, item); + } + } + return base.Visit(node); + } + } + + return base.Visit(node); + } + } + +} diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index faa490ec..dd0ee95e 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -40,6 +40,63 @@ from src in sources select fn(src, destinationMember, arg)) .FirstOrDefault(result => result != null); + if (arg.MapType == MapType.Projection && getter != null) + { + var s = new TopLevelMemberNameVisitor(); + + s.Visit(getter); + + var match = arg.Settings.ProjectToTypeResolvers.GetValueOrDefault(s.MemeberName); + + if (match != null) + { + arg.Settings.Resolvers.Add(new InvokerModel + { + Condition = null, + DestinationMemberName = destinationMember.Name, + Invoker = (LambdaExpression)match.Operand, + SourceMemberName = null, + IsChildPath = false + + }); + } + + getter = (from fn in resolvers + from src in sources + select fn(src, destinationMember, arg)) + .FirstOrDefault(result => result != null); + } + + + if (arg.MapType == MapType.Projection) + { + + var checkgetter = (from fn in resolvers.Where(ValueAccessingStrategy.CustomResolvers.Contains) + from src in sources + select fn(src, destinationMember, arg)) + .FirstOrDefault(result => result != null); + + if (checkgetter == null) + { + Type destinationType; + + if (destinationMember.Type.IsNullable()) + destinationType = destinationMember.Type.GetGenericArguments()[0]; + else + destinationType = destinationMember.Type; + + if (arg.Settings.ProjectToTypeMapConfig == Enums.ProjectToTypeAutoMapping.OnlyPrimitiveTypes + && destinationType.IsMapsterPrimitive() == false) + continue; + + if (arg.Settings.ProjectToTypeMapConfig == Enums.ProjectToTypeAutoMapping.WithoutCollections + && destinationType.IsCollectionCompatible() == true) + continue; + } + + } + + var nextIgnore = arg.Settings.Ignore.Next((ParameterExpression)source, (ParameterExpression?)destination, destinationMember.Name); var nextResolvers = arg.Settings.Resolvers.Next(arg.Settings.Ignore, (ParameterExpression)source, destinationMember.Name) .ToList(); diff --git a/src/Mapster/Settings/SettingStore.cs b/src/Mapster/Settings/SettingStore.cs index e89c776e..821b8569 100644 --- a/src/Mapster/Settings/SettingStore.cs +++ b/src/Mapster/Settings/SettingStore.cs @@ -25,6 +25,12 @@ public void Set(string key, object? value) _objectStore[key] = value; } + + public T GetEnum(string key, Func initializer) where T : System.Enum + { + return (T)_objectStore.GetOrAdd(key, _ => initializer()); + } + public bool? Get(string key) { return _booleanStore.TryGetValue(key, out var value) ? value : null; diff --git a/src/Mapster/TypeAdapterSettings.cs b/src/Mapster/TypeAdapterSettings.cs index 818a09b5..9158f3a4 100644 --- a/src/Mapster/TypeAdapterSettings.cs +++ b/src/Mapster/TypeAdapterSettings.cs @@ -1,4 +1,5 @@ -using Mapster.Models; +using Mapster.Enums; +using Mapster.Models; using System; using System.Collections.Generic; using System.Linq.Expressions; @@ -105,6 +106,19 @@ public bool? MapToTargetPrimitive set => Set(nameof(MapToTargetPrimitive), value); } + public ProjectToTypeAutoMapping ProjectToTypeMapConfig + { + get => GetEnum(nameof(ProjectToTypeMapConfig), ()=> default(ProjectToTypeAutoMapping)); + set => Set(nameof(ProjectToTypeMapConfig), value); + } + + public Dictionary ProjectToTypeResolvers + { + get => Get(nameof(ProjectToTypeResolvers), () => new Dictionary()); + set => Set(nameof(ProjectToTypeResolvers), value); + } + + public List> ShouldMapMember { get => Get(nameof(ShouldMapMember), () => new List>()); diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index ae128158..c9ec424e 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -38,7 +38,7 @@ public static Type GetTypeInfo(this Type type) public static bool IsMapsterPrimitive(this Type type) { - return _primitiveTypes.TryGetValue(type, out var primitiveType) || type == typeof(string); + return _primitiveTypes.TryGetValue(type, out var primitiveType) || type == typeof(string) || type.IsEnum; } public static bool IsNullable(this Type type) From 0e2792858c78afc4f1144bbc70a08fc1958955c1 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 13 Apr 2025 11:19:15 +0500 Subject: [PATCH 302/363] Fix Mapping Hidden Base member --- src/Mapster.Tests/WhenMappingInitProperty.cs | 4 +-- src/Mapster/Utils/ReflectionUtils.cs | 33 ++++++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingInitProperty.cs b/src/Mapster.Tests/WhenMappingInitProperty.cs index 4fda1295..6c1352de 100644 --- a/src/Mapster.Tests/WhenMappingInitProperty.cs +++ b/src/Mapster.Tests/WhenMappingInitProperty.cs @@ -19,7 +19,7 @@ public void WhenMappingToHiddenandNewInitFieldDestination() var c = source.Adapt(); var s = source.Adapt(new BDestination()); - ((ADestination)c).Id.ShouldBe(156); + ((ADestination)c).Id.ShouldBe(default); // Hidden Base member is not mapping s.Id.ShouldBe(156); } @@ -33,7 +33,7 @@ public void WhenMappingToHiddenandNewInitFieldWithConstructUsing() var c = source.Adapt(); var s = source.Adapt(new BDestination()); - ((ADestination)c).Id.ShouldBe(256); + ((ADestination)c).Id.ShouldBe(default); // Hidden Base member is not mapping s.Id.ShouldBe(256); } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index c9ec424e..b874910e 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -75,23 +75,44 @@ public static IEnumerable GetFieldsAndProperties(this Type type, var bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (includeNonPublic) bindingFlags |= BindingFlags.NonPublic; - + + var currentTypeMembers = type.FindMembers(MemberTypes.Property | MemberTypes.Field, + BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + (x, y) => true, type.FullName); + if (type.GetTypeInfo().IsInterface) { var allInterfaces = GetAllInterfaces(type); - return allInterfaces.SelectMany(GetPropertiesFunc); + return allInterfaces.SelectMany(x => GetPropertiesFunc(x, currentTypeMembers)); } - return GetPropertiesFunc(type).Concat(GetFieldsFunc(type)); + return GetPropertiesFunc(type, currentTypeMembers).Concat(GetFieldsFunc(type, currentTypeMembers)); - IEnumerable GetPropertiesFunc(Type t) => t.GetProperties(bindingFlags) - .Where(x => x.GetIndexParameters().Length == 0) + IEnumerable GetPropertiesFunc(Type t, MemberInfo[] currentTypeMembers) => t.GetProperties(bindingFlags) + .Where(x => x.GetIndexParameters().Length == 0).DropHiddenMembers(currentTypeMembers) .Select(CreateModel); - IEnumerable GetFieldsFunc(Type t) => t.GetFields(bindingFlags) + IEnumerable GetFieldsFunc(Type t, MemberInfo[] overlapMembers) => + t.GetFields(bindingFlags).DropHiddenMembers(overlapMembers) .Select(CreateModel); } + public static IEnumerable DropHiddenMembers(this IEnumerable allMembers, ICollection currentTypeMembers) where T : MemberInfo + { + var compareMemberNames = allMembers.IntersectBy(currentTypeMembers.Select(x => x.Name), x => x.Name).Select(x => x.Name); + + foreach (var member in allMembers) + { + if (compareMemberNames.Contains(member.Name)) + { + if (currentTypeMembers.First(x => x.Name == member.Name).MetadataToken == member.MetadataToken) + yield return member; + } + else + yield return member; + } + } + // GetProperties(), GetFields(), GetMethods() do not return properties/methods from parent interfaces, // so we need to process every one of them separately. public static IEnumerable GetAllInterfaces(this Type interfaceType) From a00dfa84378eed39579e5fefe479fbc68be87a3e Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 12 Apr 2025 09:40:14 +0500 Subject: [PATCH 303/363] Add support .Inherits() and .Include() to map OpenGeneric --- .../WhenMappingWithOpenGenerics.cs | 59 +++++++++++++++-- src/Mapster/Adapters/BaseAdapter.cs | 26 +++++--- src/Mapster/TypeAdapter.cs | 6 ++ src/Mapster/TypeAdapterConfig.cs | 20 ++++++ src/Mapster/TypeAdapterSetter.cs | 66 +++++++++++++++---- src/Mapster/Utils/ReflectionUtils.cs | 19 ++++++ 6 files changed, 168 insertions(+), 28 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingWithOpenGenerics.cs b/src/Mapster.Tests/WhenMappingWithOpenGenerics.cs index bd8f28fe..88cdc66e 100644 --- a/src/Mapster.Tests/WhenMappingWithOpenGenerics.cs +++ b/src/Mapster.Tests/WhenMappingWithOpenGenerics.cs @@ -1,10 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Shouldly; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Mapster.Tests { @@ -36,6 +31,60 @@ public void Setting_From_OpenGeneric_Has_No_SideEffect() var cCopy = c.Adapt(config); } + [TestMethod] + public void MapOpenGenericsUseInherits() + { + TypeAdapterConfig.GlobalSettings + .ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) + .Map("value", "Value"); + + TypeAdapterConfig.GlobalSettings + .ForType(typeof(DerivedPoco<>), typeof(DerivedDto<>)) + .Map("derivedValue", "DerivedValue") + .Inherits(typeof(GenericPoco<>), typeof(GenericDto<>)); + + var poco = new DerivedPoco { Value = 123 , DerivedValue = 42 }; + var dto = poco.Adapt>(); + dto.value.ShouldBe(poco.Value); + dto.derivedValue.ShouldBe(poco.DerivedValue); + } + + [TestMethod] + public void MapOpenGenericsUseInclude() + { + TypeAdapterConfig.GlobalSettings.Clear(); + + TypeAdapterConfig.GlobalSettings + .ForType(typeof(DerivedPoco<>), typeof(DerivedDto<>)) + .Map("derivedValue", "DerivedValue"); + + TypeAdapterConfig.GlobalSettings + .ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) + .Map("value", "Value"); + + TypeAdapterConfig.GlobalSettings + .ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) + .Include(typeof(DerivedPoco<>), typeof(DerivedDto<>)); + + var poco = new DerivedPoco { Value = 123, DerivedValue = 42 }; + var dto = poco.Adapt(typeof(GenericPoco<>), typeof(GenericDto<>)); + + dto.ShouldBeOfType>(); + + ((DerivedDto)dto).value.ShouldBe(poco.Value); + ((DerivedDto)dto).derivedValue.ShouldBe(poco.DerivedValue); + } + + public class DerivedPoco : GenericPoco + { + public T DerivedValue { get; set; } + } + + public class DerivedDto : GenericDto + { + public T derivedValue { get; set; } + } + public class GenericPoco { public T Value { get; set; } diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 0c3e4fba..b4021c87 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -174,39 +174,47 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de // return adapt(drvdSource); foreach (var tuple in arg.Settings.Includes) { + TypeTuple itemTuple = tuple; + + if (tuple.Source.IsOpenGenericType() && tuple.Destination.IsOpenGenericType()) + { + var genericArg = source.Type.GetGenericArguments(); + itemTuple = new TypeTuple(tuple.Source.MakeGenericType(genericArg), tuple.Destination.MakeGenericType(genericArg)); + } + //same type, no redirect to prevent endless loop - if (tuple.Source == arg.SourceType) + if (itemTuple.Source == arg.SourceType) continue; //type is not compatible, no redirect - if (!arg.SourceType.GetTypeInfo().IsAssignableFrom(tuple.Source.GetTypeInfo())) + if (!arg.SourceType.GetTypeInfo().IsAssignableFrom(itemTuple.Source.GetTypeInfo())) continue; - var drvdSource = Expression.Variable(tuple.Source); + var drvdSource = Expression.Variable(itemTuple.Source); vars.Add(drvdSource); var drvdSourceAssign = Expression.Assign( drvdSource, - Expression.TypeAs(source, tuple.Source)); + Expression.TypeAs(source, itemTuple.Source)); blocks.Add(drvdSourceAssign); - var cond = Expression.NotEqual(drvdSource, Expression.Constant(null, tuple.Source)); + var cond = Expression.NotEqual(drvdSource, Expression.Constant(null, itemTuple.Source)); ParameterExpression? drvdDest = null; if (destination != null) { - drvdDest = Expression.Variable(tuple.Destination); + drvdDest = Expression.Variable(itemTuple.Destination); vars.Add(drvdDest); var drvdDestAssign = Expression.Assign( drvdDest, - Expression.TypeAs(destination, tuple.Destination)); + Expression.TypeAs(destination, itemTuple.Destination)); blocks.Add(drvdDestAssign); cond = Expression.AndAlso( cond, - Expression.NotEqual(drvdDest, Expression.Constant(null, tuple.Destination))); + Expression.NotEqual(drvdDest, Expression.Constant(null, itemTuple.Destination))); } - var adaptExpr = CreateAdaptExpressionCore(drvdSource, tuple.Destination, arg, destination: drvdDest); + var adaptExpr = CreateAdaptExpressionCore(drvdSource, itemTuple.Destination, arg, destination: drvdDest); var adapt = Expression.Return(label, adaptExpr); var ifExpr = Expression.IfThen(cond, adapt); blocks.Add(ifExpr); diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 0ac37924..f30f55fa 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -136,6 +136,12 @@ private static TDestination UpdateFuncFromPackedinObject( /// Adapted destination type. public static object? Adapt(this object source, Type sourceType, Type destinationType) { + if (source != null && + sourceType.IsOpenGenericType() && destinationType.IsOpenGenericType()) + { + var arg = source.GetType().GetGenericArguments(); + return Adapt(source, sourceType.MakeGenericType(arg), destinationType.MakeGenericType(arg), TypeAdapterConfig.GlobalSettings); + } return Adapt(source, sourceType, destinationType, TypeAdapterConfig.GlobalSettings); } diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 5056bd42..a6633bb0 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -13,6 +13,8 @@ namespace Mapster { public class TypeAdapterConfig { + public Type SourceType { get; protected set; } + public Type DestinationType { get; protected set; } public static List RulesTemplate { get; } = CreateRuleTemplate(); public static TypeAdapterConfig GlobalSettings { get; } = new TypeAdapterConfig(); @@ -148,6 +150,9 @@ public TypeAdapterSetter When(Func canMap) /// public TypeAdapterSetter NewConfig() { + this.SourceType = typeof(TSource); + this.DestinationType = typeof(TDestination); + Remove(typeof(TSource), typeof(TDestination)); return ForType(); } @@ -161,6 +166,9 @@ public TypeAdapterSetter NewConfig /// public TypeAdapterSetter NewConfig(Type sourceType, Type destinationType) { + this.SourceType = sourceType; + this.DestinationType = destinationType; + Remove(sourceType, destinationType); return ForType(sourceType, destinationType); } @@ -174,6 +182,9 @@ public TypeAdapterSetter NewConfig(Type sourceType, Type destinationType) /// public TypeAdapterSetter ForType() { + this.SourceType = typeof(TSource); + this.DestinationType = typeof(TDestination); + var key = new TypeTuple(typeof(TSource), typeof(TDestination)); var settings = GetSettings(key); return new TypeAdapterSetter(settings, this); @@ -188,6 +199,9 @@ public TypeAdapterSetter ForType() /// public TypeAdapterSetter ForType(Type sourceType, Type destinationType) { + this.SourceType = sourceType; + this.DestinationType = destinationType; + var key = new TypeTuple(sourceType, destinationType); var settings = GetSettings(key); return new TypeAdapterSetter(settings, this); @@ -201,6 +215,9 @@ public TypeAdapterSetter ForType(Type sourceType, Type destinationType) /// public TypeAdapterSetter ForDestinationType() { + this.SourceType = typeof(void); + this.DestinationType = typeof(TDestination); + var key = new TypeTuple(typeof(void), typeof(TDestination)); var settings = GetSettings(key); return new TypeAdapterSetter(settings, this); @@ -214,6 +231,9 @@ public TypeAdapterSetter ForDestinationType() /// public TypeAdapterSetter ForDestinationType(Type destinationType) { + this.SourceType = typeof(void); + this.DestinationType = destinationType; + var key = new TypeTuple(typeof(void), destinationType); var settings = GetSettings(key); return new TypeAdapterSetter(settings, this); diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index a0581845..026f7501 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -269,10 +269,30 @@ public static TSetter UseDestinationValue(this TSetter setter, Func(this TSetter setter, Type sourceType, Type destType) where TSetter : TypeAdapterSetter + public static TSetter Include(this TSetter setter, Type sourceType, Type destType) where TSetter : TypeAdapterSetter { setter.CheckCompiled(); + Type baseSourceType = setter.Config.SourceType; + Type baseDestinationType = setter.Config.DestinationType; + + if (baseSourceType.IsOpenGenericType() && baseDestinationType.IsOpenGenericType()) + { + if (!sourceType.IsAssignableToGenericType(baseSourceType)) + throw new InvalidCastException("In order to use inherits, TSource must be inherited from TBaseSource."); + if (!destType.IsAssignableToGenericType(baseDestinationType)) + throw new InvalidCastException("In order to use inherits, TDestination must be inherited from TBaseDestination."); + } + else + { + if (!baseSourceType.GetTypeInfo().IsAssignableFrom(sourceType.GetTypeInfo())) + throw new InvalidCastException("In order to use inherits, TSource must be inherited from TBaseSource."); + + if (!baseDestinationType.GetTypeInfo().IsAssignableFrom(destType.GetTypeInfo())) + throw new InvalidCastException("In order to use inherits, TDestination must be inherited from TBaseDestination."); + } + + setter.Config.Rules.LockAdd(new TypeAdapterRule { Priority = arg => @@ -286,6 +306,36 @@ internal static TSetter Include(this TSetter setter, Type sourceType, T return setter; } + public static TSetter Inherits(this TSetter setter, Type baseSourceType, Type baseDestinationType) where TSetter : TypeAdapterSetter + { + setter.CheckCompiled(); + + Type derivedSourceType = setter.Config.SourceType; + Type derivedDestinationType = setter.Config.DestinationType; + + if(baseSourceType.IsOpenGenericType() && baseDestinationType.IsOpenGenericType()) + { + if (!derivedSourceType.IsAssignableToGenericType(baseSourceType)) + throw new InvalidCastException("In order to use inherits, TSource must be inherited from TBaseSource."); + if (!derivedDestinationType.IsAssignableToGenericType(baseDestinationType)) + throw new InvalidCastException("In order to use inherits, TDestination must be inherited from TBaseDestination."); + } + else + { + if (!baseSourceType.GetTypeInfo().IsAssignableFrom(derivedSourceType.GetTypeInfo())) + throw new InvalidCastException("In order to use inherits, TSource must be inherited from TBaseSource."); + + if (!baseDestinationType.GetTypeInfo().IsAssignableFrom(derivedDestinationType.GetTypeInfo())) + throw new InvalidCastException("In order to use inherits, TDestination must be inherited from TBaseDestination."); + } + + if (setter.Config.RuleMap.TryGetValue(new TypeTuple(baseSourceType, baseDestinationType), out var rule)) + { + setter.Settings.Apply(rule.Settings); + } + return setter; + } + public static TSetter ApplyAdaptAttribute(this TSetter setter, BaseAdaptAttribute attr) where TSetter : TypeAdapterSetter { if (attr.IgnoreAttributes != null) @@ -812,20 +862,8 @@ public TypeAdapterSetter Inherits Fork(Action action) diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index b874910e..981e4652 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -407,5 +407,24 @@ public static bool IsInitOnly(this PropertyInfo propertyInfo) var isExternalInitType = typeof(System.Runtime.CompilerServices.IsExternalInit); return setMethod.ReturnParameter.GetRequiredCustomModifiers().Contains(isExternalInitType); } + + public static bool IsAssignableToGenericType(this Type derivedType, Type genericType) + { + + if (derivedType.IsGenericType && derivedType.BaseType.GUID == genericType.GUID) + return true; + + Type baseType = derivedType.BaseType; + if (baseType == null) return false; + + return IsAssignableToGenericType(baseType, genericType); + } + public static bool IsOpenGenericType(this Type type) + { + if(type.IsGenericType) + return type.GetGenericArguments().All(x=>x.GUID == Guid.Empty); + + return false; + } } } \ No newline at end of file From d75579d82eb1d035d5a451e49bdaafd720e6fe05 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 23 Jan 2025 14:07:29 +0500 Subject: [PATCH 304/363] IgnoreNonMapped skip RequireDestinationMemberSource --- src/Mapster.Tests/WhenIgnoringNonMapped.cs | 1 + src/Mapster/Adapters/BaseClassAdapter.cs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/Mapster.Tests/WhenIgnoringNonMapped.cs b/src/Mapster.Tests/WhenIgnoringNonMapped.cs index 59a082ca..320feff1 100644 --- a/src/Mapster.Tests/WhenIgnoringNonMapped.cs +++ b/src/Mapster.Tests/WhenIgnoringNonMapped.cs @@ -12,6 +12,7 @@ public void Should_Ignore_Non_Mapped() { TypeAdapterConfig.NewConfig() .Map(dest => dest.Id, src => src.Id) + .RequireDestinationMemberSource(true) .IgnoreNonMapped(true) .Compile(); diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index dd0ee95e..f05d1180 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -34,7 +34,11 @@ src is LambdaExpression lambda var resolvers = arg.Settings.ValueAccessingStrategies.AsEnumerable(); if (arg.Settings.IgnoreNonMapped == true) + { + arg.Settings.SkipDestinationMemberCheck = true; resolvers = resolvers.Where(ValueAccessingStrategy.CustomResolvers.Contains); + } + var getter = (from fn in resolvers from src in sources select fn(src, destinationMember, arg)) From 3553d25727c706fe827b6c79ec6d05c5e59ca85e Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 14 Apr 2025 14:42:28 +0500 Subject: [PATCH 305/363] IgnoreNonMapped mark not mapping memeber as Ignore --- src/Mapster/Adapters/BaseClassAdapter.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index f05d1180..2219d0b4 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -21,6 +21,9 @@ protected ClassMapping CreateClassConverter(Expression source, ClassModel classM var unmappedDestinationMembers = new List(); var properties = new List(); + if (arg.Settings.IgnoreNonMapped == true) + IgnoreNonMapped(classModel,arg); + var sources = new List {source}; sources.AddRange( arg.Settings.ExtraSources.Select(src => @@ -34,11 +37,7 @@ src is LambdaExpression lambda var resolvers = arg.Settings.ValueAccessingStrategies.AsEnumerable(); if (arg.Settings.IgnoreNonMapped == true) - { - arg.Settings.SkipDestinationMemberCheck = true; resolvers = resolvers.Where(ValueAccessingStrategy.CustomResolvers.Contains); - } - var getter = (from fn in resolvers from src in sources select fn(src, destinationMember, arg)) @@ -242,6 +241,18 @@ protected virtual ClassModel GetSetterModel(CompileArgument arg) }; } + protected void IgnoreNonMapped (ClassModel classModel, CompileArgument arg) + { + var notMappingToIgnore = classModel.Members + .ExceptBy(arg.Settings.Resolvers.Select(x => x.DestinationMemberName), + y => y.Name); + + foreach (var item in notMappingToIgnore) + { + arg.Settings.Ignore.TryAdd(item.Name, new IgnoreDictionary.IgnoreItem()); + } + } + #endregion } } From f647867400c758ad8263e52200737b13acaa8b28 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 9 Apr 2025 23:00:20 +0500 Subject: [PATCH 306/363] NullableExpressionVisitor improvement and test --- .../WhenMappingNullablePrimitives.cs | 45 ++++++++++++++++++- .../Utils/NullableExpressionVisitor.cs | 2 +- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingNullablePrimitives.cs b/src/Mapster.Tests/WhenMappingNullablePrimitives.cs index 6e4bb32e..8d0fec3f 100644 --- a/src/Mapster.Tests/WhenMappingNullablePrimitives.cs +++ b/src/Mapster.Tests/WhenMappingNullablePrimitives.cs @@ -1,6 +1,6 @@ -using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Shouldly; +using System; namespace Mapster.Tests { @@ -134,8 +134,49 @@ public void Can_Map_From_Non_Nullable_Source_To_Nullable_Target() poco.IsImport.GetValueOrDefault().ShouldBeTrue(); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/414 + /// + [TestMethod] + public void MappingNullTuple() + { + TypeAdapterConfig<(string?, string?, Application414), Output414>.NewConfig() + .Map(dest => dest, src => src.Item1) + .Map(dest => dest, src => src.Item2) + .Map(dest => dest.Application, src => src.Item3 == null ? (Application414)null : new Application414() + { + Id = src.Item3.Id, + Name = src.Item3.Name + }); + + (string, string, Application414) source = (null, null, null); + + var result = source.Adapt(); + + result.Item1.ShouldBeNull(); + result.Item2.ShouldBeNull(); + result.Application.ShouldBeNull(); + } + #region TestClasses + + public class Output414 + { + public string Item1 { get; set; } + + public string Item2 { get; set; } + + public Application414 Application { get; set; } + } + + public class Application414 + { + public string Name { get; set; } + + public int Id { get; set; } + } + public class NullablePrimitivesPoco { public Guid Id { get; set; } diff --git a/src/Mapster/Utils/NullableExpressionVisitor.cs b/src/Mapster/Utils/NullableExpressionVisitor.cs index ed642996..70d25162 100644 --- a/src/Mapster/Utils/NullableExpressionVisitor.cs +++ b/src/Mapster/Utils/NullableExpressionVisitor.cs @@ -127,7 +127,7 @@ protected override Expression VisitConstant(ConstantExpression node) protected override Expression VisitMember(MemberExpression node) { - CanBeNull = node.Member.GetCustomAttributesData().All(IsNullable); + CanBeNull = node.Type.IsClass || node.Member.GetCustomAttributesData().All(IsNullable); return node; } } From 4ace87731ebaa876d930404b11b4b64f5fc844f2 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 13 Jun 2025 12:27:59 +0500 Subject: [PATCH 307/363] add Simply Async mapping --- src/Mapster.Async.Tests/AsyncTest.cs | 17 +++++++++ src/Mapster.Async/TypeAdapterExtensions.cs | 42 +++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Async.Tests/AsyncTest.cs b/src/Mapster.Async.Tests/AsyncTest.cs index 1d85146d..caefe5ce 100644 --- a/src/Mapster.Async.Tests/AsyncTest.cs +++ b/src/Mapster.Async.Tests/AsyncTest.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using MapsterMapper; using Microsoft.VisualStudio.TestTools.UnitTesting; using Shouldly; @@ -87,6 +88,22 @@ public async Task NestedAsync() dtoOwnership.Owner.Name.ShouldBe("John Doe"); } + [TestMethod] + public async Task SimplyAsync() + { + TypeAdapterConfig.NewConfig() + .AfterMappingAsync(async dest => { dest.Name = await GetName(); }); + + var poco = new Poco { Id = "foo" }; + var dto = await poco.AdaptAsync(); + dto.Name.ShouldBe("bar"); + + IMapper instance = new Mapper(); + + var destination = await instance.MapAsync(poco); + destination.Name.ShouldBe("bar"); + } + private static async Task GetName() { await Task.Delay(1); diff --git a/src/Mapster.Async/TypeAdapterExtensions.cs b/src/Mapster.Async/TypeAdapterExtensions.cs index b42a754e..6797c1b2 100644 --- a/src/Mapster.Async/TypeAdapterExtensions.cs +++ b/src/Mapster.Async/TypeAdapterExtensions.cs @@ -1,4 +1,5 @@ -using System; +using MapsterMapper; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -101,5 +102,44 @@ public static async Task AdaptToAsync(this IAdapterB } } + + /// + /// Map asynchronously to destination type. + /// + /// Destination type to map. + /// + /// Type of destination object that mapped. + public static async Task AdaptAsync(this object? source) + { + return await source.BuildAdapter().AdaptToTypeAsync(); + } + + + /// + /// Map asynchronously to destination type. + /// + /// Destination type to map. + /// + /// Configuration + /// Type of destination object that mapped. + public static async Task AdaptAsync(this object? source, TypeAdapterConfig config) + { + return await source.BuildAdapter(config).AdaptToTypeAsync(); + } + + } + + public static class IMapperAsyncExtentions + { + /// + /// Map asynchronously to destination type. + /// + /// Destination type to map. + /// + /// Type of destination object that mapped. + public static async Task MapAsync(this IMapper mapper, object? source) + { + return await mapper.From(source).AdaptToTypeAsync(); + } } } From d23b2b712bd54b1a658298ac42432a5a66cb68d9 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 3 Feb 2025 19:57:25 +0500 Subject: [PATCH 308/363] fix IgnoreNullValues for RecordType when used Map mapping --- src/Mapster/Adapters/RecordTypeAdapter.cs | 34 ++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 66cb38a7..81c1193e 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -15,6 +15,8 @@ internal class RecordTypeAdapter : ClassAdapter protected override int Score => -149; protected override bool UseTargetValue => false; + private List SkipIgnoreNullValuesMemberMap = new List(); + protected override bool CanMap(PreCompileArgument arg) { return arg.DestinationType.IsRecordType(); @@ -33,6 +35,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E { //new TDestination(src.Prop1, src.Prop2) + SkipIgnoreNullValuesMemberMap.Clear(); Expression installExpr; if (arg.GetConstructUsing() != null || arg.DestinationType == null) @@ -83,8 +86,14 @@ protected override Expression CreateInstantiationExpression(Expression source, E var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); - if (arg.MapType == MapType.MapToTarget && arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull()) // add IgnoreNullValues support + if (arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull()) // add IgnoreNullValues support { + if (arg.MapType != MapType.MapToTarget) + { + SkipIgnoreNullValuesMemberMap.Add(member); + continue; + } + if (adapt is ConditionalExpression condEx) { if (condEx.Test is BinaryExpression { NodeType: ExpressionType.Equal } binEx && @@ -160,6 +169,29 @@ protected override Expression CreateBlockExpression(Expression source, Expressio var lines = new List(); + if (arg.MapType != MapType.MapToTarget) + { + foreach (var member in SkipIgnoreNullValuesMemberMap) + { + + var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); + + if (adapt is ConditionalExpression condEx) + { + if (condEx.Test is BinaryExpression { NodeType: ExpressionType.Equal } binEx && + binEx.Left == member.Getter && + binEx.Right is ConstantExpression { Value: null }) + adapt = condEx.IfFalse; + } + adapt = member.DestinationMember.SetExpression(destination, adapt); + var sourceCondition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type)); + + + lines.Add(Expression.IfThen(sourceCondition, adapt)); + } + } + + foreach (var member in members) { if (member.DestinationMember.SetterModifier == AccessModifier.None && member.UseDestinationValue) From 2de0109fc0e13a82ff3620b633b3272357c18456 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 11 Feb 2025 22:51:15 +0500 Subject: [PATCH 309/363] Fix Name Ctor param Matching add Test Check fixing Matching Ctor param --- .../WhenMappingRecordRegression.cs | 49 +++++++++++++++++++ src/Mapster/Adapters/BaseClassAdapter.cs | 1 + src/Mapster/Compile/CompileArgument.cs | 1 + .../Settings/ValueAccessingStrategy.cs | 18 +++---- src/Mapster/Utils/ReflectionUtils.cs | 9 ++-- 5 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index b1c41f92..6304e4a0 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -2,6 +2,7 @@ using Shouldly; using System; using System.Collections.Generic; +using static Mapster.Tests.WhenMappingDerived; namespace Mapster.Tests { @@ -422,6 +423,26 @@ public void WhenRecordTypeWorksWithUseDestinationValueAndIgnoreNullValues() } + /// + /// https://github.com/MapsterMapper/Mapster/issues/771 + /// https://github.com/MapsterMapper/Mapster/issues/746 + /// + [TestMethod] + public void FixCtorParamMapping() + { + var sourceRequestPaymentDto = new PaymentDTO771("MasterCard", "1234", "12/99", "234", 12); + var sourceRequestOrderDto = new OrderDTO771(Guid.NewGuid(), Guid.NewGuid(), "order123", sourceRequestPaymentDto); + var db = new Database746(UserID: "256", Password: "123"); + + + var result = new CreateOrderRequest771(sourceRequestOrderDto).Adapt(); + var resultID = db.Adapt(new Database746()); + + + result.Order.Payment.CVV.ShouldBe("234"); + resultID.UserID.ShouldBe("256"); + } + #region NowNotWorking /// @@ -448,6 +469,34 @@ public void CollectionUpdate() #region TestClasses + + public sealed record Database746( + string Server = "", + string Name = "", + string? UserID = null, + string? Password = null); + + public record CreateOrderRequest771(OrderDTO771 Order); + + public record CreateOrderCommand771(OrderDTO771 Order); + + + public record OrderDTO771 + ( + Guid Id, + Guid CustomerId, + string OrderName, + PaymentDTO771 Payment + ); + + public record PaymentDTO771 + ( + string CardName, + string CardNumber, + string Expiration, + string CVV, + int PaymentMethod + ); public class SourceFromTestUseDestValue { public int? A { get; set; } diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 26c3c46c..e6c98a0e 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -20,6 +20,7 @@ protected ClassMapping CreateClassConverter(Expression source, ClassModel classM var destinationMembers = classModel.Members; var unmappedDestinationMembers = new List(); var properties = new List(); + arg.ConstructorMapping = ctorMapping; if (arg.Settings.IgnoreNonMapped == true) IgnoreNonMapped(classModel,arg); diff --git a/src/Mapster/Compile/CompileArgument.cs b/src/Mapster/Compile/CompileArgument.cs index 2e15637a..23c540ea 100644 --- a/src/Mapster/Compile/CompileArgument.cs +++ b/src/Mapster/Compile/CompileArgument.cs @@ -15,6 +15,7 @@ public class CompileArgument public TypeAdapterSettings Settings { get; set; } public CompileContext Context { get; set; } public bool UseDestinationValue { get; set; } + public bool? ConstructorMapping { get; set; } private HashSet? _srcNames; internal HashSet GetSourceNames() diff --git a/src/Mapster/Settings/ValueAccessingStrategy.cs b/src/Mapster/Settings/ValueAccessingStrategy.cs index ea47d9ad..fd13407d 100644 --- a/src/Mapster/Settings/ValueAccessingStrategy.cs +++ b/src/Mapster/Settings/ValueAccessingStrategy.cs @@ -73,10 +73,10 @@ public static class ValueAccessingStrategy { var members = source.Type.GetFieldsAndProperties(true); var strategy = arg.Settings.NameMatchingStrategy; - var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter); + var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter, arg); return members .Where(member => member.ShouldMapMember(arg, MemberSide.Source)) - .Where(member => member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter) == destinationMemberName) + .Where(member => member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter, arg) == destinationMemberName) .Select(member => member.GetExpression(source)) .FirstOrDefault(); } @@ -86,7 +86,7 @@ public static class ValueAccessingStrategy if (arg.MapType == MapType.Projection) return null; var strategy = arg.Settings.NameMatchingStrategy; - var destinationMemberName = "Get" + destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter); + var destinationMemberName = "Get" + destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter, arg); var getMethod = Array.Find(source.Type.GetMethods(BindingFlags.Public | BindingFlags.Instance), m => strategy.SourceMemberNameConverter(m.Name) == destinationMemberName && m.GetParameters().Length == 0); if (getMethod == null) return null; @@ -98,7 +98,7 @@ public static class ValueAccessingStrategy private static Expression? FlattenMemberFn(Expression source, IMemberModel destinationMember, CompileArgument arg) { var strategy = arg.Settings.NameMatchingStrategy; - var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter); + var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter, arg); return GetDeepFlattening(source, destinationMemberName, arg); } @@ -111,7 +111,7 @@ public static class ValueAccessingStrategy if (!member.ShouldMapMember(arg, MemberSide.Source)) continue; - var sourceMemberName = member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter); + var sourceMemberName = member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter, arg); if (string.Equals(propertyName, sourceMemberName)) return member.GetExpression(source); @@ -132,14 +132,14 @@ public static class ValueAccessingStrategy internal static IEnumerable FindUnflatteningPairs(Expression source, IMemberModel destinationMember, CompileArgument arg) { var strategy = arg.Settings.NameMatchingStrategy; - var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter); + var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter, arg); var members = source.Type.GetFieldsAndProperties(true); foreach (var member in members) { if (!member.ShouldMapMember(arg, MemberSide.Source)) continue; - var sourceMemberName = member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter); + var sourceMemberName = member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter, arg); if (!sourceMemberName.StartsWith(destinationMemberName) || sourceMemberName == destinationMemberName) continue; foreach (var prop in GetDeepUnflattening(destinationMember, sourceMemberName.Substring(destinationMemberName.Length).TrimStart('_'), arg)) @@ -161,7 +161,7 @@ private static IEnumerable GetDeepUnflattening(IMemberModel destinationM { if (!member.ShouldMapMember(arg, MemberSide.Destination)) continue; - var destMemberName = member.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter); + var destMemberName = member.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter, arg); var propertyType = member.Type; if (string.Equals(propertyName, destMemberName)) { @@ -185,7 +185,7 @@ private static IEnumerable GetDeepUnflattening(IMemberModel destinationM return null; var strategy = arg.Settings.NameMatchingStrategy; - var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter); + var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter, arg); var key = Expression.Constant(destinationMemberName); var args = dictType.GetGenericArguments(); if (strategy.SourceMemberNameConverter != MapsterHelper.Identity) diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 981e4652..93aa7e62 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -338,11 +338,14 @@ public static bool UseDestinationValue(this IMemberModel member, CompileArgument return predicates.Any(predicate => predicate(member)); } - public static string GetMemberName(this IMemberModel member, MemberSide side, List> getMemberNames, Func nameConverter) + public static string GetMemberName(this IMemberModel member, MemberSide side, List> getMemberNames, Func nameConverter, CompileArgument arg) { var memberName = getMemberNames.Select(func => func(member, side)) - .FirstOrDefault(name => name != null) - ?? member.Name; + .FirstOrDefault(name => name != null); + if (memberName == null && arg.ConstructorMapping == true) + memberName = member.Name.ToPascalCase(); + if (memberName == null) + memberName = member.Name; return nameConverter(memberName); } From 7fc68423ed84258d211c6c89c825a6855289cf0f Mon Sep 17 00:00:00 2001 From: Kristian Jeremic Date: Wed, 2 Jul 2025 23:25:50 +0200 Subject: [PATCH 310/363] Created _Footer (markdown) --- _Footer.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 _Footer.md diff --git a/_Footer.md b/_Footer.md new file mode 100644 index 00000000..30d74d25 --- /dev/null +++ b/_Footer.md @@ -0,0 +1 @@ +test \ No newline at end of file From 965fc2336ebeb0daa0c596301179943af74b36ab Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 10 Jul 2025 07:31:20 +0500 Subject: [PATCH 311/363] fix class SetValueByReflection --- src/Mapster/Adapters/ClassAdapter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 407c5a5b..6d12e57b 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -181,9 +181,9 @@ protected override Expression CreateBlockExpression(Expression source, Expressio private static Expression SetValueByReflection(MemberMapping member, MemberExpression adapt) { var typeofExpression = Expression.Constant(member.Destination!.Type); - var getPropertyMethod = typeof(Type).GetMethod("GetProperty", new[] { typeof(string) })!; + var getPropertyMethod = typeof(Type).GetMethod("GetProperty", new[] { typeof(string), typeof(Type) })!; var getPropertyExpression = Expression.Call(typeofExpression, getPropertyMethod, - Expression.Constant(member.DestinationMember.Name, member.DestinationMember.Type)); + new[] { Expression.Constant(member.DestinationMember.Name), Expression.Constant(member.DestinationMember.Type) }); var setValueMethod = typeof(PropertyInfo).GetMethod("SetValue", new[] { typeof(object), typeof(object) })!; var memberAsObject = adapt.To(typeof(object)); From 98ef536d6217bdab64784fb5694a3cec530967e1 Mon Sep 17 00:00:00 2001 From: Hopcos <454396441@qq.com> Date: Tue, 5 Aug 2025 15:31:48 +0800 Subject: [PATCH 312/363] Updated _Footer (markdown) --- _Footer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Footer.md b/_Footer.md index 30d74d25..f079749c 100644 --- a/_Footer.md +++ b/_Footer.md @@ -1 +1 @@ -test \ No newline at end of file +test1 \ No newline at end of file From 05b5d3ca5f2492066fd35cc2674b5f8c7d7a8e00 Mon Sep 17 00:00:00 2001 From: Hopcos <454396441@qq.com> Date: Tue, 5 Aug 2025 15:32:00 +0800 Subject: [PATCH 313/363] Updated _Footer (markdown) --- _Footer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Footer.md b/_Footer.md index f079749c..30d74d25 100644 --- a/_Footer.md +++ b/_Footer.md @@ -1 +1 @@ -test1 \ No newline at end of file +test \ No newline at end of file From b850763fff3d6111e3f66e908ada6de2a9a26506 Mon Sep 17 00:00:00 2001 From: Dmitry <52341158+Exzept1on@users.noreply.github.com> Date: Fri, 8 Aug 2025 12:44:17 +0700 Subject: [PATCH 314/363] Updated _Footer (markdown) --- _Footer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Footer.md b/_Footer.md index 30d74d25..f079749c 100644 --- a/_Footer.md +++ b/_Footer.md @@ -1 +1 @@ -test \ No newline at end of file +test1 \ No newline at end of file From ceb6aa1c8439e08a541aa4bb2eaaf9ebb402c2e2 Mon Sep 17 00:00:00 2001 From: J Befus <33312626+jbefus@users.noreply.github.com> Date: Sun, 10 Aug 2025 00:46:31 +0200 Subject: [PATCH 315/363] Updated _Footer (markdown) --- _Footer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Footer.md b/_Footer.md index f079749c..e2d8ff30 100644 --- a/_Footer.md +++ b/_Footer.md @@ -1 +1 @@ -test1 \ No newline at end of file +test1? \ No newline at end of file From 8586a4c8bde5c5de6474aba0c116264644517ad6 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 29 Sep 2025 23:01:13 +0500 Subject: [PATCH 316/363] add constructor param null checker --- .../WhenCtorNullableParamMapping.cs | 115 +++++++++++++ src/Mapster/Adapters/BaseClassAdapter.cs | 12 +- src/Mapster/Adapters/ClassAdapter.cs | 2 +- .../Utils/MemberExpressionExtractor.cs | 152 ++++++++++++++++++ 4 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 src/Mapster.Tests/WhenCtorNullableParamMapping.cs create mode 100644 src/Mapster/Utils/MemberExpressionExtractor.cs diff --git a/src/Mapster.Tests/WhenCtorNullableParamMapping.cs b/src/Mapster.Tests/WhenCtorNullableParamMapping.cs new file mode 100644 index 00000000..bef0b16f --- /dev/null +++ b/src/Mapster.Tests/WhenCtorNullableParamMapping.cs @@ -0,0 +1,115 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenCtorNullableParamMapping + { + [TestMethod] + public void Dto_To_Domain_MapsCorrectly() + { + var config = new TypeAdapterConfig(); + + config.Default.MapToConstructor(true); + config + .NewConfig() + .Include(); + + + var dtoDerived = new DerivedDtoTestClass + { + DerivedProperty = "DerivedValue", + AbstractProperty = "AbstractValue" + }; + + var dto = new DtoTestClass + { + AbstractType = dtoDerived + }; + + var domain = dto.Adapt(config); + + domain.AbstractType.ShouldNotBe(null); + domain.AbstractType.ShouldBeOfType(); + + var domainDerived = (DerivedDomainTestClass)domain.AbstractType; + domainDerived.DerivedProperty.ShouldBe(dtoDerived.DerivedProperty); + domainDerived.AbstractProperty.ShouldBe(dtoDerived.AbstractProperty); + + } + + [TestMethod] + public void Dto_To_Domain_AbstractClassNull_MapsCorrectly() + { + var config = new TypeAdapterConfig(); + + config.Default.MapToConstructor(true); + config + .NewConfig() + .Include(); + + var dto = new DtoTestClass + { + AbstractType = null + }; + + var domain = dto.Adapt(config); + + domain.AbstractType.ShouldBeNull(); + } + + + #region Immutable classes with private setters, map via ctors + private abstract class AbstractDomainTestClass + { + public string AbstractProperty { get; private set; } + + protected AbstractDomainTestClass(string abstractProperty) + { + AbstractProperty = abstractProperty; + } + } + + private class DerivedDomainTestClass : AbstractDomainTestClass + { + public string DerivedProperty { get; private set; } + + /// + public DerivedDomainTestClass(string abstractProperty, string derivedProperty) + : base(abstractProperty) + { + DerivedProperty = derivedProperty; + } + } + + private class DomainTestClass + { + public AbstractDomainTestClass? AbstractType { get; private set; } + + public DomainTestClass( + AbstractDomainTestClass? abstractType) + { + AbstractType = abstractType; + } + } + #endregion + + #region DTO classes + private abstract class AbstractDtoTestClass + { + public string AbstractProperty { get; set; } + } + + private class DerivedDtoTestClass : AbstractDtoTestClass + { + public string DerivedProperty { get; set; } + } + + private class DtoTestClass + { + public AbstractDtoTestClass? AbstractType { get; set; } + } + #endregion + } +} diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index faa490ec..0a18523d 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -147,7 +147,17 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi } else { - getter = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); + + if (member.Getter.CanBeNull() && member.Ignore.Condition == null) + { + var compareNull = Expression.Equal(member.Getter, Expression.Constant(null, member.Getter.Type)); + getter = Expression.Condition(ExpressionEx.Not(compareNull), + CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member), + defaultConst); + } + else + getter = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member); + if (member.Ignore.Condition != null) { var body = member.Ignore.IsChildPath diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 407c5a5b..0b984f0a 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -201,7 +201,7 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre var exp = CreateInstantiationExpression(source, arg); var memberInit = exp as MemberInitExpression; var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; - var contructorMembers = newInstance.Arguments.OfType().Select(me => me.Member).ToArray(); + var contructorMembers = newInstance.GetAllMemberExpressionsMemberInfo().ToArray(); var classModel = GetSetterModel(arg); var classConverter = CreateClassConverter(source, classModel, arg); var members = classConverter.Members; diff --git a/src/Mapster/Utils/MemberExpressionExtractor.cs b/src/Mapster/Utils/MemberExpressionExtractor.cs new file mode 100644 index 00000000..f333ad79 --- /dev/null +++ b/src/Mapster/Utils/MemberExpressionExtractor.cs @@ -0,0 +1,152 @@ +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; + +public static class MemberExpressionExtractor +{ + public static IEnumerable GetAllMemberExpressionsMemberInfo(this Expression expression) + { + var result = new List(); + CollectMemberInfos(expression, result); + return result; + } + + private static void CollectMemberInfos(Expression expression, ICollection results) + { + if (expression == null) return; + + if (expression is MemberExpression memberExpression) + { + results.Add(memberExpression.Member); + } + else if (expression is BinaryExpression binaryExpression && binaryExpression.NodeType == ExpressionType.Assign) + { + ProcessBinaryAssign(binaryExpression, results); + } + + foreach (var subExpression in expression.GetSubExpressions()) + { + CollectMemberInfos(subExpression, results); + } + } + + private static void ProcessBinaryAssign(BinaryExpression assignExpression, ICollection results) + { + if (assignExpression == null) return; + + if (assignExpression.Left is MemberExpression leftMember) + { + results.Add(leftMember.Member); + } + + CollectMemberInfos(assignExpression.Right, results); + } + + private static IEnumerable GetSubExpressions(this Expression expression) + { + if (expression == null) yield break; + + switch (expression.NodeType) + { + case ExpressionType.MemberAccess: + yield return ((MemberExpression)expression).Expression; + break; + + case ExpressionType.Call: + foreach (var arg in ((MethodCallExpression)expression).Arguments) + yield return arg; + yield return ((MethodCallExpression)expression).Object; + break; + + case ExpressionType.Lambda: + yield return ((LambdaExpression)expression).Body; + break; + + case ExpressionType.Block: + foreach (var blockExpr in ((BlockExpression)expression).Expressions) + yield return blockExpr; + break; + + case ExpressionType.Conditional: + yield return ((ConditionalExpression)expression).Test; + yield return ((ConditionalExpression)expression).IfTrue; + yield return ((ConditionalExpression)expression).IfFalse; + break; + + case ExpressionType.NewArrayInit or ExpressionType.NewArrayBounds: + foreach (var arrayItem in ((NewArrayExpression)expression).Expressions) + yield return arrayItem; + break; + + case ExpressionType.New: + foreach (var arg in ((NewExpression)expression).Arguments) + yield return arg; + break; + + case ExpressionType.Invoke: + yield return ((InvocationExpression)expression).Expression; + break; + + case ExpressionType.Assign: + yield return ((BinaryExpression)expression).Left; + yield return ((BinaryExpression)expression).Right; + break; + + case ExpressionType.Add: + case ExpressionType.Subtract: + case ExpressionType.Multiply: + case ExpressionType.Divide: + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.GreaterThan: + case ExpressionType.LessThan: + case ExpressionType.AndAlso: + case ExpressionType.OrElse: + yield return ((BinaryExpression)expression).Left; + yield return ((BinaryExpression)expression).Right; + break; + + case ExpressionType.Not: + case ExpressionType.Negate: + case ExpressionType.Convert: + case ExpressionType.Increment: + case ExpressionType.Decrement: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + case ExpressionType.OnesComplement: + yield return ((UnaryExpression)expression).Operand; + break; + + case ExpressionType.TypeIs: + yield return ((TypeBinaryExpression)expression).Expression; + break; + + case ExpressionType.Coalesce: + yield return ((BinaryExpression)expression).Left; + yield return ((BinaryExpression)expression).Conversion; + yield return ((BinaryExpression)expression).Right; + break; + + case ExpressionType.Index: + yield return ((IndexExpression)expression).Object; + foreach (var indexArg in ((IndexExpression)expression).Arguments) + yield return indexArg; + break; + + case ExpressionType.Loop: + yield return ((LoopExpression)expression).Body; + break; + + case ExpressionType.Try: + yield return ((TryExpression)expression).Body; + foreach (var handler in ((TryExpression)expression).Handlers) + yield return handler.Body; + yield return ((TryExpression)expression).Finally; + break; + + + default: + break; + } + } +} \ No newline at end of file From a2a19f1641114b0f2fe0ab68227f6bd0db4cba38 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 30 Sep 2025 22:24:43 +0500 Subject: [PATCH 317/363] Restore --- src/Mapster/Adapters/ClassAdapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 5c99ddde..de39dec2 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -201,7 +201,7 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre var exp = CreateInstantiationExpression(source, arg); var memberInit = exp as MemberInitExpression; var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; - var contructorMembers = newInstance.Arguments.OfType().Select(me => me.Member).ToArray(); + var contructorMembers = newInstance.GetAllMemberExpressionsMemberInfo().ToArray(); ClassModel? classModel; ClassMapping? classConverter; if (IsRequiredOnly) From 10ab428d9c52ead2ac0a6de0a32ddb3e6f0d19db Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Tue, 30 Sep 2025 23:06:58 +0200 Subject: [PATCH 318/363] Updated version numbers to 9.0.0-pre01. --- src/ExpressionDebugger/ExpressionDebugger.csproj | 2 +- src/ExpressionTranslator/ExpressionTranslator.csproj | 2 +- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index 3b5154fb..bc3bd8d3 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -12,7 +12,7 @@ True true ExpressionDebugger.snk - 2.2.0 + 9.0.0-pre01 https://github.com/chaowlert/ExpressionDebugger/blob/master/LICENSE 8.0 enable diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index 04b402f7..ece11ace 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -12,7 +12,7 @@ True true ExpressionTranslator.snk - 2.4.3 + 9.0.0-pre01 ExpressionDebugger MIT icon.png diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index d8b90332..75b9eebf 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -7,7 +7,7 @@ Mapster;Async true Mapster.Async.snk - 2.0.3-pre02 + 9.0.0-pre01 diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index c926ae84..c3d159a9 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -4,7 +4,7 @@ net9.0;net8.0; Mapster.Core mapster - 1.2.3-pre02 + 9.0.0-pre01 enable true true diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 1bd37229..1b945dce 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -7,7 +7,7 @@ Mapster;DependencyInjection true Mapster.DependencyInjection.snk - 1.0.3-pre02 + 9.0.0-pre01 diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index bb101550..16e42db5 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -8,7 +8,7 @@ True true Mapster.EF6.snk - 2.0.3-pre02 + 9.0.0-pre01 diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index 2d846efa..43cd952f 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -8,7 +8,7 @@ True true Mapster.EFCore.snk - 5.1.3-pre02 + 9.0.0-pre01 diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index 10d26ff8..6127eb37 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -7,7 +7,7 @@ Mapster;Immutable true Mapster.Immutable.snk - 1.0.3-pre02 + 9.0.0-pre01 enable diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 97ec21b8..1d7cda43 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -7,7 +7,7 @@ Mapster;Json.net true Mapster.JsonNet.snk - 1.1.3-pre02 + 9.0.0-pre01 diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index 6ddef117..21deb5a6 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -7,7 +7,7 @@ true Mapster.SourceGenerator.snk https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json ;$(RestoreAdditionalProjectSources) - 6.5.1 + 9.0.0-pre01 enable false diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 6ecc3c60..1dfc90eb 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -10,7 +10,7 @@ Mapster;Tool true Mapster.Tool.snk - 8.4.2-pre02 + 9.0.0-pre01 enable diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index acd8f99a..f8299fd2 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -16,7 +16,7 @@ true Mapster - 7.4.2-pre02 + 9.0.0-pre01 enable 1701;1702;8618 From 378f5da0888cd3580fcf8046156ef7970c2f96df Mon Sep 17 00:00:00 2001 From: Andreas Ravnestad Date: Wed, 1 Oct 2025 00:40:59 +0200 Subject: [PATCH 319/363] Updated Mapster.Tool (markdown) --- Mapster.Tool.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Mapster.Tool.md b/Mapster.Tool.md index b2a678b4..1bf66e8f 100644 --- a/Mapster.Tool.md +++ b/Mapster.Tool.md @@ -134,6 +134,10 @@ If you get an error similar to `Unhandled exception. System.IO.FileNotFoundExcep **Workaround 1** +Ensure that you are using Mapster.Tool version 8.4.2-pre01 or newer. + +**Workaround 2** + Add `true` to your csproj file as follows: ```xml @@ -145,7 +149,7 @@ Add `true` to your cs [...] ``` -**Workaround 2** +**Workaround 3** Change your `dotnet build` command to `dotnet build -p:CopyLocalLockFileAssemblies=true` as follows: From ec260eea895cf88688c746a10886294831b72052 Mon Sep 17 00:00:00 2001 From: Rokas Braidokas <76654333+MaRokas2005@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:21:05 +0300 Subject: [PATCH 320/363] Destroyed _Footer (markdown) --- _Footer.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 _Footer.md diff --git a/_Footer.md b/_Footer.md deleted file mode 100644 index e2d8ff30..00000000 --- a/_Footer.md +++ /dev/null @@ -1 +0,0 @@ -test1? \ No newline at end of file From 41ab04b5e071dc4bda62c542f68133dae1562b84 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 15 Oct 2025 15:21:38 +0500 Subject: [PATCH 321/363] Disable NullableEnumFix #643 from MapToProjection cases --- src/Mapster/Adapters/BaseAdapter.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index d33f118d..a3ffb648 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -479,7 +479,12 @@ protected Expression CreateAdaptExpression(Expression source, Type destinationTy } internal Expression CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg, MemberMapping? mapping, Expression? destination = null) { - var _source = source.NullableEnumExtractor(); // Extraction Nullable Enum + Expression _source; + + if (arg.MapType != MapType.Projection) + _source = source.NullableEnumExtractor(); // Extraction Nullable Enum + else + _source = source; if (_source.Type == destinationType && arg.MapType == MapType.Projection) return _source; From 08cece99734140712836fa68d24857a3b4095323 Mon Sep 17 00:00:00 2001 From: "Artyom M." Date: Tue, 14 Oct 2025 11:50:41 +0300 Subject: [PATCH 322/363] Mapster.EF.Core: Add type check for IAsyncEnumerable<> --- src/Mapster.EFCore.Tests/EFCoreTest.cs | 29 ++++++++++++++++++++++++++ src/Mapster.EFCore/MapsterQueryable.cs | 10 +++++++++ 2 files changed, 39 insertions(+) diff --git a/src/Mapster.EFCore.Tests/EFCoreTest.cs b/src/Mapster.EFCore.Tests/EFCoreTest.cs index eec16e14..6fba5c15 100644 --- a/src/Mapster.EFCore.Tests/EFCoreTest.cs +++ b/src/Mapster.EFCore.Tests/EFCoreTest.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace Mapster.EFCore.Tests { @@ -45,6 +46,34 @@ public void TestFindObject() first.Grade.ShouldBe(Grade.F); } + [TestMethod] + public async Task TestFindSingleObjectUsingProjectToType() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(Guid.NewGuid().ToString("N")) + .Options; + var context = new SchoolContext(options); + DbInitializer.Initialize(context); + + var mapsterInstance = new Mapper(); + + var query = context.Students.Where(s => s.ID == 1); + + async Task FirstExecute() => + await mapsterInstance.From(query) + .ProjectToType() + .FirstOrDefaultAsync(); + + await Should.NotThrowAsync(async () => + { + var first = await FirstExecute(); + + first.ShouldNotBeNull(); + first.ID.ShouldBe(1); + first.LastName.ShouldBe("Alexander"); + }); + } + [TestMethod] public void MapperInstance_From_OrderBy() { diff --git a/src/Mapster.EFCore/MapsterQueryable.cs b/src/Mapster.EFCore/MapsterQueryable.cs index 8d9ffb8a..cc4cd6be 100644 --- a/src/Mapster.EFCore/MapsterQueryable.cs +++ b/src/Mapster.EFCore/MapsterQueryable.cs @@ -82,11 +82,21 @@ public TResult ExecuteAsync(Expression expression, CancellationToken ca { var enumerable = ((IAsyncQueryProvider)_provider).ExecuteAsync(expression, cancellationToken); var enumerableType = typeof(TResult); + if (!IsAsyncEnumerableType(enumerableType)) + { + return enumerable; + } var elementType = enumerableType.GetGenericArguments()[0]; var wrapType = typeof(MapsterAsyncEnumerable<>).MakeGenericType(elementType); return (TResult) Activator.CreateInstance(wrapType, enumerable, _builder); } + private static bool IsAsyncEnumerableType(Type type) + { + return type.GetInterfaces() + .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>)); + } + public IAsyncEnumerable ExecuteEnumerableAsync(Expression expression, CancellationToken cancellationToken = default) { var enumerable = ((IAsyncQueryProvider)_provider).ExecuteAsync>(expression, cancellationToken); From 59c74ae0124d278bbc1f2bc7da64b30422c3d0d5 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 17 Oct 2025 18:11:56 +0500 Subject: [PATCH 323/363] refactoring support .Inherits() and .Include() to map OpenGenerics --- src/Mapster/TypeAdapterConfig.cs | 24 ++++-------------------- src/Mapster/TypeAdapterSetter.cs | 19 +++++++++---------- src/Mapster/TypeAdapterSettings.cs | 10 ++++++++++ 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index a6633bb0..1e6abade 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -13,8 +13,6 @@ namespace Mapster { public class TypeAdapterConfig { - public Type SourceType { get; protected set; } - public Type DestinationType { get; protected set; } public static List RulesTemplate { get; } = CreateRuleTemplate(); public static TypeAdapterConfig GlobalSettings { get; } = new TypeAdapterConfig(); @@ -150,9 +148,6 @@ public TypeAdapterSetter When(Func canMap) /// public TypeAdapterSetter NewConfig() { - this.SourceType = typeof(TSource); - this.DestinationType = typeof(TDestination); - Remove(typeof(TSource), typeof(TDestination)); return ForType(); } @@ -166,9 +161,6 @@ public TypeAdapterSetter NewConfig /// public TypeAdapterSetter NewConfig(Type sourceType, Type destinationType) { - this.SourceType = sourceType; - this.DestinationType = destinationType; - Remove(sourceType, destinationType); return ForType(sourceType, destinationType); } @@ -182,9 +174,6 @@ public TypeAdapterSetter NewConfig(Type sourceType, Type destinationType) /// public TypeAdapterSetter ForType() { - this.SourceType = typeof(TSource); - this.DestinationType = typeof(TDestination); - var key = new TypeTuple(typeof(TSource), typeof(TDestination)); var settings = GetSettings(key); return new TypeAdapterSetter(settings, this); @@ -199,9 +188,6 @@ public TypeAdapterSetter ForType() /// public TypeAdapterSetter ForType(Type sourceType, Type destinationType) { - this.SourceType = sourceType; - this.DestinationType = destinationType; - var key = new TypeTuple(sourceType, destinationType); var settings = GetSettings(key); return new TypeAdapterSetter(settings, this); @@ -215,9 +201,6 @@ public TypeAdapterSetter ForType(Type sourceType, Type destinationType) /// public TypeAdapterSetter ForDestinationType() { - this.SourceType = typeof(void); - this.DestinationType = typeof(TDestination); - var key = new TypeTuple(typeof(void), typeof(TDestination)); var settings = GetSettings(key); return new TypeAdapterSetter(settings, this); @@ -231,9 +214,6 @@ public TypeAdapterSetter ForDestinationType() /// public TypeAdapterSetter ForDestinationType(Type destinationType) { - this.SourceType = typeof(void); - this.DestinationType = destinationType; - var key = new TypeTuple(typeof(void), destinationType); var settings = GetSettings(key); return new TypeAdapterSetter(settings, this); @@ -249,6 +229,10 @@ private TypeAdapterSettings GetSettings(TypeTuple key) Rules.LockAdd(r); return r; }); + + rule.Settings.SourceType = key.Source; + rule.Settings.DestinationType = key.Destination; + return rule.Settings; } diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index 11820954..e08ef312 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -1,10 +1,10 @@ -using System; +using Mapster.Adapters; +using Mapster.Models; +using Mapster.Utils; +using System; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Mapster.Adapters; -using Mapster.Models; -using Mapster.Utils; namespace Mapster { @@ -273,8 +273,8 @@ public static TSetter Include(this TSetter setter, Type sourceType, Typ { setter.CheckCompiled(); - Type baseSourceType = setter.Config.SourceType; - Type baseDestinationType = setter.Config.DestinationType; + Type baseSourceType = setter.Settings.SourceType ?? typeof(void); + Type baseDestinationType = setter.Settings.DestinationType ?? typeof(void); if (baseSourceType.IsOpenGenericType() && baseDestinationType.IsOpenGenericType()) { @@ -291,8 +291,7 @@ public static TSetter Include(this TSetter setter, Type sourceType, Typ if (!baseDestinationType.GetTypeInfo().IsAssignableFrom(destType.GetTypeInfo())) throw new InvalidCastException("In order to use inherits, TDestination must be inherited from TBaseDestination."); } - - + setter.Config.Rules.LockAdd(new TypeAdapterRule { Priority = arg => @@ -310,8 +309,8 @@ public static TSetter Inherits(this TSetter setter, Type baseSourceType { setter.CheckCompiled(); - Type derivedSourceType = setter.Config.SourceType; - Type derivedDestinationType = setter.Config.DestinationType; + Type derivedSourceType = setter.Settings.SourceType ?? typeof(void); + Type derivedDestinationType = setter.Settings.DestinationType ?? typeof(void); if(baseSourceType.IsOpenGenericType() && baseDestinationType.IsOpenGenericType()) { diff --git a/src/Mapster/TypeAdapterSettings.cs b/src/Mapster/TypeAdapterSettings.cs index c896536f..15c666a2 100644 --- a/src/Mapster/TypeAdapterSettings.cs +++ b/src/Mapster/TypeAdapterSettings.cs @@ -10,6 +10,16 @@ namespace Mapster [AdaptWith(AdaptDirectives.DestinationAsRecord)] public class TypeAdapterSettings : SettingStore { + public Type? SourceType + { + get => Get(nameof(SourceType)); + set => Set(nameof(SourceType), value); + } + public Type? DestinationType + { + get => Get(nameof(DestinationType)); + set => Set(nameof(DestinationType), value); + } public IgnoreDictionary Ignore { get => Get(nameof(Ignore), () => new IgnoreDictionary()); From f2cc814874e39a4887145698116ce97d3277ddb5 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 20 Oct 2025 15:56:57 +0500 Subject: [PATCH 324/363] Update Fix #811 --- src/Mapster/Adapters/BaseClassAdapter.cs | 3 ++- src/Mapster/Utils/ReflectionUtils.cs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 7fd04fd3..d134330c 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -227,7 +227,8 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi else { - if (member.Getter.CanBeNull() && member.Ignore.Condition == null) + if (member.Getter.CanBeNull() && member.DestinationMember.Type.IsAbstractOrNotPublicCtor() + && member.Ignore.Condition == null) { var compareNull = Expression.Equal(member.Getter, Expression.Constant(null, member.Getter.Type)); getter = Expression.Condition(ExpressionEx.Not(compareNull), diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 93aa7e62..5060eb2f 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -429,5 +429,16 @@ public static bool IsOpenGenericType(this Type type) return false; } + + public static bool IsAbstractOrNotPublicCtor(this Type type) + { + if(type.IsAbstract) + return true; + + if (type.GetConstructors().All(x => !x.IsPublic)) + return true; + + return false; + } } } \ No newline at end of file From be53c539a5613f28ab73383df3b88fa8101e18d0 Mon Sep 17 00:00:00 2001 From: "Alizadeh, Rouhallah" Date: Tue, 25 Nov 2025 16:44:29 +0330 Subject: [PATCH 325/363] Add Adapt extensions with temporary TypeAdapterConfig and TypeAdapterSetter without modifying GlobalSettings --- src/Mapster.Tests/WhenUseTempAdapterConfig.cs | 75 +++++++++++++++++++ src/Mapster/TypeAdapter.cs | 35 +++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/Mapster.Tests/WhenUseTempAdapterConfig.cs diff --git a/src/Mapster.Tests/WhenUseTempAdapterConfig.cs b/src/Mapster.Tests/WhenUseTempAdapterConfig.cs new file mode 100644 index 00000000..1def0b88 --- /dev/null +++ b/src/Mapster.Tests/WhenUseTempAdapterConfig.cs @@ -0,0 +1,75 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; + +namespace Mapster.Tests +{ + + public record SourceDto(string Name, int Age); + public record DestinationDto(long Id, string Name, int Age); + + [TestClass] + public class WhenUseTempAdapterConfig + { + [TestMethod] + public void Adapt_TemporaryConfig_ShouldMapInitOnlyProperties() + { + // Arrange + var source = new SourceDto("Alice", 30); + long id = 42; + + // Act + var result = source.Adapt(cfg => + { + cfg.NewConfig() + .Map(dest => dest.Id, src => id); + }); + + // Assert + result.Name.ShouldBe("Alice"); + result.Age.ShouldBe(30); + result.Id.ShouldBe(42); + } + + [TestMethod] + public void Adapt_WithSetter_ShouldMapInitOnlyProperties() + { + // Arrange + var source = new SourceDto("Bob", 25); + long id = 99; + + // Act + var result = source.Adapt(setter => + { + setter.Map(dest => dest.Id, src => id); + }); + + // Assert + result.Name.ShouldBe("Bob"); + result.Age.ShouldBe(25); + result.Id.ShouldBe(99); + } + + [TestMethod] + public void Adapt_TemporaryConfig_ShouldNotModifyGlobalSettings() + { + // Arrange + var source = new SourceDto("Charlie", 40); + long id = 123; + + var globalMap = TypeAdapterConfig.GlobalSettings.GetMapFunction(); + + // Act + var result = source.Adapt(setter => + { + setter.Map(dest => dest.Id, src => id); + }); + + // Assert + var original = globalMap(source); // mapping via GlobalSettings + original.Id.ShouldBe(default(long)); // GlobalSettings unaffected + original.Name.ShouldBe("Charlie"); + original.Age.ShouldBe(40); + } + + } +} diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 8b87b3eb..866263e2 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -263,6 +263,41 @@ public static TDestination ValidateAndAdapt(this TSource } return source.Adapt(config); } + + + /// + /// Adapt the source object to a destination type using a temporary configuration. + /// A new TypeAdapterConfig is created for this call, ensuring GlobalSettings remain unchanged. + /// Safe for init-only properties and record types. + /// + /// Destination type. + /// Source object to adapt. + /// Action to customize the temporary config. + /// Adapted destination object of type TDestination. + public static TDestination Adapt(this object? source, Action configAction) + { + var config = new TypeAdapterConfig(); + configAction(config); + return source.Adapt(config); + } + + /// + /// Adapt the source object from TSource to TDestination using a dedicated TypeAdapterSetter. + /// A temporary TypeAdapterConfig is created and configured via the setter. + /// Safe for init-only properties and record types, without modifying GlobalSettings. + /// + /// Source type. + /// Destination type. + /// Source object to adapt. + /// Action to customize the TypeAdapterSetter. + /// Adapted destination object of type TDestination. + public static TDestination Adapt(this object? source, Action> configAction) + { + var config = new TypeAdapterConfig(); + var setter = config.NewConfig(); + configAction(setter); + return source.Adapt(config); + } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S1104:Fields should not have public accessibility", Justification = "")] From 1c2ee28387abdf2284a841c694089669de209c2a Mon Sep 17 00:00:00 2001 From: "Alizadeh, Rouhallah" Date: Wed, 26 Nov 2025 08:31:20 +0330 Subject: [PATCH 326/363] use TypeAdapterConfig.GlobalSettings.Clone() as base config --- src/Mapster/TypeAdapter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 866263e2..a4edea50 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -276,7 +276,7 @@ public static TDestination ValidateAndAdapt(this TSource /// Adapted destination object of type TDestination. public static TDestination Adapt(this object? source, Action configAction) { - var config = new TypeAdapterConfig(); + var config = TypeAdapterConfig.GlobalSettings.Clone(); configAction(config); return source.Adapt(config); } @@ -293,7 +293,7 @@ public static TDestination Adapt(this object? source, ActionAdapted destination object of type TDestination. public static TDestination Adapt(this object? source, Action> configAction) { - var config = new TypeAdapterConfig(); + var config = TypeAdapterConfig.GlobalSettings.Clone(); var setter = config.NewConfig(); configAction(setter); return source.Adapt(config); From a009effa20f6045e5faa2c07d44105c621605aa3 Mon Sep 17 00:00:00 2001 From: "Alizadeh, Rouhallah" Date: Wed, 26 Nov 2025 08:31:20 +0330 Subject: [PATCH 327/363] use config.ForType insted of config.NewConfig --- src/Mapster/TypeAdapter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index a4edea50..7a42ab4a 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -294,8 +294,9 @@ public static TDestination Adapt(this object? source, Action(this object? source, Action> configAction) { var config = TypeAdapterConfig.GlobalSettings.Clone(); - var setter = config.NewConfig(); + var setter = config.ForType(); configAction(setter); + setter.Settings.Resolvers.Reverse(); return source.Adapt(config); } } From c4d3e8d4b1c4f8df9e6133d078b2c0f310aa28a7 Mon Sep 17 00:00:00 2001 From: Stage Presence Date: Tue, 2 Dec 2025 17:21:38 -0500 Subject: [PATCH 328/363] rename without quotes --- ...erface.md" => Mapping-Configuration-With-IMapFrom-Interface.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "Mapping-Configuration-With-\"IMapFrom\"-Interface.md" => Mapping-Configuration-With-IMapFrom-Interface.md (100%) diff --git "a/Mapping-Configuration-With-\"IMapFrom\"-Interface.md" b/Mapping-Configuration-With-IMapFrom-Interface.md similarity index 100% rename from "Mapping-Configuration-With-\"IMapFrom\"-Interface.md" rename to Mapping-Configuration-With-IMapFrom-Interface.md From 3dd2a01eab09de6e8a5fe37047fc04c5a27753b3 Mon Sep 17 00:00:00 2001 From: Stage Presence Date: Tue, 2 Dec 2025 18:37:46 -0500 Subject: [PATCH 329/363] remove quotes from sidebar link --- _Sidebar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Sidebar.md b/_Sidebar.md index b03ce1ce..24a52256 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -8,7 +8,7 @@ * [Basic usages](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) * [Mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) * [Data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) -* [Mapping with interface](https://github.com/MapsterMapper/Mapster/wiki/Mapping-Configuration-With-%22IMapFrom%22-Interface) +* [Mapping with interface](https://github.com/MapsterMapper/Mapster/wiki/Mapping-Configuration-With-IMapFrom-Interface) ##### Configuration * [Configuration](https://github.com/MapsterMapper/Mapster/wiki/Configuration) From 61d63b104110d199005454fa154763186dd06eda Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Wed, 3 Dec 2025 10:29:08 +0100 Subject: [PATCH 330/363] docs(wiki): Import wiki into repository files --- {wiki => docs/articles}/Home.md | 0 {wiki => docs/articles}/_Sidebar.md | 0 .../articles/configuration}/Config-for-nested-mapping.md | 0 {wiki => docs/articles/configuration}/Config-inheritance.md | 0 {wiki => docs/articles/configuration}/Config-instance.md | 0 {wiki => docs/articles/configuration}/Config-location.md | 0 .../articles/configuration}/Config-validation-&-compilation.md | 0 {wiki => docs/articles/configuration}/Configuration.md | 0 {wiki => docs/articles/mapping}/Basic-usages.md | 0 {wiki => docs/articles/mapping}/Data-types.md | 0 {wiki => docs/articles/mapping}/Mappers.md | 0 .../mapping}/Mapping-Configuration-With-IMapFrom-Interface.md | 0 {wiki => docs/articles/plugins}/Async.md | 0 {wiki => docs/articles/plugins}/Debugging.md | 0 {wiki => docs/articles/plugins}/Dependency-Injection.md | 0 {wiki => docs/articles/plugins}/EF-6-&-EF-Core.md | 0 {wiki => docs/articles/plugins}/FastExpressionCompiler.md | 0 {wiki => docs/articles/plugins}/Immutable.md | 0 {wiki => docs/articles/plugins}/Json.net.md | 0 {wiki => docs/articles/settings}/Before-after-mapping.md | 0 {wiki => docs/articles/settings}/Constructor-mapping.md | 0 {wiki => docs/articles/settings}/Custom-conversion-logic.md | 0 {wiki => docs/articles/settings}/Object-references.md | 0 {wiki => docs/articles/settings}/Setting-values.md | 0 {wiki => docs/articles/settings}/Shallow-merge.md | 0 {wiki => docs/articles/settings/custom}/Custom-mapping.md | 0 {wiki => docs/articles/settings/custom}/Ignoring-members.md | 0 .../articles/settings/custom}/Mapping-non-public-members.md | 0 {wiki => docs/articles/settings/custom}/Mapping-readonly-prop.md | 0 {wiki => docs/articles/settings/custom}/Naming-convention.md | 0 .../articles/settings/custom}/Rule-based-member-mapping.md | 0 {wiki => docs/articles/settings/custom}/Setting-by-attributes.md | 0 {wiki => docs/articles/settings/custom}/Two-ways.md | 0 {wiki => docs/articles/tools}/Mapster.Tool.md | 0 {wiki => docs/articles/tools}/TextTemplate.md | 0 .../tools/mapster-tool}/Attribute-base-Code-generation.md | 0 .../articles/tools/mapster-tool}/Fluent-API-Code-generation.md | 0 .../tools/mapster-tool}/Interface-base-Code-generation.md | 0 38 files changed, 0 insertions(+), 0 deletions(-) rename {wiki => docs/articles}/Home.md (100%) rename {wiki => docs/articles}/_Sidebar.md (100%) rename {wiki => docs/articles/configuration}/Config-for-nested-mapping.md (100%) rename {wiki => docs/articles/configuration}/Config-inheritance.md (100%) rename {wiki => docs/articles/configuration}/Config-instance.md (100%) rename {wiki => docs/articles/configuration}/Config-location.md (100%) rename {wiki => docs/articles/configuration}/Config-validation-&-compilation.md (100%) rename {wiki => docs/articles/configuration}/Configuration.md (100%) rename {wiki => docs/articles/mapping}/Basic-usages.md (100%) rename {wiki => docs/articles/mapping}/Data-types.md (100%) rename {wiki => docs/articles/mapping}/Mappers.md (100%) rename {wiki => docs/articles/mapping}/Mapping-Configuration-With-IMapFrom-Interface.md (100%) rename {wiki => docs/articles/plugins}/Async.md (100%) rename {wiki => docs/articles/plugins}/Debugging.md (100%) rename {wiki => docs/articles/plugins}/Dependency-Injection.md (100%) rename {wiki => docs/articles/plugins}/EF-6-&-EF-Core.md (100%) rename {wiki => docs/articles/plugins}/FastExpressionCompiler.md (100%) rename {wiki => docs/articles/plugins}/Immutable.md (100%) rename {wiki => docs/articles/plugins}/Json.net.md (100%) rename {wiki => docs/articles/settings}/Before-after-mapping.md (100%) rename {wiki => docs/articles/settings}/Constructor-mapping.md (100%) rename {wiki => docs/articles/settings}/Custom-conversion-logic.md (100%) rename {wiki => docs/articles/settings}/Object-references.md (100%) rename {wiki => docs/articles/settings}/Setting-values.md (100%) rename {wiki => docs/articles/settings}/Shallow-merge.md (100%) rename {wiki => docs/articles/settings/custom}/Custom-mapping.md (100%) rename {wiki => docs/articles/settings/custom}/Ignoring-members.md (100%) rename {wiki => docs/articles/settings/custom}/Mapping-non-public-members.md (100%) rename {wiki => docs/articles/settings/custom}/Mapping-readonly-prop.md (100%) rename {wiki => docs/articles/settings/custom}/Naming-convention.md (100%) rename {wiki => docs/articles/settings/custom}/Rule-based-member-mapping.md (100%) rename {wiki => docs/articles/settings/custom}/Setting-by-attributes.md (100%) rename {wiki => docs/articles/settings/custom}/Two-ways.md (100%) rename {wiki => docs/articles/tools}/Mapster.Tool.md (100%) rename {wiki => docs/articles/tools}/TextTemplate.md (100%) rename {wiki => docs/articles/tools/mapster-tool}/Attribute-base-Code-generation.md (100%) rename {wiki => docs/articles/tools/mapster-tool}/Fluent-API-Code-generation.md (100%) rename {wiki => docs/articles/tools/mapster-tool}/Interface-base-Code-generation.md (100%) diff --git a/wiki/Home.md b/docs/articles/Home.md similarity index 100% rename from wiki/Home.md rename to docs/articles/Home.md diff --git a/wiki/_Sidebar.md b/docs/articles/_Sidebar.md similarity index 100% rename from wiki/_Sidebar.md rename to docs/articles/_Sidebar.md diff --git a/wiki/Config-for-nested-mapping.md b/docs/articles/configuration/Config-for-nested-mapping.md similarity index 100% rename from wiki/Config-for-nested-mapping.md rename to docs/articles/configuration/Config-for-nested-mapping.md diff --git a/wiki/Config-inheritance.md b/docs/articles/configuration/Config-inheritance.md similarity index 100% rename from wiki/Config-inheritance.md rename to docs/articles/configuration/Config-inheritance.md diff --git a/wiki/Config-instance.md b/docs/articles/configuration/Config-instance.md similarity index 100% rename from wiki/Config-instance.md rename to docs/articles/configuration/Config-instance.md diff --git a/wiki/Config-location.md b/docs/articles/configuration/Config-location.md similarity index 100% rename from wiki/Config-location.md rename to docs/articles/configuration/Config-location.md diff --git a/wiki/Config-validation-&-compilation.md b/docs/articles/configuration/Config-validation-&-compilation.md similarity index 100% rename from wiki/Config-validation-&-compilation.md rename to docs/articles/configuration/Config-validation-&-compilation.md diff --git a/wiki/Configuration.md b/docs/articles/configuration/Configuration.md similarity index 100% rename from wiki/Configuration.md rename to docs/articles/configuration/Configuration.md diff --git a/wiki/Basic-usages.md b/docs/articles/mapping/Basic-usages.md similarity index 100% rename from wiki/Basic-usages.md rename to docs/articles/mapping/Basic-usages.md diff --git a/wiki/Data-types.md b/docs/articles/mapping/Data-types.md similarity index 100% rename from wiki/Data-types.md rename to docs/articles/mapping/Data-types.md diff --git a/wiki/Mappers.md b/docs/articles/mapping/Mappers.md similarity index 100% rename from wiki/Mappers.md rename to docs/articles/mapping/Mappers.md diff --git a/wiki/Mapping-Configuration-With-IMapFrom-Interface.md b/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md similarity index 100% rename from wiki/Mapping-Configuration-With-IMapFrom-Interface.md rename to docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md diff --git a/wiki/Async.md b/docs/articles/plugins/Async.md similarity index 100% rename from wiki/Async.md rename to docs/articles/plugins/Async.md diff --git a/wiki/Debugging.md b/docs/articles/plugins/Debugging.md similarity index 100% rename from wiki/Debugging.md rename to docs/articles/plugins/Debugging.md diff --git a/wiki/Dependency-Injection.md b/docs/articles/plugins/Dependency-Injection.md similarity index 100% rename from wiki/Dependency-Injection.md rename to docs/articles/plugins/Dependency-Injection.md diff --git a/wiki/EF-6-&-EF-Core.md b/docs/articles/plugins/EF-6-&-EF-Core.md similarity index 100% rename from wiki/EF-6-&-EF-Core.md rename to docs/articles/plugins/EF-6-&-EF-Core.md diff --git a/wiki/FastExpressionCompiler.md b/docs/articles/plugins/FastExpressionCompiler.md similarity index 100% rename from wiki/FastExpressionCompiler.md rename to docs/articles/plugins/FastExpressionCompiler.md diff --git a/wiki/Immutable.md b/docs/articles/plugins/Immutable.md similarity index 100% rename from wiki/Immutable.md rename to docs/articles/plugins/Immutable.md diff --git a/wiki/Json.net.md b/docs/articles/plugins/Json.net.md similarity index 100% rename from wiki/Json.net.md rename to docs/articles/plugins/Json.net.md diff --git a/wiki/Before-after-mapping.md b/docs/articles/settings/Before-after-mapping.md similarity index 100% rename from wiki/Before-after-mapping.md rename to docs/articles/settings/Before-after-mapping.md diff --git a/wiki/Constructor-mapping.md b/docs/articles/settings/Constructor-mapping.md similarity index 100% rename from wiki/Constructor-mapping.md rename to docs/articles/settings/Constructor-mapping.md diff --git a/wiki/Custom-conversion-logic.md b/docs/articles/settings/Custom-conversion-logic.md similarity index 100% rename from wiki/Custom-conversion-logic.md rename to docs/articles/settings/Custom-conversion-logic.md diff --git a/wiki/Object-references.md b/docs/articles/settings/Object-references.md similarity index 100% rename from wiki/Object-references.md rename to docs/articles/settings/Object-references.md diff --git a/wiki/Setting-values.md b/docs/articles/settings/Setting-values.md similarity index 100% rename from wiki/Setting-values.md rename to docs/articles/settings/Setting-values.md diff --git a/wiki/Shallow-merge.md b/docs/articles/settings/Shallow-merge.md similarity index 100% rename from wiki/Shallow-merge.md rename to docs/articles/settings/Shallow-merge.md diff --git a/wiki/Custom-mapping.md b/docs/articles/settings/custom/Custom-mapping.md similarity index 100% rename from wiki/Custom-mapping.md rename to docs/articles/settings/custom/Custom-mapping.md diff --git a/wiki/Ignoring-members.md b/docs/articles/settings/custom/Ignoring-members.md similarity index 100% rename from wiki/Ignoring-members.md rename to docs/articles/settings/custom/Ignoring-members.md diff --git a/wiki/Mapping-non-public-members.md b/docs/articles/settings/custom/Mapping-non-public-members.md similarity index 100% rename from wiki/Mapping-non-public-members.md rename to docs/articles/settings/custom/Mapping-non-public-members.md diff --git a/wiki/Mapping-readonly-prop.md b/docs/articles/settings/custom/Mapping-readonly-prop.md similarity index 100% rename from wiki/Mapping-readonly-prop.md rename to docs/articles/settings/custom/Mapping-readonly-prop.md diff --git a/wiki/Naming-convention.md b/docs/articles/settings/custom/Naming-convention.md similarity index 100% rename from wiki/Naming-convention.md rename to docs/articles/settings/custom/Naming-convention.md diff --git a/wiki/Rule-based-member-mapping.md b/docs/articles/settings/custom/Rule-based-member-mapping.md similarity index 100% rename from wiki/Rule-based-member-mapping.md rename to docs/articles/settings/custom/Rule-based-member-mapping.md diff --git a/wiki/Setting-by-attributes.md b/docs/articles/settings/custom/Setting-by-attributes.md similarity index 100% rename from wiki/Setting-by-attributes.md rename to docs/articles/settings/custom/Setting-by-attributes.md diff --git a/wiki/Two-ways.md b/docs/articles/settings/custom/Two-ways.md similarity index 100% rename from wiki/Two-ways.md rename to docs/articles/settings/custom/Two-ways.md diff --git a/wiki/Mapster.Tool.md b/docs/articles/tools/Mapster.Tool.md similarity index 100% rename from wiki/Mapster.Tool.md rename to docs/articles/tools/Mapster.Tool.md diff --git a/wiki/TextTemplate.md b/docs/articles/tools/TextTemplate.md similarity index 100% rename from wiki/TextTemplate.md rename to docs/articles/tools/TextTemplate.md diff --git a/wiki/Attribute-base-Code-generation.md b/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md similarity index 100% rename from wiki/Attribute-base-Code-generation.md rename to docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md diff --git a/wiki/Fluent-API-Code-generation.md b/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md similarity index 100% rename from wiki/Fluent-API-Code-generation.md rename to docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md diff --git a/wiki/Interface-base-Code-generation.md b/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md similarity index 100% rename from wiki/Interface-base-Code-generation.md rename to docs/articles/tools/mapster-tool/Interface-base-Code-generation.md From 24ccdbc4561eb69bc960e890f84cc5779aceaf6e Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Wed, 3 Dec 2025 10:33:49 +0100 Subject: [PATCH 331/363] docs(api): add api folder with required contents to build api reference docs --- docs/api/.gitignore | 2 ++ docs/api/index.md | 5 +++++ 2 files changed, 7 insertions(+) create mode 100644 docs/api/.gitignore create mode 100644 docs/api/index.md diff --git a/docs/api/.gitignore b/docs/api/.gitignore new file mode 100644 index 00000000..13609968 --- /dev/null +++ b/docs/api/.gitignore @@ -0,0 +1,2 @@ +*.yml +.manifest diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 00000000..96322154 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,5 @@ +# MapsterMapper Mapster API Documentation + +Welcome in the Mapster API documentation! + +Use the table of contents to browse API documentation for the `MapsterMapper.Mapster` Package. From f22da79be566c3727517552725541f9019e881c6 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Wed, 3 Dec 2025 17:42:40 +0100 Subject: [PATCH 332/363] docs: Add yaml headers with uid to markdown files and create appropriate toc files --- .../Config-for-nested-mapping.md | 4 +++ .../configuration/Config-inheritance.md | 4 +++ .../articles/configuration/Config-instance.md | 4 +++ .../articles/configuration/Config-location.md | 4 +++ .../Config-validation-&-compilation.md | 4 +++ docs/articles/configuration/Configuration.md | 4 +++ docs/articles/configuration/toc.yml | 20 +++++++++++++++ docs/articles/mapping/Basic-usages.md | 4 +++ docs/articles/mapping/Data-types.md | 4 +++ docs/articles/mapping/Mappers.md | 4 +++ ...g-Configuration-With-IMapFrom-Interface.md | 4 +++ docs/articles/mapping/toc.yml | 13 ++++++++++ docs/articles/plugins/Async.md | 4 +++ docs/articles/plugins/Debugging.md | 4 +++ docs/articles/plugins/Dependency-Injection.md | 4 +++ docs/articles/plugins/EF-6-&-EF-Core.md | 4 +++ .../plugins/FastExpressionCompiler.md | 4 +++ docs/articles/plugins/Immutable.md | 4 +++ docs/articles/plugins/Json.net.md | 4 +++ docs/articles/plugins/toc.yml | 22 ++++++++++++++++ .../articles/settings/Before-after-mapping.md | 4 +++ docs/articles/settings/Constructor-mapping.md | 4 +++ .../settings/Custom-conversion-logic.md | 4 +++ docs/articles/settings/Object-references.md | 4 +++ docs/articles/settings/Setting-values.md | 4 +++ docs/articles/settings/Shallow-merge.md | 4 +++ .../settings/custom/Custom-mapping.md | 4 +++ .../settings/custom/Ignoring-members.md | 4 +++ .../custom/Mapping-non-public-members.md | 4 +++ .../settings/custom/Mapping-readonly-prop.md | 4 +++ .../settings/custom/Naming-convention.md | 4 +++ .../custom/Rule-based-member-mapping.md | 4 +++ .../settings/custom/Setting-by-attributes.md | 4 +++ docs/articles/settings/custom/Two-ways.md | 4 +++ docs/articles/settings/custom/toc.yml | 25 +++++++++++++++++++ docs/articles/settings/toc.yml | 21 ++++++++++++++++ docs/articles/toc.yml | 13 ++++++++++ docs/articles/tools/Mapster.Tool.md | 4 +++ docs/articles/tools/TextTemplate.md | 4 +++ .../Attribute-base-Code-generation.md | 4 +++ .../Fluent-API-Code-generation.md | 4 +++ .../Interface-base-Code-generation.md | 4 +++ docs/articles/tools/mapster-tool/toc.yml | 7 ++++++ docs/articles/tools/toc.yml | 17 +++++++++++++ docs/toc.yml | 9 +++++++ 45 files changed, 291 insertions(+) create mode 100644 docs/articles/configuration/toc.yml create mode 100644 docs/articles/mapping/toc.yml create mode 100644 docs/articles/plugins/toc.yml create mode 100644 docs/articles/settings/custom/toc.yml create mode 100644 docs/articles/settings/toc.yml create mode 100644 docs/articles/toc.yml create mode 100644 docs/articles/tools/mapster-tool/toc.yml create mode 100644 docs/articles/tools/toc.yml create mode 100644 docs/toc.yml diff --git a/docs/articles/configuration/Config-for-nested-mapping.md b/docs/articles/configuration/Config-for-nested-mapping.md index 49abc1d1..7aefb5eb 100644 --- a/docs/articles/configuration/Config-for-nested-mapping.md +++ b/docs/articles/configuration/Config-for-nested-mapping.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Configuration.NestedMapping +--- + For example if you have parent and child classes. ```csharp diff --git a/docs/articles/configuration/Config-inheritance.md b/docs/articles/configuration/Config-inheritance.md index c6589e2f..f5678f06 100644 --- a/docs/articles/configuration/Config-inheritance.md +++ b/docs/articles/configuration/Config-inheritance.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Configuration.Inheritance +--- + ### Implicit inheritance Type mappings will automatically inherit for source types. Ie. if you set up following config. diff --git a/docs/articles/configuration/Config-instance.md b/docs/articles/configuration/Config-instance.md index ec1feb05..ba131e4a 100644 --- a/docs/articles/configuration/Config-instance.md +++ b/docs/articles/configuration/Config-instance.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Configuration.Instance +--- + ### Config instance You may wish to have different settings in different scenarios. diff --git a/docs/articles/configuration/Config-location.md b/docs/articles/configuration/Config-location.md index cc20241e..67bf1b48 100644 --- a/docs/articles/configuration/Config-location.md +++ b/docs/articles/configuration/Config-location.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Configuration.Location +--- + ### Entry point Configuration should be set only once and reuse for mapping. Therefore, we should not keep configuration and mapping in the same location. For example: diff --git a/docs/articles/configuration/Config-validation-&-compilation.md b/docs/articles/configuration/Config-validation-&-compilation.md index 6eaa7e0d..6f1b73d7 100644 --- a/docs/articles/configuration/Config-validation-&-compilation.md +++ b/docs/articles/configuration/Config-validation-&-compilation.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Configuration.ValidationAndCompilation +--- + To validate your mapping in unit tests and in order to help with "Fail Fast" situations, the following strict mapping modes have been added. ### Explicit Mapping diff --git a/docs/articles/configuration/Configuration.md b/docs/articles/configuration/Configuration.md index 7cf0c487..e5aa9804 100644 --- a/docs/articles/configuration/Configuration.md +++ b/docs/articles/configuration/Configuration.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Configuration.Overview +--- + ### Setting per type pair You can easily create settings for a type mapping by using: `TypeAdapterConfig.NewConfig()`. diff --git a/docs/articles/configuration/toc.yml b/docs/articles/configuration/toc.yml new file mode 100644 index 00000000..f78c20f5 --- /dev/null +++ b/docs/articles/configuration/toc.yml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +# Side navigation for Configuration articles +- name: Configuration + uid: Mapster.Configuration.Overview + href: Configuration.md +- name: Config inheritance + uid: Mapster.Configuration.Inheritance + href: Config-inheritance.md +- name: Config instance + uid: Mapster.Configuration.Instance + href: Config-instance.md +- name: Config location + uid: Mapster.Configuration.Location + href: Config-location.md +- name: Config validation & compilation + uid: Mapster.Configuration.ValidationAndCompilation + href: Config-validation-&-compilation.md +- name: Config for nested mapping + uid: Mapster.Configuration.NestedMapping + href: Config-for-nested-mapping.md diff --git a/docs/articles/mapping/Basic-usages.md b/docs/articles/mapping/Basic-usages.md index 2b351653..246b7ca1 100644 --- a/docs/articles/mapping/Basic-usages.md +++ b/docs/articles/mapping/Basic-usages.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Mapping.BasicUsages +--- + ### Mapping to a new object Mapster creates the destination object and maps values to it. diff --git a/docs/articles/mapping/Data-types.md b/docs/articles/mapping/Data-types.md index a9429f87..f21892f0 100644 --- a/docs/articles/mapping/Data-types.md +++ b/docs/articles/mapping/Data-types.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Mapping.DataTypes +--- + ### Primitives Converting between primitive types (ie. int, bool, double, decimal) is supported, including when those types are nullable. For all other types, if you can cast types in c#, you can also cast in Mapster. diff --git a/docs/articles/mapping/Mappers.md b/docs/articles/mapping/Mappers.md index 62f49aeb..d89b02d7 100644 --- a/docs/articles/mapping/Mappers.md +++ b/docs/articles/mapping/Mappers.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Mapping.Mappers +--- + ### Extension method You can simply call `Adapt` method from anywhere. diff --git a/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md b/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md index 7c59698a..83abca15 100644 --- a/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md +++ b/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Mapping.IMapFromInterface +--- + Before using this feature you have to add this line: ```csharp TypeAdapterConfig.GlobalSettings.ScanInheritedTypes(Assembly.GetExecutingAssembly()); diff --git a/docs/articles/mapping/toc.yml b/docs/articles/mapping/toc.yml new file mode 100644 index 00000000..ab7e7755 --- /dev/null +++ b/docs/articles/mapping/toc.yml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +- name: Basic usages + uid: Mapster.Mapping.BasicUsages + href: Basic-usages.md +- name: Mappers + uid: Mapster.Mapping.Mappers + href: Mappers.md +- name: Data types + uid: Mapster.Mapping.DataTypes + href: Data-types.md +- name: Mapping with interface + uid: Mapster.Mapping.IMapFromInterface + href: Mapping-Configuration-With-IMapFrom-Interface.md \ No newline at end of file diff --git a/docs/articles/plugins/Async.md b/docs/articles/plugins/Async.md index f96b1d2f..a5489e2f 100644 --- a/docs/articles/plugins/Async.md +++ b/docs/articles/plugins/Async.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Plugins.Async +--- + ### Async support PM> Install-Package Mapster.Async diff --git a/docs/articles/plugins/Debugging.md b/docs/articles/plugins/Debugging.md index 88b2bc6f..997e0e8d 100644 --- a/docs/articles/plugins/Debugging.md +++ b/docs/articles/plugins/Debugging.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Plugins.Debugging +--- + ### Step-into debugging PM> Install-Package ExpressionDebugger diff --git a/docs/articles/plugins/Dependency-Injection.md b/docs/articles/plugins/Dependency-Injection.md index d7a50be0..530283e9 100644 --- a/docs/articles/plugins/Dependency-Injection.md +++ b/docs/articles/plugins/Dependency-Injection.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Plugins.DependencyInjection +--- + ### Dependency injection support PM> Install-Package Mapster.DependencyInjection diff --git a/docs/articles/plugins/EF-6-&-EF-Core.md b/docs/articles/plugins/EF-6-&-EF-Core.md index 45818e07..333ae7cd 100644 --- a/docs/articles/plugins/EF-6-&-EF-Core.md +++ b/docs/articles/plugins/EF-6-&-EF-Core.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Plugins.EntityFramework +--- + ### EF 6 & EF Core support For EF 6 diff --git a/docs/articles/plugins/FastExpressionCompiler.md b/docs/articles/plugins/FastExpressionCompiler.md index c312d73a..01b427a6 100644 --- a/docs/articles/plugins/FastExpressionCompiler.md +++ b/docs/articles/plugins/FastExpressionCompiler.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Plugins.FastExpressionCompiler +--- + Need more speed? Let's compile with [FastExpressionCompiler](https://github.com/dadhi/FastExpressionCompiler). Getting the package diff --git a/docs/articles/plugins/Immutable.md b/docs/articles/plugins/Immutable.md index 5e643281..1cb68ff0 100644 --- a/docs/articles/plugins/Immutable.md +++ b/docs/articles/plugins/Immutable.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Plugins.Immutable +--- + # Mapster.Immutable Immutable collection supports diff --git a/docs/articles/plugins/Json.net.md b/docs/articles/plugins/Json.net.md index cbba8c09..f4bfc632 100644 --- a/docs/articles/plugins/Json.net.md +++ b/docs/articles/plugins/Json.net.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Plugins.JsonNet +--- + # Mapster.JsonNet Json.net conversion supports diff --git a/docs/articles/plugins/toc.yml b/docs/articles/plugins/toc.yml new file mode 100644 index 00000000..2f311f2b --- /dev/null +++ b/docs/articles/plugins/toc.yml @@ -0,0 +1,22 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +- name: Async Support + uid: Mapster.Plugins.Async + href: Async.md +- name: Debugging + uid: Mapster.Plugins.Debugging + href: Debugging.md +- name: Dependency Injection + uid: Mapster.Plugins.DependencyInjection + href: Dependency-Injection.md +- name: EF 6 & EF Core + uid: Mapster.Plugins.EntityFramework + href: EF-6-&-EF-Core.md +- name: FastExpressionCompiler + uid: Mapster.Plugins.FastExpressionCompiler + href: FastExpressionCompiler.md +- name: Immutable + uid: Mapster.Plugins.Immutable + href: Immutable.md +- name: Json.net + uid: Mapster.Plugins.JsonNet + href: Json.net.md diff --git a/docs/articles/settings/Before-after-mapping.md b/docs/articles/settings/Before-after-mapping.md index 8f7ea2b9..5a6e7b22 100644 --- a/docs/articles/settings/Before-after-mapping.md +++ b/docs/articles/settings/Before-after-mapping.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.BeforeAfterMapping +--- + ### Before mapping action You can perform actions before mapping started by using `BeforeMapping` method. diff --git a/docs/articles/settings/Constructor-mapping.md b/docs/articles/settings/Constructor-mapping.md index ac22d2a7..9eece89f 100644 --- a/docs/articles/settings/Constructor-mapping.md +++ b/docs/articles/settings/Constructor-mapping.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.ConstructorMapping +--- + ### Custom Destination Object Creation You can provide a function call to create your destination objects instead of using the default object creation diff --git a/docs/articles/settings/Custom-conversion-logic.md b/docs/articles/settings/Custom-conversion-logic.md index c00d3322..e7caa544 100644 --- a/docs/articles/settings/Custom-conversion-logic.md +++ b/docs/articles/settings/Custom-conversion-logic.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.CustomConversionLogic +--- + ### Custom type conversion In some cases, you may want to have complete control over how an object is mapped. You can register specific transformations using the `MapWith` method. diff --git a/docs/articles/settings/Object-references.md b/docs/articles/settings/Object-references.md index d2df4a2e..f9659f9b 100644 --- a/docs/articles/settings/Object-references.md +++ b/docs/articles/settings/Object-references.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.ObjectReferences +--- + ### Preserve reference (preventing circular reference stackoverflow) When mapping objects with circular references, a stackoverflow exception will result. This is because Mapster will get stuck in a loop trying to recursively map the circular reference. If you would like to map circular references or preserve references (such as 2 properties pointing to the same object), you can do it by setting `PreserveReference` to `true` diff --git a/docs/articles/settings/Setting-values.md b/docs/articles/settings/Setting-values.md index 81ccd1c2..3eb55ac1 100644 --- a/docs/articles/settings/Setting-values.md +++ b/docs/articles/settings/Setting-values.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.SettingValues +--- + ### Computed value You can use `Map` method to specify logic to compute value. For example, compute full name from first name and last name. diff --git a/docs/articles/settings/Shallow-merge.md b/docs/articles/settings/Shallow-merge.md index 7408973f..fd99b7c8 100644 --- a/docs/articles/settings/Shallow-merge.md +++ b/docs/articles/settings/Shallow-merge.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.ShallowMerge +--- + ### Deep copy vs shallow copy By default, Mapster will recursively map nested objects (deep copy). You can do shallow copying by setting `ShallowCopyForSameType` to `true`. diff --git a/docs/articles/settings/custom/Custom-mapping.md b/docs/articles/settings/custom/Custom-mapping.md index 2f336609..b0e5d6dc 100644 --- a/docs/articles/settings/custom/Custom-mapping.md +++ b/docs/articles/settings/custom/Custom-mapping.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.Custom.Mapping +--- + ### Custom member mapping You can customize how Mapster maps values to a property. diff --git a/docs/articles/settings/custom/Ignoring-members.md b/docs/articles/settings/custom/Ignoring-members.md index 18ab465b..70078036 100644 --- a/docs/articles/settings/custom/Ignoring-members.md +++ b/docs/articles/settings/custom/Ignoring-members.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.Custom.IgnoringMembers +--- + ### Ignore Mapster will automatically map properties with the same names. You can ignore members by using the `Ignore` method. diff --git a/docs/articles/settings/custom/Mapping-non-public-members.md b/docs/articles/settings/custom/Mapping-non-public-members.md index 2047729f..5b82ecdf 100644 --- a/docs/articles/settings/custom/Mapping-non-public-members.md +++ b/docs/articles/settings/custom/Mapping-non-public-members.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.Custom.NonPublicMembers +--- + ### EnableNonPublicMembers This will allow Mapster to set to all non-public members. diff --git a/docs/articles/settings/custom/Mapping-readonly-prop.md b/docs/articles/settings/custom/Mapping-readonly-prop.md index c43106bc..a6fee542 100644 --- a/docs/articles/settings/custom/Mapping-readonly-prop.md +++ b/docs/articles/settings/custom/Mapping-readonly-prop.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.Custom.ReadonlyProperty +--- + ### Non public setter Mapster can map to non public setter automatically. diff --git a/docs/articles/settings/custom/Naming-convention.md b/docs/articles/settings/custom/Naming-convention.md index dcf1a7d7..2d37f9d2 100644 --- a/docs/articles/settings/custom/Naming-convention.md +++ b/docs/articles/settings/custom/Naming-convention.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.Custom.NamingConvention +--- + ### Flexible name By default, Mapster will map property with case sensitive name. You can adjust to flexible name mapping by setting `NameMatchingStrategy.Flexible` to `NameMatchingStrategy` method. This setting will allow matching between `PascalCase`, `camelCase`, `lower_case`, and `UPPER_CASE`. diff --git a/docs/articles/settings/custom/Rule-based-member-mapping.md b/docs/articles/settings/custom/Rule-based-member-mapping.md index 86fb2d1a..4e3b2bd5 100644 --- a/docs/articles/settings/custom/Rule-based-member-mapping.md +++ b/docs/articles/settings/custom/Rule-based-member-mapping.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.Custom.RuleBasedMapping +--- + By default, Mapster will include public fields and properties, but we can change this behavior by `IncludeMember` and `IgnoreMember` method. The methods require predicate, and input types of predicate are: ```csharp diff --git a/docs/articles/settings/custom/Setting-by-attributes.md b/docs/articles/settings/custom/Setting-by-attributes.md index 08d3b303..8928fca1 100644 --- a/docs/articles/settings/custom/Setting-by-attributes.md +++ b/docs/articles/settings/custom/Setting-by-attributes.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.Custom.Attributes +--- + ### AdaptIgnore attribute When a property decorated with `[AdaptIgnore]`, that property will be excluded from Mapping. For example, if we would like to exclude price to be mapped. diff --git a/docs/articles/settings/custom/Two-ways.md b/docs/articles/settings/custom/Two-ways.md index 9c0144d9..d270baea 100644 --- a/docs/articles/settings/custom/Two-ways.md +++ b/docs/articles/settings/custom/Two-ways.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Settings.Custom.TwoWaysMapping +--- + ### 2-ways mapping If you need to map object from POCO to DTO, and map back from DTO to POCO. You can define the setting once by using `TwoWays`. diff --git a/docs/articles/settings/custom/toc.yml b/docs/articles/settings/custom/toc.yml new file mode 100644 index 00000000..19010846 --- /dev/null +++ b/docs/articles/settings/custom/toc.yml @@ -0,0 +1,25 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +- name: Custom mapping + uid: Mapster.Settings.Custom.Mapping + href: Custom-mapping.md +- name: Custom naming convention + uid: Mapster.Settings.Custom.NamingConvention + href: Naming-convention.md +- name: Setting by attributes + uid: Mapster.Settings.Custom.Attributes + href: Setting-by-attributes.md +- name: Ignoring members + uid: Mapster.Settings.Custom.IgnoringMembers + href: Ignoring-members.md +- name: Rule-based member matching + uid: Mapster.Settings.Custom.RuleBasedMapping + href: Rule-based-member-mapping.md +- name: Mapping readonly prop + uid: Mapster.Settings.Custom.ReadonlyProperty + href: Mapping-readonly-prop.md +- name: Mapping non-public members + uid: Mapster.Settings.Custom.NonPublicMembers + href: Mapping-non-public-members.md +- name: Two ways & unflattening mapping + uid: Mapster.Settings.Custom.TwoWaysMapping + href: Two-ways.md diff --git a/docs/articles/settings/toc.yml b/docs/articles/settings/toc.yml new file mode 100644 index 00000000..fab3d549 --- /dev/null +++ b/docs/articles/settings/toc.yml @@ -0,0 +1,21 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +- name: Custom member matching logic + href: custom/toc.yml +- name: Constructor mapping + uid: Mapster.Settings.ConstructorMapping + href: Constructor-mapping.md +- name: Before & after mapping + uid: Mapster.Settings.BeforeAfterMapping + href: Before-after-mapping.md +- name: Setting values + uid: Mapster.Settings.SettingValues + href: Setting-values.md +- name: Shallow & merge mapping + uid: Mapster.Settings.ShallowMerge + href: Shallow-merge.md +- name: Recursive & object references + uid: Mapster.Settings.ObjectReferences + href: Object-references.md +- name: Custom conversion logic + uid: Mapster.Settings.CustomConversionLogic + href: Custom-conversion-logic.md diff --git a/docs/articles/toc.yml b/docs/articles/toc.yml new file mode 100644 index 00000000..0ee9ae5f --- /dev/null +++ b/docs/articles/toc.yml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +# NOTE: starting from here you can add side navigation items by nesting them under "items" +- items: + - name: Mapping + href: mapping/toc.yml + - name: Configuration + href: configuration/toc.yml + - name: Settings + href: settings/toc.yml + - name: Plugins + href: plugins/toc.yml + - name: Tools + href: tools/toc.yml \ No newline at end of file diff --git a/docs/articles/tools/Mapster.Tool.md b/docs/articles/tools/Mapster.Tool.md index 1bf66e8f..1c1b136c 100644 --- a/docs/articles/tools/Mapster.Tool.md +++ b/docs/articles/tools/Mapster.Tool.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Tools.MapsterTool +--- + ## Mapster.Tool ### Install Mapster.Tool diff --git a/docs/articles/tools/TextTemplate.md b/docs/articles/tools/TextTemplate.md index dcce5783..6a7e8f6b 100644 --- a/docs/articles/tools/TextTemplate.md +++ b/docs/articles/tools/TextTemplate.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Tools.TextTemplate +--- + ### TextTemplate ``` PM> Install-Package ExpressionTranslator diff --git a/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md b/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md index 7c42614e..bfe86a5e 100644 --- a/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md +++ b/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Tools.MapsterTool.Attributes +--- + ### Generate models Annotate your class with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`. diff --git a/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md b/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md index 39ddcdf8..9a543a77 100644 --- a/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md +++ b/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Tools.MapsterTool.FluentAPI +--- + ### Configuration class Create a configuration class implement `ICodeGenerationRegister`. diff --git a/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md b/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md index 7ffdb215..efc56b96 100644 --- a/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md +++ b/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md @@ -1,3 +1,7 @@ +--- +uid: Mapster.Tools.MapsterTool.Interfaces +--- + ### Generate mapper from interface Annotate your interface with `[Mapper]` in order for tool to pickup for generation. diff --git a/docs/articles/tools/mapster-tool/toc.yml b/docs/articles/tools/mapster-tool/toc.yml new file mode 100644 index 00000000..c8703eb4 --- /dev/null +++ b/docs/articles/tools/mapster-tool/toc.yml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +- name: Fluent API + href: Fluent-API-Code-generation.md +- name: Attributes + href: Attribute-base-Code-generation.md +- name: Interfaces + href: Interface-base-Code-generation.md diff --git a/docs/articles/tools/toc.yml b/docs/articles/tools/toc.yml new file mode 100644 index 00000000..29177de2 --- /dev/null +++ b/docs/articles/tools/toc.yml @@ -0,0 +1,17 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +- name: Mapster.Tool + uid: Mapster.Tools.MapsterTool + href: Mapster.Tool.md + items: + - name: Fluent API + uid: Mapster.Tools.MapsterTool.FluentAPI + href: mapster-tool/Fluent-API-Code-generation.md + - name: Attributes + uid: Mapster.Tools.MapsterTool.Attributes + href: mapster-tool/Attribute-base-Code-generation.md + - name: Interfaces + uid: Mapster.Tools.MapsterTool.Interfaces + href: mapster-tool/Interface-base-Code-generation.md +- name: TextTemplate + uid: Mapster.Tools.TextTemplate + href: TextTemplate.md diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 00000000..da9d03a1 --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +# NOTE: Those top level items will be shown as the Top Navigation Menu Entries +# IMPORTANT: Do not add the explicit toc.yml files under the appropriate folders here, which already is the default behavior of docfx, as this would otherwhise attempt to show them in the top navigation instead of the side navigation TOCs. +- name: Home + href: index.md +- name: Documentation + href: articles/ +- name: API Reference + href: api/ From b3ab4964fbf446e23d917398c3c99b00c8f2139c Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Wed, 3 Dec 2025 23:17:02 +0100 Subject: [PATCH 333/363] docs: FIx markdownlint, apply xref links and update header levels --- README.md | 86 ++++++---- docs/articles/Home.md | 154 +++++++++--------- .../Config-for-nested-mapping.md | 11 +- .../configuration/Config-inheritance.md | 9 +- .../articles/configuration/Config-instance.md | 19 ++- .../articles/configuration/Config-location.md | 30 ++-- ...d => Config-validation-and-compilation.md} | 15 +- docs/articles/configuration/Configuration.md | 11 +- docs/articles/configuration/toc.yml | 4 +- docs/articles/mapping/Basic-usages.md | 10 +- docs/articles/mapping/Data-types.md | 46 ++++-- docs/articles/mapping/Mappers.md | 11 +- ...g-Configuration-With-IMapFrom-Interface.md | 7 +- docs/articles/{plugins => packages}/Async.md | 20 +-- .../Dependency-Injection.md | 17 +- .../Debugging.md => packages/Diagnostics.md} | 24 +-- .../EF-6-and-EF-Core.md} | 32 ++-- .../FastExpressionCompiler.md | 9 +- .../{plugins => packages}/Immutable.md | 19 ++- .../{plugins => packages}/Json.net.md | 20 ++- docs/articles/packages/toc.yml | 23 +++ docs/articles/plugins/toc.yml | 22 --- .../articles/settings/Before-after-mapping.md | 16 +- docs/articles/settings/Constructor-mapping.md | 6 +- .../settings/Custom-conversion-logic.md | 5 +- docs/articles/settings/Object-references.md | 13 +- docs/articles/settings/Setting-values.md | 17 +- docs/articles/settings/Shallow-merge.md | 6 +- .../settings/custom/Custom-mapping.md | 7 +- .../settings/custom/Ignoring-members.md | 15 +- .../custom/Mapping-non-public-members.md | 17 +- .../settings/custom/Mapping-readonly-prop.md | 9 +- .../settings/custom/Naming-convention.md | 22 +-- .../custom/Rule-based-member-mapping.md | 18 +- .../settings/custom/Setting-by-attributes.md | 23 ++- docs/articles/settings/custom/Two-ways.md | 15 +- docs/articles/tools/TextTemplate.md | 92 ++++++----- .../Attribute-base-Code-generation.md | 76 ++------- .../Attribute-based-Extension-generation.md | 32 ++++ .../Configuration-based-Code-generation.md | 33 ++++ .../Fluent-API-Code-generation.md | 37 +++-- .../Interface-base-Code-generation.md | 7 +- .../Mapster-Tool-Overview.md} | 97 +++++++---- docs/articles/tools/mapster-tool/toc.yml | 6 +- docs/articles/tools/toc.yml | 20 +-- docs/index.md | 2 + docs/toc.yml | 3 +- 47 files changed, 691 insertions(+), 502 deletions(-) rename docs/articles/configuration/{Config-validation-&-compilation.md => Config-validation-and-compilation.md} (91%) rename docs/articles/{plugins => packages}/Async.md (62%) rename docs/articles/{plugins => packages}/Dependency-Injection.md (83%) rename docs/articles/{plugins/Debugging.md => packages/Diagnostics.md} (78%) rename docs/articles/{plugins/EF-6-&-EF-Core.md => packages/EF-6-and-EF-Core.md} (55%) rename docs/articles/{plugins => packages}/FastExpressionCompiler.md (84%) rename docs/articles/{plugins => packages}/Immutable.md (74%) rename docs/articles/{plugins => packages}/Json.net.md (66%) create mode 100644 docs/articles/packages/toc.yml delete mode 100644 docs/articles/plugins/toc.yml create mode 100644 docs/articles/tools/mapster-tool/Attribute-based-Extension-generation.md create mode 100644 docs/articles/tools/mapster-tool/Configuration-based-Code-generation.md rename docs/articles/tools/{Mapster.Tool.md => mapster-tool/Mapster-Tool-Overview.md} (69%) create mode 100644 docs/index.md diff --git a/README.md b/README.md index 9be17bb0..f043abf4 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,55 @@ ![Mapster Icon](https://cloud.githubusercontent.com/assets/5763993/26522718/d16f3e42-4330-11e7-9b78-f8c7402624e7.png) -## Mapster - The Mapper of Your Domain +# Mapster - The Mapper of Your Domain + Writing mapping methods is a machine job. Do not waste your time, let Mapster do it. -[![NuGet](https://img.shields.io/nuget/v/Mapster.svg)](https://www.nuget.org/packages/Mapster) +[![NuGet](https://img.shields.io/nuget/v/Mapster.svg)](https://www.nuget.org/packages/Mapster/latest) + +## Installation -### Installation Install Mapster with the NuGet CLI: -``` + +```Powershell Install-Package Mapster ``` Or use the .NET core CLI to install Mapster: + +```bash +dotnet add package Mapster --project ``` -dotnet add package Mapster -``` -### Basic usage -#### Mapping to a new object +## Basic usage + +### Mapping to a new object + Mapster creates the destination object and maps values to it. ```csharp var destObject = sourceObject.Adapt(); ``` -#### Mapping to an existing object +### Mapping to an existing object + You create the object, Mapster maps to the object. ```csharp sourceObject.Adapt(destObject); ``` -#### You can get IMapper instance via dependency injection so you do not have to change code when migrating to mapster from automapper -Add Mapster to service collection +### Use Mapster with Dependency Injection + +You can get your `IMapper` instance via dependency injection, so you do not have to change code, when migrating to mapster from automapper! + +**Just add Mapster to service collection:** + ```csharp services.AddMapster(); ``` -And use it with DI + +**And use it with DI in your Project:** + ```csharp public class Test { @@ -47,7 +60,8 @@ public class Test } ``` -#### Queryable Extensions +### Queryable Extensions + Mapster also provides extensions to map queryables. ```csharp @@ -67,7 +81,8 @@ using (MyDbContext context = new MyDbContext()) } ``` -#### Generating models & mappers +### Generating models & mappers + No need to write your own DTO classes. Mapster provides [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) to help you generating models. And if you would like to have explicit mapping, Mapster also generates mapper class for you. ```csharp @@ -77,7 +92,8 @@ public class Student { } ``` -Then Mapster will generate: +Then Mapster will generate for you: + ```csharp public class StudentDto { ... @@ -89,7 +105,8 @@ public static class StudentMapper { } ``` -### What's new +## What's new + - [Fluent API for code generation](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation) - [Automatically generate mapping code on build](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) - [Define setting to nested mapping](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-mapping) @@ -99,10 +116,13 @@ public static class StudentMapper { - New plugins - [Immutable collection support](https://github.com/MapsterMapper/Mapster/wiki/Immutable) -### Why Mapster? -#### Performance & Memory efficient +## Why Mapster? + +### Performance & Memory efficient + Mapster was designed to be efficient on both speed and memory. You could gain a 4x performance improvement whilst using only 1/3 of memory. -And you could gain up to 12x faster performance with +And you could gain up to 12x faster performance with: + - [Roslyn Compiler](https://github.com/MapsterMapper/Mapster/wiki/Debugging) - [FEC](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) - Code generation @@ -116,34 +136,38 @@ And you could gain up to 12x faster performance with | 'ExpressMapper 1.9.1' | 205.78 ms | 5.357 ms | 8.098 ms | 59000.0000 | - | - | 236.51 MB | | 'AutoMapper 10.0.0' | 420.97 ms | 23.266 ms | 35.174 ms | 87000.0000 | - | - | 350.95 MB | +### Step into debugging +[Step-into debugging](https://mapstermapper.github.io/docs/packages/Diagnostics.html) lets you debug your mapping and inspect values just like your code. +![inspect-generated-source-code-while-debugging](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) -#### Step into debugging +### Code Generation -[Step-into debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) lets you debug your mapping and inspect values just like your code. -![image](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) - -#### Code Generation Code generation allows you to + - Validate mapping at compile time - Getting raw performance - Seeing your mapping code & debugging - Finding usage of your models' properties There are currently two tools which you can choose based on your preferences. -* [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) NEW! -* [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) -### Change logs +- [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) ***NEW!*** +- [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) + +## Change logs + https://github.com/MapsterMapper/Mapster/releases -### Usages -* [English](https://github.com/MapsterMapper/Mapster/wiki) -* [中文文档](https://github.com/rivenfx/Mapster-docs) (sp thx to [@staneee](https://github.com/staneee)) +## Usage Guides with Translations + +- [English](https://github.com/MapsterMapper/Mapster/wiki) +- [中文文档](https://github.com/rivenfx/Mapster-docs) (sp thx to [@staneee](https://github.com/staneee)) -### Acknowledgements +## Acknowledgements [JetBrains](https://www.jetbrains.com/?from=Mapster) kindly provides Mapster with a free open-source licence for their Resharper and Rider. + - **Resharper** makes Visual Studio a much better IDE - **Rider** is fast & powerful cross platform .NET IDE diff --git a/docs/articles/Home.md b/docs/articles/Home.md index 16899f3e..ddb05c69 100644 --- a/docs/articles/Home.md +++ b/docs/articles/Home.md @@ -1,27 +1,29 @@ # References -### Basic +## Basic | Method | Description | Link | | ------------- |-----------------------| ----- | -| `src.Adapt()` | Mapping to new type | [basic](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) | -| `src.Adapt(dest)` | Mapping to existing object | [basic](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) | -| `query.ProjectToType()` | Mapping from queryable | [basic](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) | -| | Convention & Data type support | [data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) | +| `src.Adapt()` | Mapping to new type | [basic](xref:Mapster.Mapping.BasicUsages) | +| `src.Adapt(dest)` | Mapping to existing object | [basic](xref:Mapster.Mapping.BasicUsages) | +| `query.ProjectToType()` | Mapping from queryable | [basic](xref:Mapster.Mapping.BasicUsages) | +| | Convention & Data type support | [data types](xref:Mapster.Mapping.DataTypes) | + +### Mapper instance (for dependency injection) -#### Mapper instance (for dependency injection) | Method | Description | Link | | ------------- |-----------------------| ----- | -| `IMapper mapper = new Mapper()` | Create mapper instance | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | +| `IMapper mapper = new Mapper()` | Create mapper instance | [mappers](xref:Mapster.Mapping.Mappers) | | `mapper.Map(src)` | Mapping to new type | | | `mapper.Map(src, dest)` | Mapping to existing object | | -#### Builder (for complex mapping) +### Builder (for complex mapping) + | Method | Description | Link | | ------------- |-----------------------| ----- | -| `src.BuildAdapter()`
`mapper.From(src)` | Create builder | [mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) | -| `.ForkConfig(config => ...)` | Inline configuration | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | -| `.AddParameters(name, value)` | Passing runtime value | [setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) | +| `src.BuildAdapter()`
`mapper.From(src)` | Create builder | [mappers](xref:Mapster.Mapping.Mappers) | +| `.ForkConfig(config => ...)` | Inline configuration | [config location](xref:Mapster.Configuration.Location) | +| `.AddParameters(name, value)` | Passing runtime value | [setting values](xref:Mapster.Settings.SettingValues) | | `.AdaptToType()` | Mapping to new type | | | `.AdaptTo(dest)` | Mapping to existing object | | | `.CreateMapExpression()` | Get mapping expression | | @@ -29,102 +31,104 @@ | `.CreateProjectionExpression()` | Get mapping from queryable expression | | #### Config + | Method | Description | Link | | ------------- |-----------------------| ----- | -| `TypeAdapterConfig.GlobalSettings` | Global config | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `var config = new TypeAdapterConfig()` | Create new config instance | [config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) | +| `TypeAdapterConfig.GlobalSettings` | Global config | [config](xref:Mapster.Configuration.Overview) | +| `var config = new TypeAdapterConfig()` | Create new config instance | [config instance](xref:Mapster.Configuration.Instance) | | `src.Adapt(config)` | Passing config to mapping | | | `new Mapper(config)` | Passing config to mapper instance | | | `src.BuildAdapter(config)` | Passing config to builder | | -| `config.RequireDestinationMemberSource` | Validate all properties are mapped | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | -| `config.RequireExplicitMapping` | Validate all type pairs are defined | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | -| `config.AllowImplicitDestinationInheritance` | Use config from destination based class | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | -| `config.AllowImplicitSourceInheritance` | Use config from source based class | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | -| `config.SelfContainedCodeGeneration` | Generate all nested mapping in 1 method | [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) | -| `config.Compile()` | Validate mapping instruction & cache | [config validation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) | +| `config.RequireDestinationMemberSource` | Validate all properties are mapped | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | +| `config.RequireExplicitMapping` | Validate all type pairs are defined | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | +| `config.AllowImplicitDestinationInheritance` | Use config from destination based class | [inheritance](xref:Mapster.Configuration.Inheritance) | +| `config.AllowImplicitSourceInheritance` | Use config from source based class | [inheritance](xref:Mapster.Configuration.Inheritance) | +| `config.SelfContainedCodeGeneration` | Generate all nested mapping in 1 method | [TextTemplate](xref:Mapster.Tools.TextTemplate) | +| `config.Compile()` | Validate mapping instruction & cache | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | | `config.CompileProjection()` | Validate mapping instruction & cache for queryable | | -| `config.Clone()` | Copy config | [config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) | -| `config.Fork(forked => ...)` | Inline configuration | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | +| `config.Clone()` | Copy config | [config instance](xref:Mapster.Configuration.Instance) | +| `config.Fork(forked => ...)` | Inline configuration | [config location](xref:Mapster.Configuration.Location) | #### Config scanning | Method | Description | Link | | ------------- |-----------------------| ----- | -| `IRegister` | Interface for config scanning | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | -| `config.Scan(...assemblies)` | Scan for config in assemblies | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | -| `config.Apply(...registers)` | Apply registers directly | [config location](https://github.com/MapsterMapper/Mapster/wiki/Config-location) | +| `IRegister` | Interface for config scanning | [config location](xref:Mapster.Configuration.Location) | +| `config.Scan(...assemblies)` | Scan for config in assemblies | [config location](xref:Mapster.Configuration.Location) | +| `config.Apply(...registers)` | Apply registers directly | [config location](xref:Mapster.Configuration.Location) | #### Declare settings | Method | Description | Link | | ------------- |-----------------------| ----- | -| `config.Default` | Get setting applied to all type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `TypeAdapterConfig.NewConfig()`
`config.NewConfig()` | Create setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `TypeAdapterConfig.ForType()`
`config.ForType()` | Get setting applied to specific type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `config.ForType(typeof(GenericPoco<>),typeof(GenericDto<>))` | Get setting applied to generic type pairs | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `config.When((src, dest, mapType) => ...)` | Get setting that applied conditionally | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| `config.ForDestinationType()` | Get setting that applied to specific destination type | [config](https://github.com/MapsterMapper/Mapster/wiki/Configuration) | -| | Configuration for nested mapping | [nested mapping](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-mapping) +| `config.Default` | Get setting applied to all type pairs | [config](xref:Mapster.Configuration.Overview) | +| `TypeAdapterConfig.NewConfig()`
`config.NewConfig()` | Create setting applied to specific type pairs | [config](xref:Mapster.Configuration.Overview) | +| `TypeAdapterConfig.ForType()`
`config.ForType()` | Get setting applied to specific type pairs | [config](xref:Mapster.Configuration.Overview) | +| `config.ForType(typeof(GenericPoco<>),typeof(GenericDto<>))` | Get setting applied to generic type pairs | [config](xref:Mapster.Configuration.Overview) | +| `config.When((src, dest, mapType) => ...)` | Get setting that applied conditionally | [config](xref:Mapster.Configuration.Overview) | +| `config.ForDestinationType()` | Get setting that applied to specific destination type | [config](xref:Mapster.Configuration.Overview) | +| | Configuration for nested mapping | [nested mapping](xref:Mapster.Configuration.NestedMapping) | #### Settings + | Method | Description | Apply to queryable | Link | | ------------- |-----------------------| ------------ | ----- | -| `AddDestinationTransform` | Clean up data for a specific type | x | [setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) | -| `AfterMapping` | Add steps after mapping done | | [before-after](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) | -| `AvoidInlineMapping` | Skip inline process for large type mapping | | [object reference](https://github.com/MapsterMapper/Mapster/wiki/Object-references) | -| `BeforeMapping` | Add steps before mapping start | | [before-after](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) | -| `ConstructUsing` | Define how to create object | x | [constructor](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) | -| `EnableNonPublicMembers` | Mapping non-public properties | | [non-public](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) | -| `EnumMappingStrategy` | Choose whether mapping enum by value or by name | | [data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) | -| `Fork` | Add new settings without side effect on main config | x | [nested mapping](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-mapping) | -| `GetMemberName` | Define how to resolve property name | x | [custom naming](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) | -| `Ignore` | Ignore specific properties | x | [ignore](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) | -| `IgnoreAttribute` | Ignore specific attributes annotated on properties | x | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | -| `IgnoreIf` | Ignore conditionally | x | [ignore](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) | -| `IgnoreMember` | Setup rules to ignore | x | [rule based](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) | -| `IgnoreNonMapped` | Ignore all properties not defined in `Map` | x | [ignore](https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members) | -| `IgnoreNullValues` | Not map if src property is null | | [shallow & merge](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) | -| `Include` | Include derived types on mapping | | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | -| `IncludeAttribute` | Include specific attributes annotated on properties | x | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | -| `IncludeMember` | Setup rules to include | x | [rule based](https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping) | -| `Inherits` | Copy setting from based type | x | [inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) | -| `Map` | Define property pairs | x | [custom mapping](https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) | -| `MapToConstructor` | Mapping to constructor | x | [constructor](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) | -| `MapToTargetWith` | Define how to map to existing object between type pair | | [custom conversion](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) | -| `MapWith` | Define how to map between type pair | x | [custom conversion](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) | -| `MaxDepth` | Limit depth of nested mapping | x | [object reference](https://github.com/MapsterMapper/Mapster/wiki/Object-references) | -| `NameMatchingStrategy` | Define how to resolve property's name | x | [custom naming](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) | -| `PreserveReference` | Tracking reference when mapping | | [object reference](https://github.com/MapsterMapper/Mapster/wiki/Object-references) | -| `ShallowCopyForSameType` | Direct assign rather than deep clone if type pairs are the same | | [shallow & merge](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) | -| `TwoWays` | Define type mapping are 2 ways | x | [2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | -| `Unflattening` | Allow unflatten mapping | x |[2-ways & unflattening](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) | -| `UseDestinationValue` | Use existing property object to map data | |[readonly-prop](https://github.com/MapsterMapper/Mapster/wiki/Mapping-readonly-prop) | +| `AddDestinationTransform` | Clean up data for a specific type | x | [setting values](xref:Mapster.Settings.SettingValues) | +| `AfterMapping` | Add steps after mapping done | | [before-after](xref:Mapster.Settings.BeforeAfterMapping) | +| `AvoidInlineMapping` | Skip inline process for large type mapping | | [object reference](xref:Mapster.Settings.ObjectReferences) | +| `BeforeMapping` | Add steps before mapping start | | [before-after](xref:Mapster.Settings.BeforeAfterMapping) | +| `ConstructUsing` | Define how to create object | x | [constructor](xref:Mapster.Settings.ConstructorMapping) | +| `EnableNonPublicMembers` | Mapping non-public properties | | [non-public](xref:Mapster.Settings.Custom.NonPublicMembers) | +| `EnumMappingStrategy` | Choose whether mapping enum by value or by name | | [data types](xref:Mapster.Mapping.DataTypes) | +| `Fork` | Add new settings without side effect on main config | x | [nested mapping](xref:Mapster.Configuration.NestedMapping) | +| `GetMemberName` | Define how to resolve property name | x | [custom naming](xref:Mapster.Settings.Custom.NamingConvention) | +| `Ignore` | Ignore specific properties | x | [ignore](xref:Mapster.Settings.Custom.IgnoringMembers) | +| `IgnoreAttribute` | Ignore specific attributes annotated on properties | x | [attribute](xref:Mapster.Settings.Custom.Attributes) | +| `IgnoreIf` | Ignore conditionally | x | [ignore](xref:Mapster.Settings.Custom.IgnoringMembers) | +| `IgnoreMember` | Setup rules to ignore | x | [rule based](xref:Mapster.Settings.Custom.RuleBasedMapping) | +| `IgnoreNonMapped` | Ignore all properties not defined in `Map` | x | [ignore](xref:Mapster.Settings.Custom.IgnoringMembers) | +| `IgnoreNullValues` | Not map if src property is null | | [shallow & merge](xref:Mapster.Settings.ShallowMerge) | +| `Include` | Include derived types on mapping | | [inheritance](xref:Mapster.Configuration.Inheritance) | +| `IncludeAttribute` | Include specific attributes annotated on properties | x | [attribute](xref:Mapster.Settings.Custom.Attributes) | +| `IncludeMember` | Setup rules to include | x | [rule based](xref:Mapster.Settings.Custom.RuleBasedMapping) | +| `Inherits` | Copy setting from based type | x | [inheritance](xref:Mapster.Configuration.Inheritance) | +| `Map` | Define property pairs | x | [custom mapping](xref:Mapster.Settings.Custom.Mapping) | +| `MapToConstructor` | Mapping to constructor | x | [constructor](xref:Mapster.Settings.ConstructorMapping) | +| `MapToTargetWith` | Define how to map to existing object between type pair | | [custom conversion](xref:Mapster.Settings.CustomConversionLogic) | +| `MapWith` | Define how to map between type pair | x | [custom conversion](xref:Mapster.Settings.CustomConversionLogic) | +| `MaxDepth` | Limit depth of nested mapping | x | [object reference](xref:Mapster.Settings.ObjectReferences) | +| `NameMatchingStrategy` | Define how to resolve property's name | x | [custom naming](xref:Mapster.Settings.Custom.NamingConvention) | +| `PreserveReference` | Tracking reference when mapping | | [object reference](xref:Mapster.Settings.ObjectReferences) | +| `ShallowCopyForSameType` | Direct assign rather than deep clone if type pairs are the same | | [shallow & merge](xref:Mapster.Settings.ShallowMerge) | +| `TwoWays` | Define type mapping are 2 ways | x | [2-ways & unflattening](xref:Mapster.Settings.Custom.TwoWaysMapping) | +| `Unflattening` | Allow unflatten mapping | x |[2-ways & unflattening](xref:Mapster.Settings.Custom.TwoWaysMapping) | +| `UseDestinationValue` | Use existing property object to map data | |[readonly-prop](xref:Mapster.Settings.Custom.ReadonlyProperty) | #### Attributes | Annotation | Description | Link | | ------------- |-----------------------| ----- | -| `[AdaptMember(name)]` | Mapping property to different name | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | -| `[AdaptIgnore(side)]` | Ignore property from mapping | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | -| `[UseDestinationValue]` | Use existing property object to map data | [attribute](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) | -| `[AdaptTo]` `[AdaptFrom]` `[AdaptTwoWays]` | Add setting on POCO class | [location](https://github.com/MapsterMapper/Mapster/wiki/Config-location#attributes) | -| `[Mapper]` `[GenerateMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | +| `[AdaptMember(name)]` | Mapping property to different name | [attribute](xref:Mapster.Settings.Custom.Attributes) | +| `[AdaptIgnore(side)]` | Ignore property from mapping | [attribute](xref:Mapster.Settings.Custom.Attributes) | +| `[UseDestinationValue]` | Use existing property object to map data | [attribute](xref:Mapster.Settings.Custom.Attributes) | +| `[AdaptTo]` `[AdaptFrom]` `[AdaptTwoWays]` | Add setting on POCO class | [location](xref:Mapster.Configuration.Location#attributes) | +| `[Mapper]` `[GenerateMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](xref:Mapster.Tools.MapsterTool) | #### Plugins | Plugin | Method | Description | | ------ | ------------- |-----------------------| -| [Async](https://github.com/MapsterMapper/Mapster/wiki/Async) | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | -| [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | -| [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) | `MapContext.Current.GetService()` | Inject service into mapping logic | -| [EF 6 & EF Core](https://github.com/MapsterMapper/Mapster/wiki/EF-6-&-EF-Core) | `builder.EntityFromContext` | Copy data to tracked EF entity | -| [FEC](https://github.com/MapsterMapper/Mapster/wiki/FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | -| [Immutable](https://github.com/MapsterMapper/Mapster/wiki/Immutable) | `config.EnableImmutableMapping()` | mapping to immutable collection | -| [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) | `config.EnableJsonMapping()` | map json from/to poco and string | +| [Async](xref:Mapster.Plugins.Async) | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | +| [Debugging](xref:Mapster.Plugins.Debugging) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | +| [Dependency Injection](xref:Mapster.Plugins.DependencyInjection) | `MapContext.Current.GetService()` | Inject service into mapping logic | +| [EF 6 & EF Core](xref:Mapster.Plugins.EntityFramework) | `builder.EntityFromContext` | Copy data to tracked EF entity | +| [FEC](xref:Mapster.Plugins.FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | +| [Immutable](xref:Mapster.Plugins.Immutable) | `config.EnableImmutableMapping()` | mapping to immutable collection | +| [Json.net](xref:Mapster.Plugins.JsonNet) | `config.EnableJsonMapping()` | map json from/to poco and string | #### Code Generation Tools | Plugin | Tool | Description | | ------ | ------------- |-----------------------| -| [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) | `dotnet mapster` | generate DTOs and mapping codes on build | -| [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) | `t4` | generate mapping codes using t4 | +| [Mapster.Tool](xref:Mapster.Tools.MapsterTool.Overview) | `dotnet mapster` | generate DTOs and mapping codes on build | +| [TextTemplate](xref:Mapster.Tools.TextTemplate) | `t4` | generate mapping codes using t4 | diff --git a/docs/articles/configuration/Config-for-nested-mapping.md b/docs/articles/configuration/Config-for-nested-mapping.md index 7aefb5eb..9416b503 100644 --- a/docs/articles/configuration/Config-for-nested-mapping.md +++ b/docs/articles/configuration/Config-for-nested-mapping.md @@ -1,5 +1,6 @@ --- uid: Mapster.Configuration.NestedMapping +title: "Configuration - Config for nested mapping" --- For example if you have parent and child classes. @@ -11,11 +12,13 @@ class ParentPoco public List Children { get; set; } public string Name { get; set; } } + class ChildPoco { public string Id { get; set; } public List GrandChildren { get; set; } } + class GrandChildPoco { public string Id { get; set; } @@ -29,7 +32,7 @@ TypeAdapterConfig.NewConfig() .PreserveReference(true); ``` -By default, children types will not get effect from `PreserveReference`. +By default, children types will not get effect from `PreserveReference`. To do so, you must specify all type pairs inside `ParentPoco`. @@ -48,7 +51,7 @@ Or you can set `PreserveReference` in global setting. TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true); ``` -### Fork +## Fork You can use `Fork` command to define config that applies only specified mapping down to nested mapping without polluting global setting. @@ -59,11 +62,11 @@ TypeAdapterConfig.NewConfig() **Ignore if string is null or empty** -Another example, Mapster only can ignore null value ([IgnoreNullValues](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge#copy-vs-merge)), however, you use `Fork` to ignore null or empty. +Another example, Mapster only can ignore null value ([IgnoreNullValues](xref:Mapster.Settings.ShallowMerge#copy-vs-merge)), however, you use `Fork` to ignore null or empty. ```csharp TypeAdapterConfig.NewConfig() .Fork(config => config.ForType() .MapToTargetWith((src, dest) => string.IsNullOrEmpty(src) ? dest : src) ); -``` \ No newline at end of file +``` diff --git a/docs/articles/configuration/Config-inheritance.md b/docs/articles/configuration/Config-inheritance.md index f5678f06..edcc93f4 100644 --- a/docs/articles/configuration/Config-inheritance.md +++ b/docs/articles/configuration/Config-inheritance.md @@ -1,8 +1,9 @@ --- uid: Mapster.Configuration.Inheritance +title: "Configuration - Inheritance" --- -### Implicit inheritance +## Implicit inheritance Type mappings will automatically inherit for source types. Ie. if you set up following config. @@ -14,7 +15,7 @@ TypeAdapterConfig.NewConfig() A derived type of `SimplePoco` will automatically apply the base mapping config. ```csharp -var dest = TypeAdapter.Adapt(src); +var dest = TypeAdapter.Adapt(src); //dest.Name = src.Name + "_Suffix" ``` @@ -30,7 +31,7 @@ And by default, Mapster will not inherit destination type mappings. You can turn TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true; ``` -### Explicit inheritance +## Explicit inheritance You can copy setting from based type explicitly. @@ -39,7 +40,7 @@ TypeAdapterConfig.NewConfig() .Inherits(); ``` -### Include derived types +## Include derived types You can also include derived type to the based type declaration. For example: diff --git a/docs/articles/configuration/Config-instance.md b/docs/articles/configuration/Config-instance.md index ba131e4a..c82a1860 100644 --- a/docs/articles/configuration/Config-instance.md +++ b/docs/articles/configuration/Config-instance.md @@ -1,8 +1,9 @@ --- uid: Mapster.Configuration.Instance +title: "Configuration - Config Instance" --- -### Config instance +## Creating `Config` Instance with `TypeAdapterConfig` You may wish to have different settings in different scenarios. If you would not like to apply setting at a static level, Mapster also provides setting instance configurations. @@ -12,6 +13,8 @@ var config = new TypeAdapterConfig(); config.Default.Ignore("Id"); ``` +### `ForType` Configuration Methods + For instance configurations, you can use the same `NewConfig` and `ForType` methods that are used at the global level with the same behavior: `NewConfig` drops any existing configuration and `ForType` creates or enhances a configuration. @@ -25,36 +28,40 @@ config.ForType() src => string.Format("{0} {1}", src.FirstName, src.LastName)); ``` +### `Adapt` Configuration Methods + You can apply a specific config instance by passing it to the `Adapt` method. (NOTE: please reuse your config instance to prevent recompilation) ```csharp var result = src.Adapt(config); ``` -Or to an Mapper instance. +### `Map` Configuration Methods + +Or to a Mapper instance. ```csharp var mapper = new Mapper(config); var result = mapper.Map(src); ``` -### Clone +## Cloning `Config` Instance If you would like to create configuration instance from existing configuration, you can use `Clone` method. For example, if you would like to clone from global setting. ```csharp var newConfig = TypeAdapterConfig.GlobalSettings.Clone(); ``` - + Or clone from existing configuration instance ```csharp var newConfig = oldConfig.Clone(); ``` -### Fork +### `Fork` Configuration -`Fork` is similar to `Clone`, but `Fork` will allow you to keep configuration and mapping in the same location. See (https://github.com/MapsterMapper/Mapster/wiki/Config-location) for more info. +`Fork` is similar to `Clone`, but `Fork` will allow you to keep configuration and mapping in the same location. See [Config Location](xref:Mapster.Configuration.Location) for more info. ```csharp var forked = mainConfig.Fork(config => diff --git a/docs/articles/configuration/Config-location.md b/docs/articles/configuration/Config-location.md index 67bf1b48..4ac27893 100644 --- a/docs/articles/configuration/Config-location.md +++ b/docs/articles/configuration/Config-location.md @@ -1,8 +1,9 @@ --- uid: Mapster.Configuration.Location +title: "Configuration - Location" --- -### Entry point +## Entry point for configuration Configuration should be set only once and reuse for mapping. Therefore, we should not keep configuration and mapping in the same location. For example: @@ -14,7 +15,7 @@ config.ForType().Ignore("Id"); //<--- Exception occurred here, becaus var dto2 = poco2.Adapt(config); ``` -Therefore, you should separate configuration and mapping. Configuration should keep in entry point such as `Main` function or `Global.asax.cs` or `Startup.cs`. +Therefore, you should separate configuration and mapping. Configuration should keep in entry point such as `Main` function or `Global.asax.cs` or `Program.cs` / `Startup.cs`. ```csharp // Application_Start in Global.asax.cs @@ -32,13 +33,12 @@ var dto2 = poco2.Adapt(config); A potential problem with separating configuration and mapping is that the code will be separated into 2 locations. You might remove or alter mapping, and you can forget to update the configuration. `Fork` method allow you to keep config and mapping inline. ```csharp -var dto = poco.Adapt( - config.Fork(forked => forked.ForType().Ignore("Id")); +var dto = poco.Adapt(config.Fork(forked => forked.ForType().Ignore("Id")); ``` Don't worry about performance, forked config will be compiled only once. When mapping occurs for the second time, `Fork` function will return config from cache. -##### Using Fork in generic class or method +#### Using Fork in generic class or method `Fork` method uses filename and line number and the key. But if you use `Fork` method inside generic class or method, you must specify your own key (with all type names) to prevent `Fork` to return invalid config from different type arguments. @@ -54,8 +54,8 @@ IQueryable GetItems() ### In separated assemblies -It's relatively common to have mapping configurations spread across a number of different assemblies. -Perhaps your domain assembly has some rules to map to domain objects and your web api has some specific rules to map to your api contracts. +It's relatively common to have mapping configurations spread across a number of different assemblies. +Perhaps your domain assembly has some rules to map to domain objects and your web api has some specific rules to map to your api contracts. #### Scan method @@ -66,13 +66,13 @@ Assembly scanning is simple, just create any number of `IRegister` implementatio ```csharp public class MyRegister : IRegister { - public void Register(TypeAdapterConfig config) - { - config.NewConfig(); + public void Register(TypeAdapterConfig config) + { + config.NewConfig(); - //OR to create or enhance an existing configuration - config.ForType(); - } + //OR to create or enhance an existing configuration + config.ForType(); + } } ``` @@ -111,7 +111,7 @@ You can also set config together with your POCO classes. For example: ```csharp [AdaptTo(typeof(StudentDto), PreserveReference = true)] -public class Student { +public class Student { ... } -``` \ No newline at end of file +``` diff --git a/docs/articles/configuration/Config-validation-&-compilation.md b/docs/articles/configuration/Config-validation-and-compilation.md similarity index 91% rename from docs/articles/configuration/Config-validation-&-compilation.md rename to docs/articles/configuration/Config-validation-and-compilation.md index 6f1b73d7..f3a06210 100644 --- a/docs/articles/configuration/Config-validation-&-compilation.md +++ b/docs/articles/configuration/Config-validation-and-compilation.md @@ -1,10 +1,12 @@ --- uid: Mapster.Configuration.ValidationAndCompilation +title: "Configuration - Validation and Compilation" --- To validate your mapping in unit tests and in order to help with "Fail Fast" situations, the following strict mapping modes have been added. -### Explicit Mapping +## Explicit Mapping + Forcing all classes to be explicitly mapped: ```csharp @@ -14,7 +16,8 @@ TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; TypeAdapterConfig.NewConfig(); ``` -### Checking Destination Member +## Checking Destination Member + Forcing all destination properties to have a corresponding source member or explicit mapping/ignore: ```csharp @@ -22,7 +25,8 @@ Forcing all destination properties to have a corresponding source member or expl TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; ``` -### Validating Mappings +## Validating Mappings + Both a specific TypeAdapterConfig or all current configurations can be validated. In addition, if Explicit Mappings (above) are enabled, it will also include errors for classes that are not registered at all with the mapper. ```csharp @@ -36,7 +40,8 @@ TypeAdapterConfig.NewConfig(); TypeAdapterConfig.GlobalSettings.Compile(); ``` -### Config Compilation +## Config Compilation + Mapster will automatically compile mapping for first time usage. ```csharp @@ -56,4 +61,4 @@ config.Compile(); Calling `Compile` method on start up helps you validate mapping and detect problem on start-up time, not on run-time. -NOTE: After compile, when you change setting in config, it will generate errors. Therefore, make sure you finish configuration before calling `Compile`. \ No newline at end of file +NOTE: After compile, when you change setting in config, it will generate errors. Therefore, make sure you finish configuration before calling `Compile`. diff --git a/docs/articles/configuration/Configuration.md b/docs/articles/configuration/Configuration.md index e5aa9804..3fb21f0a 100644 --- a/docs/articles/configuration/Configuration.md +++ b/docs/articles/configuration/Configuration.md @@ -1,8 +1,9 @@ --- uid: Mapster.Configuration.Overview +title: "Configuration - Overview" --- -### Setting per type pair +## Setting per type pair You can easily create settings for a type mapping by using: `TypeAdapterConfig.NewConfig()`. When `NewConfig` is called, any previous configuration for this particular TSource => TDestination mapping is dropped. @@ -28,7 +29,7 @@ TypeAdapterConfig `ForType` differs in that it will create a new mapping if one doesn't exist, but if the specified TSource => TDestination mapping does already exist, it will enhance the existing mapping instead of dropping and replacing it. -### Global setting +## Global setting Use global settings to apply policies to all mappings. @@ -42,7 +43,7 @@ Then for individual type mappings, you can easily override the global setting(s) TypeAdapterConfig.NewConfig().PreserveReference(false); ``` -### Rule based settings +## Rule based settings To set the setting at a more granular level. You can use the `When` method in global settings. In the example below, when any source type and destination type are the same, we will not the copy the `Id` property. @@ -59,7 +60,7 @@ TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == .IgnoreAttribute(typeof(NotMapAttribute)); ``` -### Destination type only +## Destination type only A setting can also be created without knowing the source type, by using `ForDestinationType`. For example, you can do `AfterMapping` setting to validate after mapping. @@ -70,7 +71,7 @@ TypeAdapterConfig.GlobalSettings.ForDestinationType() NOTE: `ForDestinationType` above will always apply to all types assignable to `IValidator`. If destination class implements `IValidator`, it will also apply the `AfterMapping` config. -### Open generics +## Open generics If the mapping type is generic, you can create a setting by passing generic type definition to `ForType`. diff --git a/docs/articles/configuration/toc.yml b/docs/articles/configuration/toc.yml index f78c20f5..e4718218 100644 --- a/docs/articles/configuration/toc.yml +++ b/docs/articles/configuration/toc.yml @@ -1,12 +1,12 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json # Side navigation for Configuration articles -- name: Configuration +- name: Configuration Overview uid: Mapster.Configuration.Overview href: Configuration.md - name: Config inheritance uid: Mapster.Configuration.Inheritance href: Config-inheritance.md -- name: Config instance +- name: Config Instance uid: Mapster.Configuration.Instance href: Config-instance.md - name: Config location diff --git a/docs/articles/mapping/Basic-usages.md b/docs/articles/mapping/Basic-usages.md index 246b7ca1..21d0fd12 100644 --- a/docs/articles/mapping/Basic-usages.md +++ b/docs/articles/mapping/Basic-usages.md @@ -1,8 +1,10 @@ --- uid: Mapster.Mapping.BasicUsages +title: "Mapping - Basic Usages" --- -### Mapping to a new object +## Mapping to a new object + Mapster creates the destination object and maps values to it. ```csharp @@ -17,11 +19,11 @@ You make the object, Mapster maps to the object. sourceObject.Adapt(destObject); ``` -### Queryable Extensions +## Queryable Extensions Mapster also provides extensions to map queryables. -> [!IMPORTANT] +> [!IMPORTANT] > Avoid calling ProjectToType() before materializing queries from Entity Framework. This is known to cause issues. Instead, call ToList() or ToListAsync() before calling ProjectToType. ```csharp @@ -39,4 +41,4 @@ using (MyDbContext context = new MyDbContext()) }) .ToList(); } -``` \ No newline at end of file +``` diff --git a/docs/articles/mapping/Data-types.md b/docs/articles/mapping/Data-types.md index f21892f0..f4951ed4 100644 --- a/docs/articles/mapping/Data-types.md +++ b/docs/articles/mapping/Data-types.md @@ -1,55 +1,68 @@ --- uid: Mapster.Mapping.DataTypes +title: "Mapping - Data Types" --- -### Primitives +## Primitives Converting between primitive types (ie. int, bool, double, decimal) is supported, including when those types are nullable. For all other types, if you can cast types in c#, you can also cast in Mapster. + ```csharp decimal i = 123.Adapt(); //equal to (decimal)123; ``` -### Enums + +## Enums Mapster maps enums to numerics automatically, but it also maps strings to and from enums automatically in a fast manner. The default Enum.ToString() in .NET is quite slow. The implementation in Mapster is double the speed. Likewise, a fast conversion from strings to enums is also included. If the string is null or empty, the enum will initialize to the first enum value. In Mapster, flagged enums are also supported. + ```csharp -var e = "Read, Write, Delete".Adapt(); +var e = "Read, Write, Delete".Adapt(); //FileShare.Read | FileShare.Write | FileShare.Delete ``` + For enum to enum with different type, by default, Mapster will map enum by value. You can override to map enum by name by: + ```csharp TypeAdapterConfig.GlobalSettings.Default .EnumMappingStrategy(EnumMappingStrategy.ByName); ``` -### Strings + +## Strings When Mapster maps other types to string, Mapster will use `ToString` method. And whenever Mapster maps string to the other types, Mapster will use `Parse` method. + ```csharp var s = 123.Adapt(); //equal to 123.ToString(); var i = "123".Adapt(); //equal to int.Parse("123"); ``` -### Collections + +## Collections This includes mapping among lists, arrays, collections, dictionary including various interfaces: `IList`, `ICollection`, `IEnumerable`, `ISet`, `IDictionary` etc... + ```csharp var list = db.Pocos.ToList(); -var target = list.Adapt>(); +var target = list.Adapt>(); ``` -### Mappable Objects -Mapster can map two different objects using the following rules +## Mappable Objects + +Mapster can map two different objects using the following rules: + - Source and destination property names are the same. Ex: `dest.Name = src.Name` - Source has get method. Ex: `dest.Name = src.GetName()` - Source property has child object which can flatten to destination. Ex: `dest.ContactName = src.Contact.Name` or `dest.Contact_Name = src.Contact.Name` Example: + ```csharp class Staff { public string Name { get; set; } - public int GetAge() { - return (DateTime.Now - this.BirthDate).TotalDays / 365.25; + public int GetAge() { + return (DateTime.Now - this.BirthDate).TotalDays / 365.25; } public Staff Supervisor { get; set; } ... @@ -61,11 +74,12 @@ struct StaffDto { public string SupervisorName { get; set; } } -var dto = staff.Adapt(); +var dto = staff.Adapt(); //dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name ``` -Mappable Object types are included: +**Mappable Object types are included:** + - POCO classes - POCO structs - POCO interfaces @@ -80,8 +94,8 @@ var dict = point.Adapt>(); dict["Y"].ShouldBe(3); ``` -Example for record types - +Example for record types: + ```csharp class Person { public string Name { get; } @@ -95,6 +109,6 @@ class Person { var src = new { Name = "Mapster", Age = 3 }; var target = src.Adapt(); -``` +``` -There are limitations to map Record type automatically. Record type must not have a setter and have only one non-empty constructor, and all parameter names must match with properties. Otherwise you need to add [`MapToConstructor`](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping#map-to-constructor) config. +There are limitations to map Record type automatically. Record type must not have a setter and have only one non-empty constructor, and all parameter names must match with properties. Otherwise you need to add [`MapToConstructor` configuration](xref:Mapster.Settings.ConstructorMapping#map-to-constructor). diff --git a/docs/articles/mapping/Mappers.md b/docs/articles/mapping/Mappers.md index d89b02d7..b3ac783a 100644 --- a/docs/articles/mapping/Mappers.md +++ b/docs/articles/mapping/Mappers.md @@ -1,8 +1,9 @@ --- uid: Mapster.Mapping.Mappers +title: "Mapping - Mappers" --- -### Extension method +## Extension method You can simply call `Adapt` method from anywhere. @@ -18,7 +19,7 @@ var dest = src.Adapt(); 2 extension methods are doing the same thing. `src.Adapt` will cast `src` to object. Therefore, if you map value type, please use `src.Adapt` to avoid boxing and unboxing. -### Mapper instance +## Mapper instance In some cases, you need an instance of a mapper (or a factory function) to pass into a DI container. Mapster has the `IMapper` and `Mapper` to fill this need: @@ -33,7 +34,7 @@ And usage `Map` method to perform mapping. var result = mapper.Map(source); ``` -### Builder +## Builder In most case `Adapt` method is enough, but sometimes we need builder to support fancy scenario. Basic example, is to pass run-time value. @@ -51,6 +52,6 @@ var dto = mapper.From(poco) .AdaptToType(); ``` -### Code generation +## Code generation -See [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) for generating your specific mapper class, rather than using the provided mappers. \ No newline at end of file +See [Mapster.Tool](xref:Mapster.Tools.MapsterTool.Overview) for generating your specific mapper class, rather than using the provided mappers. diff --git a/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md b/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md index 83abca15..1d8ecf50 100644 --- a/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md +++ b/docs/articles/mapping/Mapping-Configuration-With-IMapFrom-Interface.md @@ -1,14 +1,18 @@ --- uid: Mapster.Mapping.IMapFromInterface +title: "Mapping - IMapFrom Interface" --- Before using this feature you have to add this line: + ```csharp TypeAdapterConfig.GlobalSettings.ScanInheritedTypes(Assembly.GetExecutingAssembly()); ``` + With adding above line to your Startup.cs or Program.cs or any other way to run at startup, you can write mapping configs in the destination class that implements IMapFrom interface Example: + ```csharp public class InheritedDestinationModel : IMapFrom { @@ -26,9 +30,10 @@ public class InheritedDestinationModel : IMapFrom Even if your destination model doesn't have a specific configuration (you don't want to customize anything), you can just inherit from IMapFrom interface Example: + ```csharp public class DestinationModel : IMapFrom { public string Type { get; set; } } -``` \ No newline at end of file +``` diff --git a/docs/articles/plugins/Async.md b/docs/articles/packages/Async.md similarity index 62% rename from docs/articles/plugins/Async.md rename to docs/articles/packages/Async.md index a5489e2f..563cb65c 100644 --- a/docs/articles/plugins/Async.md +++ b/docs/articles/packages/Async.md @@ -1,16 +1,17 @@ --- -uid: Mapster.Plugins.Async +uid: Mapster.Packages.Async +title: "Packages - Mapster Async Support" --- -### Async support +This Package allows you to perform async operation for mapping. - PM> Install-Package Mapster.Async - -This plugin allows you to perform async operation for mapping +```nuget + PM> Install-Package Mapster.Async +``` -##### Setup +## Setup -Use `AfterMappingAsync` to setup async operation +Use `AfterMappingAsync` to setup async operation: ```csharp config.NewConfig() @@ -22,7 +23,7 @@ config.NewConfig() }); ``` -##### Mapping +## Mapping Then map asynchronously with `AdaptToTypeAsync`. @@ -31,8 +32,7 @@ var dto = await poco.BuildAdapter() .AdaptToTypeAsync(); ``` - -Or like this, if you use mapper instance. +Or like this, if you use mapper instance: ```csharp var dto = await _mapper.From(poco) diff --git a/docs/articles/plugins/Dependency-Injection.md b/docs/articles/packages/Dependency-Injection.md similarity index 83% rename from docs/articles/plugins/Dependency-Injection.md rename to docs/articles/packages/Dependency-Injection.md index 530283e9..358b256d 100644 --- a/docs/articles/plugins/Dependency-Injection.md +++ b/docs/articles/packages/Dependency-Injection.md @@ -1,14 +1,17 @@ --- -uid: Mapster.Plugins.DependencyInjection +uid: Mapster.Packages.DependencyInjection +title: "Packages - Dependency Injection Support" --- -### Dependency injection support +## Installation - PM> Install-Package Mapster.DependencyInjection +This package allows you to inject service into mapping configuration. -This plugin allows you to inject service into mapping configuration. +```nuget + PM> Install-Package Mapster.DependencyInjection +``` -#### Usage +## Usage On startup, register `TypeAdapterConfig`, and `ServiceMapper`. @@ -27,7 +30,7 @@ public void ConfigureServices(IServiceCollection services) NOTE: lifetime of `ServiceMapper` is up to services you would like to inject. It can be singleton, if you inject only singleton services. Or it can be transient, if any injected services is transient. -##### Mapping configuration +## Mapping configuration You can get service by `MapContext.Current.GetService<>()`, for example @@ -36,7 +39,7 @@ config.NewConfig() .Map(dest => dest.Name, src => MapContext.Current.GetService().Format(src.Name)); ``` -##### Mapping +## Mapping If you setup service injection, you need to use mapper instance to map object. diff --git a/docs/articles/plugins/Debugging.md b/docs/articles/packages/Diagnostics.md similarity index 78% rename from docs/articles/plugins/Debugging.md rename to docs/articles/packages/Diagnostics.md index 997e0e8d..919054b7 100644 --- a/docs/articles/plugins/Debugging.md +++ b/docs/articles/packages/Diagnostics.md @@ -1,14 +1,15 @@ --- -uid: Mapster.Plugins.Debugging +uid: Mapster.Packages.Diagnostics +title: "Packages - Diagnostics - Expression Debugging" --- -### Step-into debugging +This Package allows you to perform step-into debugging using Roslyn! +```nuget PM> Install-Package ExpressionDebugger +``` -This plugin allows you to perform step-into debugging using Roslyn! - -##### Usage +## Usage Then add following code on start up (or anywhere before mapping is compiled) @@ -22,9 +23,9 @@ Now in your mapping code (only in `DEBUG` mode). var dto = poco.Adapt(); //<--- you can step-into this function!! ``` -![image](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) +![step-into-debugging-screenshot](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) -##### Using internal classes or members +## Using internal classes or members `private`, `protected` and `internal` aren't allowed in debug mode. @@ -32,14 +33,16 @@ var dto = poco.Adapt(); //<--- you can step-into this fun We can also see how Mapster generates mapping logic with `ToScript` method. -``` +```csharp var script = poco.BuildAdapter() .CreateMapExpression() .ToScript(); ``` -### Visual Studio for Mac +## Specifics for Visual Studio on Mac + To step-into debugging, you might need to emit file + ```csharp var opt = new ExpressionCompilationOptions { EmitFile = true }; TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(opt); @@ -48,7 +51,8 @@ var dto = poco.Adapt(); //<-- you can step-into this func ``` ### Do not worry about performance -In `RELEASE` mode, Roslyn compiler is actually faster than default dynamic compilation by 2x. + +In `RELEASE` mode, Roslyn compiler is actually faster than default dynamic compilation by 2x. Here is the result: | Method | Mean | StdDev | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | diff --git a/docs/articles/plugins/EF-6-&-EF-Core.md b/docs/articles/packages/EF-6-and-EF-Core.md similarity index 55% rename from docs/articles/plugins/EF-6-&-EF-Core.md rename to docs/articles/packages/EF-6-and-EF-Core.md index 333ae7cd..f255d6e9 100644 --- a/docs/articles/plugins/EF-6-&-EF-Core.md +++ b/docs/articles/packages/EF-6-and-EF-Core.md @@ -1,26 +1,35 @@ --- -uid: Mapster.Plugins.EntityFramework +uid: Mapster.Packages.EntityFramework +title: "Packages - EF 6 and EF Core Support" --- -### EF 6 & EF Core support +## [EntityFramework 6 support](#tab/ef6) -For EF 6 +To install the package for EF 6: +```nuget PM> Install-Package Mapster.EF6 +``` + +## [EntityFramework Core support](#tab/efcore) -For EF Core +To install the package for EF Core: +```nuget PM> Install-Package Mapster.EFCore +``` -In EF, objects are tracked, when you copy data from dto to entity containing navigation properties, this plugin will help finding entity object in navigation properties automatically. +In EF, objects are tracked, when you copy data from dto to entity containing navigation properties, this package will help finding entity object in navigation properties automatically. + +--- + +## Compatibility -#### Mapster.EFCore & EFCore compatiability - use Mapster.EFCore version 5.x for EFCore 5.x - use Mapster.EFCore version 3.x for EFCore 3.x - use Mapster.EFCore version 1.x for EFCore 2.x - -#### Usage +## Usage Use `EntityFromContext` method to define data context. @@ -41,11 +50,12 @@ _mapper.From(dto) .AdaptTo(poco); ``` -#### EF Core ProjectToType -`Mapster.EFCore` also allows `ProjectToType` from mapper instance. +### EF Core `ProjectToType` + +`Mapster.EFCore` also allows you to perform projection from `IQueryable` source via mapper instance and `ProjectToType` directly to your DTO type. ```csharp var query = db.Customers.Where(...); _mapper.From(query) .ProjectToType(); -``` \ No newline at end of file +``` diff --git a/docs/articles/plugins/FastExpressionCompiler.md b/docs/articles/packages/FastExpressionCompiler.md similarity index 84% rename from docs/articles/plugins/FastExpressionCompiler.md rename to docs/articles/packages/FastExpressionCompiler.md index 01b427a6..a20df828 100644 --- a/docs/articles/plugins/FastExpressionCompiler.md +++ b/docs/articles/packages/FastExpressionCompiler.md @@ -1,12 +1,17 @@ --- -uid: Mapster.Plugins.FastExpressionCompiler +uid: Mapster.Packages.FastExpressionCompiler +title: "Packages - Fast Expression Compiler Support" --- Need more speed? Let's compile with [FastExpressionCompiler](https://github.com/dadhi/FastExpressionCompiler). -Getting the package +## Installation +Getting the package: + +```nuget PM> Install-Package FastExpressionCompiler +``` Then add following code on start up diff --git a/docs/articles/plugins/Immutable.md b/docs/articles/packages/Immutable.md similarity index 74% rename from docs/articles/plugins/Immutable.md rename to docs/articles/packages/Immutable.md index 1cb68ff0..3b8bd5ef 100644 --- a/docs/articles/plugins/Immutable.md +++ b/docs/articles/packages/Immutable.md @@ -1,14 +1,16 @@ --- -uid: Mapster.Plugins.Immutable +uid: Mapster.Packages.Immutable +title: "Packages - Immutable Support" --- -# Mapster.Immutable -Immutable collection supports +This Package enables Immutable collection support in Mapster. -### Install +### Installation +```nuget PM> Install-Package Mapster.Immutable - +``` + ### Usage Call `EnableImmutableMapping` from your `TypeAdapterConfig` to enable Immutable collection. @@ -17,13 +19,14 @@ Call `EnableImmutableMapping` from your `TypeAdapterConfig` to enable Immutable TypeAdapterConfig.GlobalSettings.EnableImmutableMapping(); ``` -or +or: ```csharp config.EnableImmutableMapping(); ``` - -This will allow mapping to + +This will allow mapping to: + - `IImmutableDictionary<,>` - `IImmutableList<>` - `IImmutableQueue<>` diff --git a/docs/articles/plugins/Json.net.md b/docs/articles/packages/Json.net.md similarity index 66% rename from docs/articles/plugins/Json.net.md rename to docs/articles/packages/Json.net.md index f4bfc632..2cc8d3b3 100644 --- a/docs/articles/plugins/Json.net.md +++ b/docs/articles/packages/Json.net.md @@ -1,15 +1,17 @@ --- -uid: Mapster.Plugins.JsonNet +uid: Mapster.Packages.JsonNet +title: "Packages - Json.net Support" --- -# Mapster.JsonNet -Json.net conversion supports +The `Json.net` Package adds conversion supports for Json.Net types. -### Install +## Installation +```nuget PM> Install-Package Mapster.JsonNet - -### Usage +``` + +## Usage Call `EnableJsonMapping` from your `TypeAdapterConfig` to enable Json.Net mapping. @@ -17,13 +19,13 @@ Call `EnableJsonMapping` from your `TypeAdapterConfig` to enable Json.Net mappin TypeAdapterConfig.GlobalSettings.EnableJsonMapping(); ``` -or +or: ```csharp config.EnableJsonMapping(); ``` - -This will allow + +This will allow: - Mapping between Json.Net types (`JToken`, `JArray`, `JObject`) from/to POCO types - Serialize and deserialize Json.Net types from/to string diff --git a/docs/articles/packages/toc.yml b/docs/articles/packages/toc.yml new file mode 100644 index 00000000..ceedfea3 --- /dev/null +++ b/docs/articles/packages/toc.yml @@ -0,0 +1,23 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +# NOTE: Use quotes for names with special characters like "&" and don't use them in filenames +- name: Async Support + uid: Mapster.Packages.Async + href: Async.md +- name: "Debugging & Diagnostics Support" + uid: Mapster.Packages.Diagnostics + href: Debugging.md +- name: Dependency Injection + uid: Mapster.Packages.DependencyInjection + href: Dependency-Injection.md +- name: "EF 6 and EF Core Support" + uid: Mapster.Packages.EntityFramework + href: EF-6-and-EF-Core.md +- name: FastExpressionCompiler + uid: Mapster.Packages.FastExpressionCompiler + href: FastExpressionCompiler.md +- name: Immutable + uid: Mapster.Packages.Immutable + href: Immutable.md +- name: "Json.net Support" + uid: Mapster.Packages.JsonNet + href: Json.net.md diff --git a/docs/articles/plugins/toc.yml b/docs/articles/plugins/toc.yml deleted file mode 100644 index 2f311f2b..00000000 --- a/docs/articles/plugins/toc.yml +++ /dev/null @@ -1,22 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json -- name: Async Support - uid: Mapster.Plugins.Async - href: Async.md -- name: Debugging - uid: Mapster.Plugins.Debugging - href: Debugging.md -- name: Dependency Injection - uid: Mapster.Plugins.DependencyInjection - href: Dependency-Injection.md -- name: EF 6 & EF Core - uid: Mapster.Plugins.EntityFramework - href: EF-6-&-EF-Core.md -- name: FastExpressionCompiler - uid: Mapster.Plugins.FastExpressionCompiler - href: FastExpressionCompiler.md -- name: Immutable - uid: Mapster.Plugins.Immutable - href: Immutable.md -- name: Json.net - uid: Mapster.Plugins.JsonNet - href: Json.net.md diff --git a/docs/articles/settings/Before-after-mapping.md b/docs/articles/settings/Before-after-mapping.md index 5a6e7b22..cff135a2 100644 --- a/docs/articles/settings/Before-after-mapping.md +++ b/docs/articles/settings/Before-after-mapping.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.BeforeAfterMapping +title: "Settings - Before and After mapping" --- -### Before mapping action +## Before mapping action You can perform actions before mapping started by using `BeforeMapping` method. @@ -11,7 +12,7 @@ TypeAdapterConfig.ForType() .BeforeMapping((src, result) => result.Initialize()); ``` -### After mapping action +## After mapping action You can perform actions after each mapping by using `AfterMapping` method. For instance, you might would like to validate object after each mapping. @@ -26,11 +27,12 @@ Or you can set for all mappings to types which implemented a specific interface TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMapping(result => result.Validate()); ``` -### Before & after mapping in code generation + +## Code generation `BeforeMapping` and `AfterMapping` accept action which allowed you to pass multiple statements. In code generation, you might need to pass expression instead of action using `BeforeMappingInline` and `AfterMappingInline`, expression can be translated into code, but action cannot. -#### Single line statement +### Single line statement For single line statement, you can directly change from `BeforeMapping` and `AfterMapping` to `BeforeMappingInline` and `AfterMappingInline`. @@ -39,7 +41,7 @@ TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMappingInline(result => result.Validate()); ``` -#### Multiple statements +### Multiple statements For multiple statements, you need to declare a method for actions. @@ -58,7 +60,7 @@ TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMappingInline(result => PocoToDtoMapper.Validate(result)); ``` -### Before and After mapping have overloads with `destination` parameter +## Overloads with `destination` parameter You can use `BeforeMapping` with `destination` to construct final (`result`) object. @@ -104,4 +106,4 @@ var result = poco.Adapt(oldDto); result.ShouldNotBeSameAs(oldDto); result.Id.ShouldBe(poco.Id); result.Name.ShouldBe(poco.Name + "zzzxxx"); -``` \ No newline at end of file +``` diff --git a/docs/articles/settings/Constructor-mapping.md b/docs/articles/settings/Constructor-mapping.md index 9eece89f..7cac272f 100644 --- a/docs/articles/settings/Constructor-mapping.md +++ b/docs/articles/settings/Constructor-mapping.md @@ -1,13 +1,15 @@ --- uid: Mapster.Settings.ConstructorMapping +title: "Settings - Constructor mapping" --- -### Custom Destination Object Creation +## Custom Destination Object Creation You can provide a function call to create your destination objects instead of using the default object creation (which expects an empty constructor). To do so, use the `ConstructUsing` method when configuring. This method expects a function that will provide the destination instance. You can call your own constructor, a factory method, or anything else that provides an object of the expected type. + ```csharp //Example using a non-default constructor TypeAdapterConfig.NewConfig() @@ -47,6 +49,7 @@ class Dto { } } ``` + ```csharp TypeAdapterConfig.NewConfig() .MapToConstructor(true) @@ -66,6 +69,7 @@ class Dto { public Dto(int foo, int bar, int baz) { ... } } ``` + Or you can also explicitly pass ConstructorInfo to the method. ```csharp diff --git a/docs/articles/settings/Custom-conversion-logic.md b/docs/articles/settings/Custom-conversion-logic.md index e7caa544..0edaaa23 100644 --- a/docs/articles/settings/Custom-conversion-logic.md +++ b/docs/articles/settings/Custom-conversion-logic.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.CustomConversionLogic +title: "Settings - Custom conversion logic" --- -### Custom type conversion +## Custom type conversion In some cases, you may want to have complete control over how an object is mapped. You can register specific transformations using the `MapWith` method. @@ -27,7 +28,7 @@ TypeAdapterConfig.NewConfig() .MapWith(poco => poco.ToDto(), applySettings: true); ``` -### Custom mapping data to existing object +## Custom mapping data to existing object You can control mapping to existing object logic by `MapToTargetWith`. For example, you can copy data to existing array. diff --git a/docs/articles/settings/Object-references.md b/docs/articles/settings/Object-references.md index f9659f9b..86d2d955 100644 --- a/docs/articles/settings/Object-references.md +++ b/docs/articles/settings/Object-references.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.ObjectReferences +title: "Settings - Object references" --- -### Preserve reference (preventing circular reference stackoverflow) +## Preserve reference (preventing circular reference stackoverflow) When mapping objects with circular references, a stackoverflow exception will result. This is because Mapster will get stuck in a loop trying to recursively map the circular reference. If you would like to map circular references or preserve references (such as 2 properties pointing to the same object), you can do it by setting `PreserveReference` to `true` @@ -12,11 +13,11 @@ TypeAdapterConfig .PreserveReference(true); ``` -NOTE: in Mapster setting is per type pair, not per hierarchy (see https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-classes). Therefore, you need to apply config to all type pairs. +NOTE: in Mapster setting is per type pair, not per hierarchy (see [**Configuration for nested Classes**](xref:Mapster.Configuration.NestedMapping)). Therefore, you need to apply config to all type pairs. NOTE: you might need to use `MaxDepth`. `PreserveReference` doesn't support EF Query (`ProjectTo`) -### MaxDepth +## MaxDepth Rather than `PreserveReference`, you could also try `MaxDepth`. `MaxDepth` will map until it reaches the defined limit. Unlike `PreserveReference`, `MaxDepth` also works with queryable projection. @@ -30,7 +31,7 @@ NOTE 1: `MaxDepth` starts with 1, means you will copy only primitives. POCO clas NOTE 2: even `MaxDepth` has no maximum value, you shouldn't input large number. Each depth will generate a mapping logic, otherwise it will consume a lot of memory. -### Shallow copy +## Shallow copy By default, Mapster will recursively map nested objects. You can do shallow copying by setting `ShallowCopyForSameType` to `true`. @@ -40,11 +41,11 @@ TypeAdapterConfig .ShallowCopyForSameType(true); ``` -### Mapping very large objects +## Mapping very large objects For performance optimization, Mapster tried to inline class mapping. This process will takes time if your models are complex. -![](https://cloud.githubusercontent.com/assets/21364231/25666644/ce38c8c0-3029-11e7-8793-8a51c519c2a0.png) +![inline-class-mapping-diagram](https://cloud.githubusercontent.com/assets/21364231/25666644/ce38c8c0-3029-11e7-8793-8a51c519c2a0.png) You can skip inlining process by: diff --git a/docs/articles/settings/Setting-values.md b/docs/articles/settings/Setting-values.md index 3eb55ac1..a08dc9df 100644 --- a/docs/articles/settings/Setting-values.md +++ b/docs/articles/settings/Setting-values.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.SettingValues +title: "Settings - Setting values" --- -### Computed value +## Computed value You can use `Map` method to specify logic to compute value. For example, compute full name from first name and last name. @@ -11,28 +12,31 @@ TypeAdapterConfig.NewConfig() .Map(dest => dest.FullName, src => src.FirstName + " " + src.LastName); ``` -### Transform value +## Transform value While `Map` method specify logic for single property, `AddDestinationTransform` allows transforms for all items of a type, such as trimming all strings. But really any operation can be performed on the destination value before assignment. -**Trim string** +### Trim string + ```csharp TypeAdapterConfig.NewConfig() .AddDestinationTransform((string x) => x.Trim()); ``` -**Null replacement** +### Null replacement + ```csharp TypeAdapterConfig.NewConfig() .AddDestinationTransform((string x) => x ?? ""); ``` -**Return empty collection if null** +### Return empty collection if null + ```csharp config.Default.AddDestinationTransform(DestinationTransform.EmptyCollectionIfNull); ``` -### Passing run-time value +## Passing run-time value In some cases, you might would like to pass runtime values (ie, current user). On configuration, we can receive run-time value by `MapContext.Current.Parameters`. @@ -49,4 +53,3 @@ var dto = poco.BuildAdapter() .AddParameters("user", this.User.Identity.Name) .AdaptToType(); ``` - diff --git a/docs/articles/settings/Shallow-merge.md b/docs/articles/settings/Shallow-merge.md index fd99b7c8..ba101264 100644 --- a/docs/articles/settings/Shallow-merge.md +++ b/docs/articles/settings/Shallow-merge.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.ShallowMerge +title: "Settings - Shallow merge" --- -### Deep copy vs shallow copy +## Deep copy vs. shallow copy By default, Mapster will recursively map nested objects (deep copy). You can do shallow copying by setting `ShallowCopyForSameType` to `true`. @@ -11,7 +12,8 @@ TypeAdapterConfig .NewConfig() .ShallowCopyForSameType(true); ``` -### Copy vs Merge + +## Copy vs. Merge By default, Mapster will map all properties, even source properties containing null values. You can copy only properties that have values (merge) by using `IgnoreNullValues` method. diff --git a/docs/articles/settings/custom/Custom-mapping.md b/docs/articles/settings/custom/Custom-mapping.md index b0e5d6dc..b687c081 100644 --- a/docs/articles/settings/custom/Custom-mapping.md +++ b/docs/articles/settings/custom/Custom-mapping.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.Custom.Mapping +title: "Settings - Custom Mapping" --- -### Custom member mapping +## Custom member mapping You can customize how Mapster maps values to a property. @@ -35,7 +36,7 @@ TypeAdapterConfig .Map(dest => dest.FullName, src => "Mr. " + src.FullName); ``` -NOTE: if you would like to skip mapping, when condition is met, you can use `IgnoreIf` (https://github.com/MapsterMapper/Mapster/wiki/Ignoring-members#ignore-conditionally). +NOTE: if you would like to skip mapping, when condition is met, you can use `IgnoreIf` (xref:Mapster.Settings.Custom.IgnoringMembers#ignore-conditionally). ### Mapping to non-public members @@ -47,7 +48,7 @@ TypeAdapterConfig .Map("PrivateDestName", "PrivateSrcName"); ``` -For more information about mapping non-public members, please see https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members. +For more information about mapping non-public members, please see [Mapping non-public members](xref:Mapster.Settings.Custom.NonPublicMembers). ### Deep destination property diff --git a/docs/articles/settings/custom/Ignoring-members.md b/docs/articles/settings/custom/Ignoring-members.md index 70078036..a958ad89 100644 --- a/docs/articles/settings/custom/Ignoring-members.md +++ b/docs/articles/settings/custom/Ignoring-members.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.Custom.IgnoringMembers +title: "Settings - Ignoring members" --- -### Ignore +## Ignore Extension Method Mapster will automatically map properties with the same names. You can ignore members by using the `Ignore` method. @@ -12,9 +13,9 @@ TypeAdapterConfig .Ignore(dest => dest.Id); ``` -### Rule based ignore +## Rule based ignore with `IgnoreMember` -You can ignore based on member information by `IgnoreMember` command. Please see https://github.com/MapsterMapper/Mapster/wiki/Rule-based-member-mapping for more info. +You can ignore based on member information by `IgnoreMember` command. Please see [Rule-based-member-mapping](xref:Mapster.Settings.Custom.RuleBasedMapping) for more info. ```csharp TypeAdapterConfig.GlobalSettings.Default @@ -33,9 +34,9 @@ TypeAdapterConfig .IgnoreNonMapped(true); ``` -### Ignore by attribute +## Using the `AdaptIgnore` attribute -You can ignore member by decorate with `[AdaptIgnore]`, and you can ignore custom attributes by `IgnoreAttribute` command. Please see https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes for more info. +You can ignore member by decorate with `[AdaptIgnore]`, and you can ignore custom attributes by `IgnoreAttribute` command. Please see [Setting by attributes](xref:Mapster.Settings.Custom.Attributes) for more info. ```csharp public class Product { @@ -47,7 +48,7 @@ public class Product { } ``` -### Ignore conditionally +### Ignore conditionally with `IgnoreIf` Extension Method You can ignore members conditionally, with condition based on source or target. When the condition is met, mapping of the property will be skipped altogether. This is the difference from custom `Map` with condition, where destination is set to `null` when condition is met. @@ -57,7 +58,7 @@ TypeAdapterConfig .IgnoreIf((src, dest) => !string.IsNullOrEmpty(dest.Name), dest => dest.Name); ``` -### IgnoreNullValues +### `IgnoreNullValues` Extension Method You might would like to merge from input object, By default, Mapster will map all properties, even source properties containing null values. You can copy only properties that have values by using `IgnoreNullValues` method. diff --git a/docs/articles/settings/custom/Mapping-non-public-members.md b/docs/articles/settings/custom/Mapping-non-public-members.md index 5b82ecdf..21864fa5 100644 --- a/docs/articles/settings/custom/Mapping-non-public-members.md +++ b/docs/articles/settings/custom/Mapping-non-public-members.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.Custom.NonPublicMembers +title: "Settings - Mapping non-public members" --- -### EnableNonPublicMembers +## `EnableNonPublicMembers` Extension Method This will allow Mapster to set to all non-public members. @@ -14,12 +15,12 @@ TypeAdapterConfig.NewConfig().EnableNonPublicMembers(true); TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMembers(true); ``` -### AdaptMember attribute +## `AdaptMember` attribute You can also map non-public members with `AdaptMember` attribute. ```csharp -public class Product +public class Product { [AdaptMember] private string HiddenId { get; set; } @@ -27,9 +28,9 @@ public class Product } ``` -### Map +## `Map` -`Map` command can map to private member by specify name of the members. +The `Map` extension can map to private member by specify name of the members. ```csharp TypeAdapterConfig @@ -37,13 +38,13 @@ TypeAdapterConfig .Map("PrivateDestName", "PrivateSrcName"); ``` -### IncludeMember +## `IncludeMember` -With `IncludeMember`, you can select which access modifier to allow. +With the `IncludeMember` extension, you can select which access modifier to allow. ```csharp TypeAdapterConfig.GlobalSettings.Default - .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal + .IncludeMember((member, side) => member.AccessModifier == AccessModifier.Internal || member.AccessModifier == AccessModifier.ProtectedInternal); ``` diff --git a/docs/articles/settings/custom/Mapping-readonly-prop.md b/docs/articles/settings/custom/Mapping-readonly-prop.md index a6fee542..c027b911 100644 --- a/docs/articles/settings/custom/Mapping-readonly-prop.md +++ b/docs/articles/settings/custom/Mapping-readonly-prop.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.Custom.ReadonlyProperty +title: "Settings - Mapping readonly properties" --- -### Non public setter +## Non public setter Mapster can map to non public setter automatically. @@ -13,9 +14,9 @@ public class Order { } ``` -### Using UseDestinationValue attribute +## Using `UseDestinationValue` attribute -You can make your type pure readonly and annotate with [UseDestinationValue]. +You can make your type pure readonly and annotate with `[UseDestinationValue]`. ```csharp public class Order { @@ -26,7 +27,7 @@ public class Order { } ``` -### Convention based setup +## Convention based setup using `UseDestinationValue` Extension Method Or you can apply without annotate each type, for example, if you would like all readonly `ICollection<>` to use destination value. diff --git a/docs/articles/settings/custom/Naming-convention.md b/docs/articles/settings/custom/Naming-convention.md index 2d37f9d2..f84e64e7 100644 --- a/docs/articles/settings/custom/Naming-convention.md +++ b/docs/articles/settings/custom/Naming-convention.md @@ -1,10 +1,11 @@ --- uid: Mapster.Settings.Custom.NamingConvention +title: "Settings - Naming convention" --- -### Flexible name +## Flexible name -By default, Mapster will map property with case sensitive name. You can adjust to flexible name mapping by setting `NameMatchingStrategy.Flexible` to `NameMatchingStrategy` method. This setting will allow matching between `PascalCase`, `camelCase`, `lower_case`, and `UPPER_CASE`. +By default, Mapster will map property with case sensitive name. You can adjust to flexible name mapping by setting `NameMatchingStrategy.Flexible` to `NameMatchingStrategy` method. This setting will allow matching between `PascalCase`, `camelCase`, `lower_case`, and `UPPER_CASE`. ```csharp //global @@ -14,7 +15,7 @@ TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrate TypeAdapterConfig.NewConfig().NameMatchingStrategy(NameMatchingStrategy.Flexible); ``` -### Ignore cases +## Ignore cases Flexible name could not map between `MiXcAsE` and `MixCase`, because flexible name will break `MiXcAsE` into `Mi-Xc-As-E` rather than just `Mix-Case`. In this case, we need to use `IgnoreCase` to perform case insensitive matching. @@ -22,7 +23,7 @@ Flexible name could not map between `MiXcAsE` and `MixCase`, because flexible na TypeAdapterConfig.GlobalSettings.Default.NameMatchingStrategy(NameMatchingStrategy.IgnoreCase); ``` -### Prefix & Replace +## Prefix & Replace For custom rules, you can use either `ConvertSourceMemberName` or `ConvertDestinationMemberName` up to which side you would like to convert. For example, you might would like to add `m_` to all properties. @@ -38,7 +39,7 @@ TypeAdapterConfig.NewConfig() .NameMatchingStrategy(NameMatchingStrategy.ConvertSourceMemberName(name => name.Replace("Ä", "A")); ``` -### Naming Convention with IDictionary +## Naming Convention with `IDictionary` If you would like to change case from POCO to `IDictionary` to camelCase, you can use `ToCamelCase`. Another way around, if you would like to map `IDictionary` back to POCO, you can use `FromCamelCase`. @@ -49,9 +50,10 @@ TypeAdapterConfig, Poco>.NewConfig() .NameMatchingStrategy(NameMatchingStrategy.FromCamelCase); ``` -NOTE: mapping from `IDictionary` to POCO, you can also use `Flexible` or `IgnoreCase`, but both will be slower since it will scan through dictionary entries rather than lookup. +> [!NOTE] +> Mapping from `IDictionary` to POCO, you can also use `Flexible` or `IgnoreCase`, but both will be slower since it will scan through dictionary entries rather than lookup. -### Rule based Naming +## Rule based Naming using `GetMemberName` Extension Method You can change name based on rule by `GetMemberName` method. For example, if we would like to rename property based on `JsonProperty` attribute. @@ -62,10 +64,10 @@ TypeAdapterConfig.GlobalSettings.Default .FirstOrDefault()?.PropertyName); //if return null, property will not be renamed ``` -Then in your class +Then in your class: ```csharp -public class Poco +public class Poco { [JsonProperty("code")] public string Id { get; set; } @@ -74,4 +76,4 @@ public class Poco } ``` -With above config, `Id` will be mapped to `code`. \ No newline at end of file +With above config, `Id` will be mapped to `code`. diff --git a/docs/articles/settings/custom/Rule-based-member-mapping.md b/docs/articles/settings/custom/Rule-based-member-mapping.md index 4e3b2bd5..7901dd48 100644 --- a/docs/articles/settings/custom/Rule-based-member-mapping.md +++ b/docs/articles/settings/custom/Rule-based-member-mapping.md @@ -1,5 +1,6 @@ --- uid: Mapster.Settings.Custom.RuleBasedMapping +title: "Settings - Rule-based member mapping" --- By default, Mapster will include public fields and properties, but we can change this behavior by `IncludeMember` and `IgnoreMember` method. The methods require predicate, and input types of predicate are: @@ -22,7 +23,7 @@ public enum MemberSide } ``` -### Not allow fields +## Not allow fields If you would like to allow only properties not public field to be mapped, you can check from `Info`. Possible values could be `PropertyInfo`, `FieldInfo`, or `ParameterInfo`. In this case, we will reject member of type `FieldInfo`. @@ -31,16 +32,19 @@ TypeAdapterConfig.GlobalSettings.Default .IgnoreMember((member, side) => member.Info is FieldInfo); ``` -### Allow only some list of types to be mapped +## Allow only some list of types to be mapped Suppose you are working with EF, and you would like to skip all navigation properties. Then we will allow only short list of types. -**Allow by types** +### Allow by types + ```csharp TypeAdapterConfig.GlobalSettings.Default .IgnoreMember((member, side) => !validTypes.Contains(member.Type)); ``` -**Allow by Namespaces** + +### Allow by Namespaces + ```csharp TypeAdapterConfig.GlobalSettings.Default .IgnoreMember((member, side) => !member.Type.Namespace.StartsWith("System")); @@ -56,9 +60,9 @@ If you would like to map members marked as internal, you can do it by: || member.AccessModifier == AccessModifier.ProtectedInternal); ``` -### Allow only DataMember attribute +### Allow only `DataMember` attribute -If you would like to include all members decorated with DataMember attribute, and ignore all members with no DataMember attribute, you can set up by: +If you would like to include all members decorated with `DataMember` attribute, and ignore all members with no `DataMember` attribute, you can set up by: ```csharp TypeAdapterConfig.GlobalSettings.Default @@ -67,7 +71,7 @@ TypeAdapterConfig.GlobalSettings.Default .IgnoreMember((member, side) => !member.GetCustomAttributes(true).OfType().Any()); ``` -### Turn-off non-public setters +### Turn-off non-public setters using `IgnoreMember` Extension Method Mapster always allows non-public setters. But you can override by: diff --git a/docs/articles/settings/custom/Setting-by-attributes.md b/docs/articles/settings/custom/Setting-by-attributes.md index 8928fca1..b81dcd22 100644 --- a/docs/articles/settings/custom/Setting-by-attributes.md +++ b/docs/articles/settings/custom/Setting-by-attributes.md @@ -1,8 +1,9 @@ --- uid: Mapster.Settings.Custom.Attributes +title: "Settings - Setting by attributes" --- -### AdaptIgnore attribute +## `AdaptIgnore` attribute When a property decorated with `[AdaptIgnore]`, that property will be excluded from Mapping. For example, if we would like to exclude price to be mapped. @@ -30,7 +31,7 @@ public class Product { Above example, `Price` will be ignored only when `Product` is used as source. -### Ignore custom attributes +## `IgnoreAttribute` usage You can ignore members annotated with any attributes by using the `IgnoreAttribute` method. @@ -45,8 +46,12 @@ However `IgnoreAttribute` will ignore both source and destination. If you would config.IgnoreMember((member, side) => member.HasCustomAttribute(typeof(NotMapAttribute)) && side == MemberSide.Source); ``` -### AdaptMember attribute -**Map to different name** +## `AdaptMember` attribute + +`AdaptMember` attribute allows you to customize member mapping by attributes. + +### Map to different name + With `AdaptMember` attribute, you can specify name of source or target to be mapped. For example, if we would like to map `Id` to `Code`. ```csharp @@ -57,8 +62,10 @@ public class Product { } ``` -**Map to non-public members** -You can also map non-public members with `AdaptMember` attribute. +### Map to non-public members + +By default, Mapster only map public members. You can enable mapping to non-public members by `AdaptMember` attribute: + ```csharp public class Product { [AdaptMember] @@ -78,7 +85,7 @@ TypeAdapterConfig.GlobalSettings.Default .FirstOrDefault()?.PropertyName); //if return null, property will not be renamed ``` -### Include custom attributes +### Using `IncludeAttribute` And if we would like to include non-public members decorated with `JsonProperty` attribute, we can do it by `IncludeAttribute`. @@ -87,7 +94,7 @@ TypeAdapterConfig.GlobalSettings.Default .IncludeAttribute(typeof(JsonPropertyAttribute)); ``` -### Use destination value +### Using the `UseDestinationValue` attribute You can tell Mapster to use existing property object to map data rather than create new object. diff --git a/docs/articles/settings/custom/Two-ways.md b/docs/articles/settings/custom/Two-ways.md index d270baea..cc0ec0ae 100644 --- a/docs/articles/settings/custom/Two-ways.md +++ b/docs/articles/settings/custom/Two-ways.md @@ -1,9 +1,8 @@ --- uid: Mapster.Settings.Custom.TwoWaysMapping +title: "Settings - Two-ways mapping" --- -### 2-ways mapping - If you need to map object from POCO to DTO, and map back from DTO to POCO. You can define the setting once by using `TwoWays`. ```csharp @@ -23,7 +22,7 @@ TypeAdapterConfig .Map(dto => dto.Foz, poco => poco.Baz); //<-- this map will apply both side ``` -### Unflattening +## Flattening By default, Mapster will perform flattening. @@ -39,18 +38,20 @@ struct StaffDto { } ``` -Above example, without any setup, you can map from POCO to DTO and you will get `SupervisorName` from `Supervisor.Name` (flattening). +Above example, without any setup, you can map from POCO to DTO and you will get `SupervisorName` from `Supervisor.Name`. -However, unflattening process needed to be defined. You can map to `Supervisor.Name` from `SupervisorName` by +## Using `Unflattening` -#### using `Unflattening`. +However, unflattening process needed to be defined. You can map to `Supervisor.Name` from `SupervisorName` by `Unflattening` setting. ```csharp TypeAdapterConfig.NewConfig() .Unflattening(true); ``` -#### using `TwoWays` +## Using `TwoWays` + +Or you can use `TwoWays` to define both flattening and unflattening in one setting. ```csharp TypeAdapterConfig diff --git a/docs/articles/tools/TextTemplate.md b/docs/articles/tools/TextTemplate.md index 6a7e8f6b..049bc253 100644 --- a/docs/articles/tools/TextTemplate.md +++ b/docs/articles/tools/TextTemplate.md @@ -2,53 +2,55 @@ uid: Mapster.Tools.TextTemplate --- -### TextTemplate -``` +# Text Template + +```nuget PM> Install-Package ExpressionTranslator ``` -Although Mapster allow you to [step-into debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging), but all mapping are generated at runtime. Therefore, error will be captured at runtime, and we also lose the power of static analysis to find the usage. +Although Mapster allow you to [step-into debugging](xref:Mapster.Plugins.Debugging), but all mapping are generated at runtime. Therefore, error will be captured at runtime, and we also lose the power of static analysis to find the usage. Here are steps to add code generation. 1. Create text template -![image](https://user-images.githubusercontent.com/5763993/56052976-f9377580-5d7c-11e9-841c-0a911fdb3a7f.png) + ![add-new-text-item](https://user-images.githubusercontent.com/5763993/56052976-f9377580-5d7c-11e9-841c-0a911fdb3a7f.png) 2. In template, add references & mapping logic -```xml -<#@ template debug="true" language="C#" #> -<#@ output extension=".g.cs" #> -<#@ Assembly Name="netstandard" #> -<#@ Assembly Name="System.Core" #> -<#@ Assembly Name="System.Runtime" #> -<#@ Assembly Name="System.Linq.Expressions" #> -<#@ Assembly Name="$(TargetDir)/$(ProjectName).dll" #> -<#@ Assembly Name="$(TargetDir)/Mapster.dll" #> -<#@ Assembly Name="$(TargetDir)/ExpressionTranslator.dll" #> -<#@ import namespace="ExpressionDebugger" #> -<#@ import namespace="Mapster" #> -<#@ import namespace="YourNamespace" #> -``` -```csharp -<# - //this line is to generate all nested mapping in 1 file - TypeAdapterConfig.GlobalSettings.SelfContainedCodeGeneration = true; - var cust = default(Customer); - var def = new ExpressionDefinitions - { - IsStatic = true, //change to false if you want instance - MethodName = "Map", - Namespace = "YourNamespace", - TypeName = "CustomerMapper" - }; - var code = cust.BuildAdapter() - .CreateMapExpression() - .ToScript(def); - WriteLine(code); -#> -``` + ```xml + <#@ template debug="true" language="C#" #> + <#@ output extension=".g.cs" #> + <#@ Assembly Name="netstandard" #> + <#@ Assembly Name="System.Core" #> + <#@ Assembly Name="System.Runtime" #> + <#@ Assembly Name="System.Linq.Expressions" #> + <#@ Assembly Name="$(TargetDir)/$(ProjectName).dll" #> + <#@ Assembly Name="$(TargetDir)/Mapster.dll" #> + <#@ Assembly Name="$(TargetDir)/ExpressionTranslator.dll" #> + <#@ import namespace="ExpressionDebugger" #> + <#@ import namespace="Mapster" #> + <#@ import namespace="YourNamespace" #> + ``` + + ```csharp + <# + //this line is to generate all nested mapping in 1 file + TypeAdapterConfig.GlobalSettings.SelfContainedCodeGeneration = true; + var cust = default(Customer); + var def = new ExpressionDefinitions + { + IsStatic = true, //change to false if you want instance + MethodName = "Map", + Namespace = "YourNamespace", + TypeName = "CustomerMapper" + }; + var code = cust.BuildAdapter() + .CreateMapExpression() + .ToScript(def); + WriteLine(code); + #> + ``` 3. Generate code by right click on template file, and select `Run Custom Tool`. @@ -56,24 +58,24 @@ That's it. Done! --- -## Information +## Additional Information ### Links - Example: [CustomerMapper]( https://github.com/MapsterMapper/Mapster/blob/master/src/Benchmark/CustomerMapper.tt) -- [T4 Documentation](https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2019) (Microsoft) +- [code generation and T4 Documentation](https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2019) (Microsoft) ### Q & A Q: How to pass lambda to Before/After mapping? -A: Please use `BeforeMappingInline` and `AfterMappingInline` instead. [link](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) +A: Please use [`BeforeMappingInline` and `AfterMappingInline` instead](xref:Mapster.Settings.BeforeAfterMapping) Q: Can I generate multiple outputs from single template? -A: You can. [link](https://stackoverflow.com/questions/33575419/how-to-create-multiple-output-files-from-a-single-t4-template-using-tangible-edi) +A: You can: [How to: Create multiple output files from a single t4 template using tangible edi](https://stackoverflow.com/questions/33575419/how-to-create-multiple-output-files-from-a-single-t4-template-using-tangible-edi) Q: After running template file, it said library XXX not found. -A: Some unused libraries will be excluded during build. You can direct reference to dll in template file. Or tell Visual Studio to copy all reference libraries to output. [link](https://stackoverflow.com/questions/43837638/how-to-get-net-core-projects-to-copy-nuget-references-to-build-output/43841481) +A: Some unused libraries will be excluded during build. You can direct reference to dll in template file. Or [tell Visual Studio to copy all reference libraries to output](https://stackoverflow.com/questions/43837638/how-to-get-net-core-projects-to-copy-nuget-references-to-build-output/43841481). ```xml @@ -94,12 +96,12 @@ A: You need direct reference. <#@ Assembly Name="/usr/local/share/dotnet/sdk/2.2.103/Microsoft/Microsoft.NET.Build.Extensions/net461/lib/netstandard.dll" #> ``` -Q: After running template file in .NET Core project on Windows, it said, System.Runtime version 4.2.x.x not found. -A: You can build using .NET Framework version. Otherwise, you need to update assembly binding in Visual Studio config file. [link](https://stackoverflow.com/questions/51550265/t4-template-could-not-load-file-or-assembly-system-runtime-version-4-2-0-0) +Q: After running template file in .NET Core project on Windows, it said, System.Runtime version 4.2.x.x not found. +A: You can build using .NET Framework version. Otherwise, [you need to update assembly binding in Visual Studio config file](https://stackoverflow.com/questions/51550265/t4-template-could-not-load-file-or-assembly-system-runtime-version-4-2-0-0) -Q: After running template file, it said Compile items are duplicated. +Q: After running template file, it said Compile items are duplicated. A: You can set property to skip generated items. ```xml **/*.g.cs -``` \ No newline at end of file +``` diff --git a/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md b/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md index bfe86a5e..07dcd00e 100644 --- a/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md +++ b/docs/articles/tools/mapster-tool/Attribute-base-Code-generation.md @@ -1,12 +1,14 @@ --- -uid: Mapster.Tools.MapsterTool.Attributes +uid: Mapster.Tools.MapsterTool.AttributesDtoGeneration +title: Mapster Tool --- -### Generate models +## Attribute-based Dto model generation Annotate your class with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`. Example: + ```csharp [AdaptTo("[name]Dto")] public class Student { @@ -15,35 +17,38 @@ public class Student { ``` Then Mapster will generate: + ```csharp public class StudentDto { ... } ``` -#### Ignore some properties on generation +### Ignore some properties on generation By default, code generation will ignore properties that annotated `[AdaptIgnore]` attribute. But you can add more settings which include `IgnoreAttributes`, `IgnoreNoAttributes`, `IgnoreNamespaces`. Example: + ```csharp [AdaptTo("[name]Dto", IgnoreNoAttributes = new[] { typeof(DataMemberAttribute) })] public class Student { [DataMember] public string Name { get; set; } //this property will be generated - + public string LastName { get; set; } //this will not be generated } ``` -#### Change property types +### Change property types By default, if property type annotated with the same adapt attribute, code generation will forward to that type. (For example, `Student` has `ICollection`, after code generation `StudentDto` will has `ICollection`). You can override this by `[PropertyType(typeof(Target))]` attribute. This annotation can be annotated to either on property or on class. For example: + ```csharp [AdaptTo("[name]Dto")] public class Student { @@ -58,6 +63,7 @@ public class Enrollment { ``` This will generate: + ```csharp public class StudentDto { public ICollection Enrollments { get; set; } @@ -72,6 +78,7 @@ public class EnrollmentDto { For `[AdaptTo]` and `[AdaptTwoWays]`, you can generate readonly properties with `MapToConstructor` setting. For example: + ```csharp [AdaptTo("[name]Dto", MapToConstructor = true)] public class Student { @@ -80,6 +87,7 @@ public class Student { ``` This will generate: + ```csharp public class StudentDto { public string Name { get; } @@ -90,11 +98,12 @@ public class StudentDto { } ``` -#### Generate nullable properties +### Generate nullable properties For `[AdaptFrom]`, you can generate nullable properties with `IgnoreNullValues` setting. For example: + ```csharp [AdaptFrom("[name]Merge", IgnoreNullValues = true)] public class Student { @@ -103,62 +112,9 @@ public class Student { ``` This will generate: + ```csharp public class StudentMerge { public int? Age { get; set; } } ``` - -### Generate extension methods - -#### Generate using `[GenerateMapper]` attribute -For any POCOs annotate with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`, you can add `[GenerateMapper]` in order to generate extension methods. - -Example: -```csharp -[AdaptTo("[name]Dto"), GenerateMapper] -public class Student { - ... -} -``` - -Then Mapster will generate: -```csharp -public class StudentDto { - ... -} -public static class StudentMapper { - public static StudentDto AdaptToDto(this Student poco) { ... } - public static StudentDto AdaptTo(this Student poco, StudentDto dto) { ... } - public static Expression> ProjectToDto => ... -} -``` - -#### Configuration -If you have configuration, it must be in `IRegister` - -```csharp -public class MyRegister : IRegister -{ - public void Register(TypeAdapterConfig config) - { - config.NewConfig(); - } -} -``` - -#### Generate using configuration - -You can also generate extension methods and add extra settings from configuration. - -```csharp -public class MyRegister : IRegister -{ - public void Register(TypeAdapterConfig config) - { - config.NewConfig() - .GenerateMapper(MapType.Map | MapType.MapToTarget); - } -} -``` - diff --git a/docs/articles/tools/mapster-tool/Attribute-based-Extension-generation.md b/docs/articles/tools/mapster-tool/Attribute-based-Extension-generation.md new file mode 100644 index 00000000..ad4d7a81 --- /dev/null +++ b/docs/articles/tools/mapster-tool/Attribute-based-Extension-generation.md @@ -0,0 +1,32 @@ +--- +uid: Mapster.Tools.MapsterTool.AttributeBasedExtensionGeneration +title: Mapster Tool +--- + +## Generate extension methods via attributes + +### Generate using `[GenerateMapper]` attribute + +For any POCOs annotate with `[AdaptFrom]`, `[AdaptTo]`, or `[AdaptTwoWays]`, you can add `[GenerateMapper]` in order to generate extension methods. + +Example: + +```csharp +[AdaptTo("[name]Dto"), GenerateMapper] +public class Student { + ... +} +``` + +Then Mapster will generate: + +```csharp +public class StudentDto { + ... +} +public static class StudentMapper { + public static StudentDto AdaptToDto(this Student poco) { ... } + public static StudentDto AdaptTo(this Student poco, StudentDto dto) { ... } + public static Expression> ProjectToDto => ... +} +``` diff --git a/docs/articles/tools/mapster-tool/Configuration-based-Code-generation.md b/docs/articles/tools/mapster-tool/Configuration-based-Code-generation.md new file mode 100644 index 00000000..82b92741 --- /dev/null +++ b/docs/articles/tools/mapster-tool/Configuration-based-Code-generation.md @@ -0,0 +1,33 @@ +--- +uid: Mapster.Tools.MapsterTool.ConfigurationBasedCodeGeneration +title: Mapster Tool +--- + +## Configuration-based code generation + +If you have configuration, it must be in `IRegister` + +```csharp +public class MyRegister : IRegister +{ + public void Register(TypeAdapterConfig config) + { + config.NewConfig(); + } +} +``` + +### Advanced configuration + +You can also generate extension methods and add extra settings from configuration. + +```csharp +public class MyRegister : IRegister +{ + public void Register(TypeAdapterConfig config) + { + config.NewConfig() + .GenerateMapper(MapType.Map | MapType.MapToTarget); + } +} +``` diff --git a/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md b/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md index 9a543a77..1d4076ef 100644 --- a/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md +++ b/docs/articles/tools/mapster-tool/Fluent-API-Code-generation.md @@ -1,8 +1,9 @@ --- uid: Mapster.Tools.MapsterTool.FluentAPI +title: Mapster Tool --- -### Configuration class +## Configuration class Create a configuration class implement `ICodeGenerationRegister`. @@ -21,26 +22,29 @@ public class MyRegister : ICodeGenerationRegister } ``` -### Generate models +## Generate models Declare `AdaptFrom`, `AdaptTo`, or `AdaptTwoWays`. Example: + ```csharp config.AdaptTo("[name]Dto") .ForType(); ``` Then Mapster will generate: + ```csharp public class StudentDto { ... } ``` -#### Add types to generate +### Add types to generate + +You can add types by `ForTypes`, `ForAllTypesInNamespace`, `ForType<>`, and you can remove added types using `ExcludeTypes`: -You can add types by `ForTypes`, `ForAllTypesInNamespace`, `ForType<>`, and you can remove added types using `ExcludeTypes`. ```csharp config.AdaptTo("[name]Dto") .ForAllTypesInNamespace(Assembly.GetExecutingAssembly(), "Sample.CodeGen.Domains") @@ -48,12 +52,12 @@ config.AdaptTo("[name]Dto") .ExcludeTypes(type => type.IsEnum) ``` - -#### Ignore some properties on generation +### Ignore some properties on generation By default, code generation will ignore properties that annotated `[AdaptIgnore]` attribute. But you can add more settings which include `IgnoreAttributes`, `IgnoreNoAttributes`, `IgnoreNamespaces`. Example: + ```csharp config.AdaptTo("[name]Dto") .ForType() @@ -66,7 +70,7 @@ public class Student { } ``` -#### Ignore a property +### Ignore a property ```csharp config.AdaptTo("[name]Dto") @@ -75,7 +79,7 @@ config.AdaptTo("[name]Dto") }); ``` -#### Change a property name, type +### Change a property name, type ```csharp config.AdaptTo("[name]Dto") @@ -85,7 +89,7 @@ config.AdaptTo("[name]Dto") }); ``` -#### Forward property types +### Forward property types By default, code generation will forward type on the same declaration. (For example, `Student` has `ICollection`, after code generation `StudentDto` will has `ICollection`). @@ -97,11 +101,12 @@ config.AdaptTo("[name]Dto") .AlterType(); //forward all Student to Person ``` -#### Generate readonly properties +### Generate readonly properties For `AdaptTo` and `AdaptTwoWays`, you can generate readonly properties with `MapToConstructor` setting. For example: + ```csharp config.AdaptTo("[name]Dto") .ForType() @@ -109,6 +114,7 @@ config.AdaptTo("[name]Dto") ``` This will generate: + ```csharp public class StudentDto { public string Name { get; } @@ -119,11 +125,12 @@ public class StudentDto { } ``` -#### Generate nullable properties +### Generate nullable properties For `AdaptFrom`, you can generate nullable properties with `IgnoreNullValues` setting. For example: + ```csharp config.AdaptFrom("[name]Merge") .ForType() @@ -131,18 +138,21 @@ config.AdaptFrom("[name]Merge") ``` This will generate: + ```csharp public class StudentMerge { public int? Age { get; set; } } ``` -### Generate extension methods +## Generate extension methods + +### Generate using `GenerateMapper` -#### Generate using `GenerateMapper`. For any POCOs declared with `AdaptFrom`, `AdaptTo`, or `AdaptTwoWays`, you can declare `GenerateMapper` in order to generate extension methods. Example: + ```csharp config.AdaptTo("[name]Dto") .ForType(); @@ -152,6 +162,7 @@ config.GenerateMapper("[name]Mapper") ``` Then Mapster will generate: + ```csharp public class StudentDto { ... diff --git a/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md b/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md index efc56b96..6fca9b8d 100644 --- a/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md +++ b/docs/articles/tools/mapster-tool/Interface-base-Code-generation.md @@ -1,12 +1,14 @@ --- uid: Mapster.Tools.MapsterTool.Interfaces +title: Mapster Tool --- -### Generate mapper from interface +## Generate mapper from interface Annotate your interface with `[Mapper]` in order for tool to pickup for generation. This is example interface. + ```csharp [Mapper] public interface IProductMapper @@ -16,13 +18,14 @@ public interface IProductMapper ``` You can add multiple members as you want. All member names are flexible, but signature must be in following patterns: + ```csharp [Mapper] public interface ICustomerMapper { //for queryable Expression> ProjectToDto { get; } - + //map from POCO to DTO CustomerDTO MapToDto(Customer customer); diff --git a/docs/articles/tools/Mapster.Tool.md b/docs/articles/tools/mapster-tool/Mapster-Tool-Overview.md similarity index 69% rename from docs/articles/tools/Mapster.Tool.md rename to docs/articles/tools/mapster-tool/Mapster-Tool-Overview.md index 1c1b136c..00d551bb 100644 --- a/docs/articles/tools/Mapster.Tool.md +++ b/docs/articles/tools/mapster-tool/Mapster-Tool-Overview.md @@ -1,46 +1,58 @@ --- -uid: Mapster.Tools.MapsterTool +uid: Mapster.Tools.MapsterTool.Overview --- -## Mapster.Tool +# Mapster Tool Overview + +The Mapster.Tool is a command-line tool that helps you generate DTOs (Data Transfer Objects) and mapping code based on your domain models or interfaces. It supports various generation strategies, including Fluent API configuration, attribute-based annotations, and interface-based definitions. + +## Install Mapster.Tool -### Install Mapster.Tool ```bash #skip this step if you already have dotnet-tools.json -dotnet new tool-manifest +dotnet new tool-manifest dotnet tool install Mapster.Tool ``` -### Install Mapster +## Install Mapster + For lightweight dependency, you can just install `Mapster.Core`. -``` + +```nuget PM> Install-Package Mapster.Core ``` However, if you need `TypeAdapterConfig` for advance configuration, you still need `Mapster`. -``` + +```nuget PM> Install-Package Mapster ``` -### Commands -Mapster.Tool provides 3 commands +## Commands + +Mapster.Tool provides 3 commands: + - **model**: generate models from entities - **extension**: generate extension methods from entities - **mapper**: generate mappers from interfaces And Mapster.Tool provides following options -- -a: Define input assembly -- -b: Specify base namespace for generating dynamic outputs & namespaces -- -n: Define namespace of generated classes -- -o: Define output directory -- -p: Print full type name (if your DTOs/POCOs having the same name) -- -r: Generate record types instead of POCO types + +- `-a`: Define input assembly +- `-b`: Specify base namespace for generating dynamic outputs & namespaces +- `-n`: Define namespace of generated classes +- `-o`: Define output directory +- `-p`: Print full type name (if your DTOs/POCOs having the same name) +- `-r`: Generate record types instead of POCO types - -s: Skip generating existing files + ### csproj integration #### Generate manually -add following code to your `csproj` file. + +add following code to your `csproj` file: + ```xml @@ -50,13 +62,17 @@ add following code to your `csproj` file. ``` + to generate run following command on `csproj` file directory: + ```bash dotnet msbuild -t:Mapster ``` #### Generate automatically on build -add following code to your `csproj` file. + +add following code to your `csproj` file: + ```xml @@ -67,7 +83,9 @@ add following code to your `csproj` file. ``` #### Clean up -add following code to your `csproj` file. + +add following code to your `csproj` file: + ```xml @@ -76,14 +94,17 @@ add following code to your `csproj` file. ``` + to clean up run following command: + ```bash dotnet msbuild -t:CleanGenerated ``` #### Generate full type name -If your POCOs and DTOs have the same name, you might need to generate using full type name, by adding `-p` flag. +If your POCOs and DTOs have the same name, you might need to generate using full type name, by adding `-p` flag: + ```xml @@ -95,8 +116,10 @@ If your POCOs and DTOs have the same name, you might need to generate using full ``` #### Dynamic outputs & namespaces + For example you have following structure. -``` + +```text Sample.CodeGen - Domains - Sub1 @@ -105,14 +128,16 @@ Sample.CodeGen - Domain2 ``` -And if you can specify base namespace as `Sample.CodeGen.Domains` +And if you can specify base namespace as `Sample.CodeGen.Domains`: + ```xml ``` -Code will be generated to -``` +Code will be generated to: + +```text Sample.CodeGen - Generated - Sub1 @@ -120,12 +145,14 @@ Sample.CodeGen - Sub2 - Dto2 ``` + ### Generate DTOs and mapping codes -There are 3 flavors, to generate DTOs and mapping codes -- [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation): if you don't want to touch your domain classes, or generate DTOs from domain types in different assembly. -- [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation): if you would like to keep mapping declaration close to your domain classes. -- [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation): if you already have DTOs, and you would like to define mapping through interfaces. +There are 3 flavors, to generate DTOs and mapping codes: + +- [Fluent API](xref:Mapster.Tools.MapsterTool.FluentAPI): if you don't want to touch your domain classes, or generate DTOs from domain types in different assembly. +- [Attributes](xref:Mapster.Tools.MapsterTool.AttributesDtoGeneration): if you would like to keep mapping declaration close to your domain classes. +- [Interfaces](xref:Mapster.Tools.MapsterTool.Interfaces): if you already have DTOs, and you would like to define mapping through interfaces. ### Sample @@ -134,11 +161,12 @@ There are 3 flavors, to generate DTOs and mapping codes ### Troubleshooting #### System.IO.FileNotFoundException + If you get an error similar to `Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly '...'. The system cannot find the file specified.` and you are using Mapster.Tool 8.4.1 or newer, then you can try one of the following workarounds: **Workaround 1** -Ensure that you are using Mapster.Tool version 8.4.2-pre01 or newer. +Ensure that you are using Mapster.Tool version 8.4.2-pre01 or newer. The latest pre-release version is: [![NuGet](https://img.shields.io/nuget/vpre/Mapster.Tool.svg)](https://www.nuget.org/packages/Mapster.Tool) **Workaround 2** @@ -158,13 +186,12 @@ Add `true` to your cs Change your `dotnet build` command to `dotnet build -p:CopyLocalLockFileAssemblies=true` as follows: ```xml + + [...] - - [...] + + [...] + [...] + ``` - - - -``` -``` \ No newline at end of file diff --git a/docs/articles/tools/mapster-tool/toc.yml b/docs/articles/tools/mapster-tool/toc.yml index c8703eb4..20bb314d 100644 --- a/docs/articles/tools/mapster-tool/toc.yml +++ b/docs/articles/tools/mapster-tool/toc.yml @@ -1,7 +1,11 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json +- name: Overview and Installation + uid: Mapster.Tools.MapsterTool.Overview + href: Mapster-Tool-Overview.md - name: Fluent API href: Fluent-API-Code-generation.md -- name: Attributes +- name: Attribute-based Dto model generation + uid: Mapster.Tools.MapsterTool.AttributesDtoGeneration href: Attribute-base-Code-generation.md - name: Interfaces href: Interface-base-Code-generation.md diff --git a/docs/articles/tools/toc.yml b/docs/articles/tools/toc.yml index 29177de2..a206bd9a 100644 --- a/docs/articles/tools/toc.yml +++ b/docs/articles/tools/toc.yml @@ -1,17 +1,9 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json -- name: Mapster.Tool - uid: Mapster.Tools.MapsterTool - href: Mapster.Tool.md - items: - - name: Fluent API - uid: Mapster.Tools.MapsterTool.FluentAPI - href: mapster-tool/Fluent-API-Code-generation.md - - name: Attributes - uid: Mapster.Tools.MapsterTool.Attributes - href: mapster-tool/Attribute-base-Code-generation.md - - name: Interfaces - uid: Mapster.Tools.MapsterTool.Interfaces - href: mapster-tool/Interface-base-Code-generation.md -- name: TextTemplate +# TODO: Decide if we want Tools to have a topicHref +items: +- name: Mapster Tool + topicHref: xref:Mapster.Tools.MapsterTool.Overview + href: mapster-tool/toc.yml +- name: Text Template uid: Mapster.Tools.TextTemplate href: TextTemplate.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..53c65a35 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,2 @@ + +[!INCLUDE [landing-page](../README.md)] diff --git a/docs/toc.yml b/docs/toc.yml index da9d03a1..af01d451 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -1,6 +1,7 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json # NOTE: Those top level items will be shown as the Top Navigation Menu Entries -# IMPORTANT: Do not add the explicit toc.yml files under the appropriate folders here, which already is the default behavior of docfx, as this would otherwhise attempt to show them in the top navigation instead of the side navigation TOCs. +# IMPORTANT: Do not add the explicit toc.yml files under the appropriate folders here, as this would otherwhise attempt to show them in the top navigation instead of the side navigation TOCs. +# See: https://dotnet.github.io/docfx/docs/table-of-contents.html#navigation-bar - name: Home href: index.md - name: Documentation From 46ae282cba2066aa0f7e244d0c9435fa1026e1cd Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Wed, 3 Dec 2025 23:20:43 +0100 Subject: [PATCH 334/363] chore(docs): Add docfx setup and files for spellcheck and markdownlint --- .markdownlint.jsonc | 16 +++++ .settings/extensions.json | 6 ++ cSpell.json | 26 ++++++++ docs/.gitignore | 2 + docs/.nojekyll | 0 docs/articles/toc.yml | 7 +- docs/config/filterConfig.yml | 14 ++++ docs/docfx.json | 123 +++++++++++++++++++++++++++++++++++ 8 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 .markdownlint.jsonc create mode 100644 .settings/extensions.json create mode 100644 cSpell.json create mode 100644 docs/.gitignore create mode 100644 docs/.nojekyll create mode 100644 docs/config/filterConfig.yml create mode 100644 docs/docfx.json diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 00000000..17ee9dd2 --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,16 @@ +{ + "$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint/v0.38.0/schema/markdownlint-config-schema.json", + "default": true, + "line-length": false, + "commands-show-output": false, + "no-bare-urls": false, + "no-inline-html": false, + "no-duplicate-heading": false, + "no-emphasis-as-heading": false, + // Headers must start at the beginning of the line - false positive in some cases where it makes sense. + "MD023": false, + // First line in a file should be a top-level heading - false positive for include files. + "MD041": false, + // Link fragments should be valid - false positive for docfx tabs + "MD051": false +} \ No newline at end of file diff --git a/.settings/extensions.json b/.settings/extensions.json new file mode 100644 index 00000000..0e96c08b --- /dev/null +++ b/.settings/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "DavidAnson.vscode-markdownlint", + "streetsidesoftware.code-spell-checker-cspell-bundled-dictionaries" + ] +} diff --git a/cSpell.json b/cSpell.json new file mode 100644 index 00000000..23a431b2 --- /dev/null +++ b/cSpell.json @@ -0,0 +1,26 @@ +{ + "version": "0.2", + "language": "en", + "words": [ + "alertblocks", + "appsettings", + "definitionlists", + "docfx", + "dotnet", + "github", + "gridtables", + "markdig", + "medialinks", + "MVVM" + ], + "dictionaries": [ + "csharp", + "companies" + ], + "ignoreWords": [], + "ignorePaths": [ + "src/*", + "**.gitignore", + "LICENSE" + ] +} diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..cd2e91a0 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_site/* +!docs/articles/packages/**.md diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/docs/articles/toc.yml b/docs/articles/toc.yml index 0ee9ae5f..33c770b3 100644 --- a/docs/articles/toc.yml +++ b/docs/articles/toc.yml @@ -3,11 +3,16 @@ - items: - name: Mapping href: mapping/toc.yml + topicHref: xref:Mapster.Mapping.BasicUsages - name: Configuration href: configuration/toc.yml + topicHref: xref:Mapster.Configuration.Overview - name: Settings href: settings/toc.yml + topicHref: xref:Mapster.Settings.Custom.Mapping - name: Plugins href: plugins/toc.yml + topicHref: xref:Mapster.Plugins.Async - name: Tools - href: tools/toc.yml \ No newline at end of file + href: tools/toc.yml + topicHref: xref:Mapster.Tools.MapsterTool.Overview \ No newline at end of file diff --git a/docs/config/filterConfig.yml b/docs/config/filterConfig.yml new file mode 100644 index 00000000..7d1cde95 --- /dev/null +++ b/docs/config/filterConfig.yml @@ -0,0 +1,14 @@ +# YAML-Language Server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/filterconfig.schema.json +# Filter configuration file +# More information about filter configuration can be found at https://dotnet.github.io/docfx/docs/dotnet-api-docs.html#custom-filter-rules +# To exclude specific namespaces, types, or members from the generated API documentation, uncomment and modify the rules below, using appropriate regular expressions. +# apiRules: +# - exclude: +# uidRegex: ^PartialNamespace\.NextNamespace$ +# type: Namespace +# - exclude: +# uidRegex: SomeTypeName$ +# type: Type +# - exclude: +# uidRegex: __SomeMemberName(\(.*\))?$ +# type: Member \ No newline at end of file diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 00000000..f34f9c46 --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,123 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", + "metadata": [ + { + "src": [ + { + "files": [ + // "**/DevTKSS.Extensions.Uno.Extensions.csproj", + // "**/DevTKSS.Uno.Samples.MvuxGallery.csproj" + ], + "src": "../src" + } + ], + "includeExplicitInterfaceImplementations": false, + "disableDefaultFilter": false, + "filter": "Resources/filterConfig.yml", + "properties": { + "TargetFramework" : "net9.0" + }, + "codeSourceBasePath": "../", + "noRestore": true, + "output": "api", + "memberLayout": "samePage", + "outputFormat": "apiPage" + } + ], + "build": { + "globalMetadata": { + "_appTitle": "Mapster - The Mapper of Your Domain", + "_appName": "Mapster", + //"_appFaviconPath": "images/logo/your-logo.ico", + "_appLogoPath": "https://cloud.githubusercontent.com/assets/5763993/26522718/d16f3e42-4330-11e7-9b78-f8c7402624e7.png", + "_copyrightFooter": "© 2025 Here comes your Name, All rights reserved.", + "_ownerName": "Awesome Developer", + "_githubProfileLink": "https://github.com/MapsterMapper", + "_enableSearch": true, + "_disableAffix": false, + "_disableBreadcrumb": false, + "_disableNextArticle": false, + "_disableTocFilter": false, + "_lang": "en", + "_gitConribute": { + "branch": "development" + }, + "pdf": false, + "_layout": "landing" + }, + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.{md,yml}", + "toc.yml", + "index.md", + "../README.md" + ], + "src": "articles", + "dest": "docs", + "exclude": [ + "**/.include/**" + ] + }, + { + "rootTocPath": "releases", + "files":["../releases/**.{md}"], + "src": "../releases", + "dest": "releases" + } + ], + "resource": [ + { + "files": [ + "images/*.{png,ico}", + "articles/.assets/*.{png,gif}" + ], + "dot": true + } + ], + "overwrite": [ + { + "files":[ + "api/**.md", + "api/**.yml", + "articles/**.md", + "articles/**.yml", + "index.md" + ], + "exclude":[ + "obj/**", + "_site/**" + ] + } + ], + "template": [ + "default", + "modern" + ], + "xref": [ "https://learn.microsoft.com/en-us/dotnet/.xrefmap.json" ], + "markdownEngineProperties": { + "markdigExtensions": [ + "yaml", + "definitionlists", + "diagrams", + "advanced", + "alerts", + "footers", + "footnotes", + "medialinks" + ] + }, + "output": "_site", + "sitemap": { + "baseUrl": "https://mapstermapper.github.io/Mapster", + "priority": 0.1, + "changefreq": "monthly" + } + } +} From 76c944a5a0910309a3293ac0a2e046ef78f6edcd Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Wed, 3 Dec 2025 23:34:34 +0100 Subject: [PATCH 335/363] chore: adjust path --- docs/docfx.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docfx.json b/docs/docfx.json index f34f9c46..ef361c01 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -13,7 +13,7 @@ ], "includeExplicitInterfaceImplementations": false, "disableDefaultFilter": false, - "filter": "Resources/filterConfig.yml", + "filter": "config/filterConfig.yml", "properties": { "TargetFramework" : "net9.0" }, From 47bbb3651196d951970d601d08f4241088faca06 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Wed, 3 Dec 2025 23:49:42 +0100 Subject: [PATCH 336/363] chore: link updates --- docs/.gitignore | 2 +- docs/articles/Home.md | 16 ++++++++-------- docs/articles/_Sidebar.md | 21 ++++++++++++--------- docs/articles/configuration/toc.yml | 2 +- docs/articles/packages/toc.yml | 2 +- docs/articles/toc.yml | 6 +++--- docs/articles/tools/TextTemplate.md | 2 +- docs/docfx.json | 13 ++++--------- 8 files changed, 31 insertions(+), 33 deletions(-) diff --git a/docs/.gitignore b/docs/.gitignore index cd2e91a0..c91848d7 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,2 +1,2 @@ _site/* -!docs/articles/packages/**.md +!docs/articles/**.{md,yml} diff --git a/docs/articles/Home.md b/docs/articles/Home.md index ddb05c69..9ff4b7a9 100644 --- a/docs/articles/Home.md +++ b/docs/articles/Home.md @@ -112,19 +112,19 @@ | `[AdaptIgnore(side)]` | Ignore property from mapping | [attribute](xref:Mapster.Settings.Custom.Attributes) | | `[UseDestinationValue]` | Use existing property object to map data | [attribute](xref:Mapster.Settings.Custom.Attributes) | | `[AdaptTo]` `[AdaptFrom]` `[AdaptTwoWays]` | Add setting on POCO class | [location](xref:Mapster.Configuration.Location#attributes) | -| `[Mapper]` `[GenerateMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](xref:Mapster.Tools.MapsterTool) | +| `[Mapper]` `[GenerateMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](xref:Mapster.Tools.MapsterTool.Overview) | #### Plugins | Plugin | Method | Description | | ------ | ------------- |-----------------------| -| [Async](xref:Mapster.Plugins.Async) | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | -| [Debugging](xref:Mapster.Plugins.Debugging) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | -| [Dependency Injection](xref:Mapster.Plugins.DependencyInjection) | `MapContext.Current.GetService()` | Inject service into mapping logic | -| [EF 6 & EF Core](xref:Mapster.Plugins.EntityFramework) | `builder.EntityFromContext` | Copy data to tracked EF entity | -| [FEC](xref:Mapster.Plugins.FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | -| [Immutable](xref:Mapster.Plugins.Immutable) | `config.EnableImmutableMapping()` | mapping to immutable collection | -| [Json.net](xref:Mapster.Plugins.JsonNet) | `config.EnableJsonMapping()` | map json from/to poco and string | +| [Async](xref:Mapster.Packages.Async) | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | +| [Debugging](xref:Mapster.Packages.Diagnostics) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | +| [Dependency Injection](xref:Mapster.Packages.DependencyInjection) | `MapContext.Current.GetService()` | Inject service into mapping logic | +| [EF 6 & EF Core](xref:Mapster.Packages.EntityFramework) | `builder.EntityFromContext` | Copy data to tracked EF entity | +| [FEC](xref:Mapster.Packages.FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | +| [Immutable](xref:Mapster.Packages.Immutable) | `config.EnableImmutableMapping()` | mapping to immutable collection | +| [Json.net](xref:Mapster.Packages.JsonNet) | `config.EnableJsonMapping()` | map json from/to poco and string | #### Code Generation Tools diff --git a/docs/articles/_Sidebar.md b/docs/articles/_Sidebar.md index 24a52256..bf59116d 100644 --- a/docs/articles/_Sidebar.md +++ b/docs/articles/_Sidebar.md @@ -1,16 +1,16 @@ -### Getting Started +# Getting Started with Mapster -##### Translation -* [中文文档](https://github.com/rivenfx/Mapster-docs) (sp thx to [@staneee](https://github.com/staneee)) +## [References](https://github.com/MapsterMapper/Mapster/wiki) + +## Mapping -##### [References](https://github.com/MapsterMapper/Mapster/wiki) -##### Mapping * [Basic usages](https://github.com/MapsterMapper/Mapster/wiki/Basic-usages) * [Mappers](https://github.com/MapsterMapper/Mapster/wiki/Mappers) * [Data types](https://github.com/MapsterMapper/Mapster/wiki/Data-types) * [Mapping with interface](https://github.com/MapsterMapper/Mapster/wiki/Mapping-Configuration-With-IMapFrom-Interface) -##### Configuration +## Configuration + * [Configuration](https://github.com/MapsterMapper/Mapster/wiki/Configuration) * [Config inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) * [Config instance](https://github.com/MapsterMapper/Mapster/wiki/Config-instance) @@ -18,7 +18,8 @@ * [Config validation & compilation](https://github.com/MapsterMapper/Mapster/wiki/Config-validation-&-compilation) * [Config for nested mapping](https://github.com/MapsterMapper/Mapster/wiki/Config-for-nested-mapping) -##### Settings +## Settings + * Custom member matching logic * [Custom mapping](https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) * [Custom naming convention](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) @@ -36,7 +37,8 @@ * [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) * [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) -##### Plugins +## Plugins + * [Async Support](https://github.com/MapsterMapper/Mapster/wiki/Async) * [Debugging](https://github.com/MapsterMapper/Mapster/wiki/Debugging) * [Dependency Injection](https://github.com/MapsterMapper/Mapster/wiki/Dependency-Injection) @@ -45,7 +47,8 @@ * [Immutable](https://github.com/MapsterMapper/Mapster/wiki/Immutable) * [Json.net](https://github.com/MapsterMapper/Mapster/wiki/Json.net) -##### Tools +## Tools + * [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) * [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation) * [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation) diff --git a/docs/articles/configuration/toc.yml b/docs/articles/configuration/toc.yml index e4718218..3979af6c 100644 --- a/docs/articles/configuration/toc.yml +++ b/docs/articles/configuration/toc.yml @@ -14,7 +14,7 @@ href: Config-location.md - name: Config validation & compilation uid: Mapster.Configuration.ValidationAndCompilation - href: Config-validation-&-compilation.md + href: Config-validation-and-compilation.md - name: Config for nested mapping uid: Mapster.Configuration.NestedMapping href: Config-for-nested-mapping.md diff --git a/docs/articles/packages/toc.yml b/docs/articles/packages/toc.yml index ceedfea3..e3770629 100644 --- a/docs/articles/packages/toc.yml +++ b/docs/articles/packages/toc.yml @@ -5,7 +5,7 @@ href: Async.md - name: "Debugging & Diagnostics Support" uid: Mapster.Packages.Diagnostics - href: Debugging.md + href: Diagnostics.md - name: Dependency Injection uid: Mapster.Packages.DependencyInjection href: Dependency-Injection.md diff --git a/docs/articles/toc.yml b/docs/articles/toc.yml index 33c770b3..2d874dda 100644 --- a/docs/articles/toc.yml +++ b/docs/articles/toc.yml @@ -10,9 +10,9 @@ - name: Settings href: settings/toc.yml topicHref: xref:Mapster.Settings.Custom.Mapping - - name: Plugins - href: plugins/toc.yml - topicHref: xref:Mapster.Plugins.Async + - name: Packages + href: packages/toc.yml + topicHref: xref:Mapster.Packages.Async - name: Tools href: tools/toc.yml topicHref: xref:Mapster.Tools.MapsterTool.Overview \ No newline at end of file diff --git a/docs/articles/tools/TextTemplate.md b/docs/articles/tools/TextTemplate.md index 049bc253..ab02177a 100644 --- a/docs/articles/tools/TextTemplate.md +++ b/docs/articles/tools/TextTemplate.md @@ -8,7 +8,7 @@ uid: Mapster.Tools.TextTemplate PM> Install-Package ExpressionTranslator ``` -Although Mapster allow you to [step-into debugging](xref:Mapster.Plugins.Debugging), but all mapping are generated at runtime. Therefore, error will be captured at runtime, and we also lose the power of static analysis to find the usage. +Although Mapster allow you to [step-into debugging](xref:Mapster.Packages.Diagnostics), but all mapping are generated at runtime. Therefore, error will be captured at runtime, and we also lose the power of static analysis to find the usage. Here are steps to add code generation. diff --git a/docs/docfx.json b/docs/docfx.json index ef361c01..d9d379c3 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -5,15 +5,14 @@ "src": [ { "files": [ - // "**/DevTKSS.Extensions.Uno.Extensions.csproj", - // "**/DevTKSS.Uno.Samples.MvuxGallery.csproj" + "../src/Mapster/Mapster.sln" ], "src": "../src" } ], "includeExplicitInterfaceImplementations": false, "disableDefaultFilter": false, - "filter": "config/filterConfig.yml", + // "filter": "config/filterConfig.yml", "properties": { "TargetFramework" : "net9.0" }, @@ -57,19 +56,15 @@ "articles/**.{md,yml}", "toc.yml", "index.md", - "../README.md" + "README.md" ], - "src": "articles", - "dest": "docs", "exclude": [ "**/.include/**" ] }, { "rootTocPath": "releases", - "files":["../releases/**.{md}"], - "src": "../releases", - "dest": "releases" + "files":["releases/**.{md}"] } ], "resource": [ From 8d34b75051dc02e5aec13ef3d25b221af4290e16 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Mon, 8 Dec 2025 22:17:35 +0100 Subject: [PATCH 337/363] chore: Update docs fix links --- .gitignore | 3 +++ docs/articles/Home.md | 6 +++--- .../packages/{Diagnostics.md => ExpressionDebugging.md} | 4 ++-- docs/articles/packages/toc.yml | 6 +++--- docs/articles/tools/TextTemplate.md | 2 +- docs/articles/tools/mapster-tool/toc.yml | 8 ++++++++ 6 files changed, 20 insertions(+), 9 deletions(-) rename docs/articles/packages/{Diagnostics.md => ExpressionDebugging.md} (95%) diff --git a/.gitignore b/.gitignore index 9c0a14cd..336ce443 100644 --- a/.gitignore +++ b/.gitignore @@ -131,6 +131,9 @@ packages/ # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) !packages/build/ +# Not ignore packages folder in docs folder +!docs/articles/packages/ + # Windows Azure Build Output csx/ *.build.csdef diff --git a/docs/articles/Home.md b/docs/articles/Home.md index 9ff4b7a9..6f8ee624 100644 --- a/docs/articles/Home.md +++ b/docs/articles/Home.md @@ -114,12 +114,12 @@ | `[AdaptTo]` `[AdaptFrom]` `[AdaptTwoWays]` | Add setting on POCO class | [location](xref:Mapster.Configuration.Location#attributes) | | `[Mapper]` `[GenerateMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](xref:Mapster.Tools.MapsterTool.Overview) | -#### Plugins +#### Packages -| Plugin | Method | Description | +| Packages | Method | Description | | ------ | ------------- |-----------------------| | [Async](xref:Mapster.Packages.Async) | `setting.AfterMappingAsync`
`builder.AdaptToTypeAsync` | perform async operation on mapping | -| [Debugging](xref:Mapster.Packages.Diagnostics) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | +| [Debugging](xref:Mapster.Packages.ExpressionDebugging) | `config.Compiler = exp => exp.CompileWithDebugInfo()` | compile to allow step into debugging | | [Dependency Injection](xref:Mapster.Packages.DependencyInjection) | `MapContext.Current.GetService()` | Inject service into mapping logic | | [EF 6 & EF Core](xref:Mapster.Packages.EntityFramework) | `builder.EntityFromContext` | Copy data to tracked EF entity | | [FEC](xref:Mapster.Packages.FastExpressionCompiler) | `config.Compiler = exp => exp.CompileFast()` | compile using FastExpressionCompiler | diff --git a/docs/articles/packages/Diagnostics.md b/docs/articles/packages/ExpressionDebugging.md similarity index 95% rename from docs/articles/packages/Diagnostics.md rename to docs/articles/packages/ExpressionDebugging.md index 919054b7..0de03e83 100644 --- a/docs/articles/packages/Diagnostics.md +++ b/docs/articles/packages/ExpressionDebugging.md @@ -1,6 +1,6 @@ --- -uid: Mapster.Packages.Diagnostics -title: "Packages - Diagnostics - Expression Debugging" +uid: Mapster.Packages.ExpressionDebugging +title: "Packages - Expression Debugging" --- This Package allows you to perform step-into debugging using Roslyn! diff --git a/docs/articles/packages/toc.yml b/docs/articles/packages/toc.yml index e3770629..7558022c 100644 --- a/docs/articles/packages/toc.yml +++ b/docs/articles/packages/toc.yml @@ -3,9 +3,9 @@ - name: Async Support uid: Mapster.Packages.Async href: Async.md -- name: "Debugging & Diagnostics Support" - uid: Mapster.Packages.Diagnostics - href: Diagnostics.md +- name: "Packages - Expression Debugging" + uid: Mapster.Packages.ExpressionDebugging + href: ExpressionDebugging.md - name: Dependency Injection uid: Mapster.Packages.DependencyInjection href: Dependency-Injection.md diff --git a/docs/articles/tools/TextTemplate.md b/docs/articles/tools/TextTemplate.md index ab02177a..13d94c30 100644 --- a/docs/articles/tools/TextTemplate.md +++ b/docs/articles/tools/TextTemplate.md @@ -8,7 +8,7 @@ uid: Mapster.Tools.TextTemplate PM> Install-Package ExpressionTranslator ``` -Although Mapster allow you to [step-into debugging](xref:Mapster.Packages.Diagnostics), but all mapping are generated at runtime. Therefore, error will be captured at runtime, and we also lose the power of static analysis to find the usage. +Although Mapster allow you to [step-into debugging](xref:Mapster.Packages.ExpressionDebugging), but all mapping are generated at runtime. Therefore, error will be captured at runtime, and we also lose the power of static analysis to find the usage. Here are steps to add code generation. diff --git a/docs/articles/tools/mapster-tool/toc.yml b/docs/articles/tools/mapster-tool/toc.yml index 20bb314d..627f1be7 100644 --- a/docs/articles/tools/mapster-tool/toc.yml +++ b/docs/articles/tools/mapster-tool/toc.yml @@ -3,9 +3,17 @@ uid: Mapster.Tools.MapsterTool.Overview href: Mapster-Tool-Overview.md - name: Fluent API + uid: Mapster.Tools.MapsterTool.FluentAPI href: Fluent-API-Code-generation.md - name: Attribute-based Dto model generation uid: Mapster.Tools.MapsterTool.AttributesDtoGeneration href: Attribute-base-Code-generation.md +- name: Configuration based Code generation + uid: Mapster.Tools.MapsterTool.ConfigurationBasedCodeGeneration + href: Configuration-based-Code-generation.md +- name: Attribute based Extensions generation + uid: Mapster.Tools.MapsterTool.AttributeBasedExtensionGeneration + href: Attribute-based-Extension-generation.md - name: Interfaces + uid: Mapster.Tools.MapsterTool.Interfaces href: Interface-base-Code-generation.md From 9793c2dd9028923c2dad510a43a6ff91d84db3d0 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Mon, 8 Dec 2025 22:19:05 +0100 Subject: [PATCH 338/363] chore: logo needs to be a relative path, no cloud source allowed --- docs/docfx.json | 4 ++-- docs/images/mapster-logo.png | Bin 0 -> 4368 bytes 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 docs/images/mapster-logo.png diff --git a/docs/docfx.json b/docs/docfx.json index d9d379c3..2ab3ec39 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -27,8 +27,8 @@ "globalMetadata": { "_appTitle": "Mapster - The Mapper of Your Domain", "_appName": "Mapster", - //"_appFaviconPath": "images/logo/your-logo.ico", - "_appLogoPath": "https://cloud.githubusercontent.com/assets/5763993/26522718/d16f3e42-4330-11e7-9b78-f8c7402624e7.png", + //"_appFaviconPath": "images/mapster-logo.png", + "_appLogoPath": "images/mapster-logo.png", "_copyrightFooter": "© 2025 Here comes your Name, All rights reserved.", "_ownerName": "Awesome Developer", "_githubProfileLink": "https://github.com/MapsterMapper", diff --git a/docs/images/mapster-logo.png b/docs/images/mapster-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..72ea8e1060eb82b6b4bf3d5ab201ea547f365fba GIT binary patch literal 4368 zcmbW5WmFRm)bM4Z3>6qD&FGTYKsrWu2rAu-?jE^GOT*|!Lb^dx96cr|2uS}0MhHkM zj?l;V+xzu-KHPiH{c!KO=iGDS{1OfIHOL+?KDcw|4w;sws?k52{{IAc|DWDf-+XuH z4rozJRq1&M(NQr)y48z_amDx7L8RRJFPi&od2aYLTBkeZ9Tj43Pd?FQ zw;=jp4h)@@D)9o^!5hL+$_?7B@b6_vmV+z9o{IZ`#G$>F4!@*#Gm$JH`nUl z5SZYSDmcq-UF@zMDkvkyHR(@cPUFfV5jX^|Svo3mfSI3Sxe=J;+J=$wUP zkek!vV@EPizS-H3dL2x88l=N$TJ-Q$BQ#|x*@pshiKS2L zpweTKRAIGa!0^KJ=3URVv%K^vH5WgPCt1Q0FYi9CCgK_wUr8VUT3)Inph@S6yx2C-{xJL zBc3JFuN<)QfW$j)SH( z_9kV%4D_(TVtwP_EV2iiZmfaa8lPt*QE%Kie#z^$sCs=HjYvcMJ?208`M_F-t(0SP z0C9IN<0jh%ygDS|h3?C*TPzns=mPM;RhX#7nvVxPyaf?fDwTI1&CX=`=g94lJtVm+ z*@7kuTDDuTFi84(4b|jSFsk3#&p*QZOAJxhy|aq+D`R&DOmDIIi_*;H6G%sbfYD`H3Jp zL=1CWUi7dCtu|PDW?21oSlMh`weBJ{u{RDTGsO0+&G^b4GyX3ipMz~Sp z0a%0joLu$;!|8P`^i+$BS#a3nbdtk$xe1(}L%8E(5tsD}a-#NM5W_cArR_Gmv*IL% z7AOd^&emYK#!HW`z?1)e6_JZarc!H?5P&C=4D+5;_;b}&@UUF=vyyyA2a@{_-%;U* zdzu$V!(r-Xxrv*e(EUBt3i386^%uHeV~_E;8fVZobq4q&5$;!AO=5#ojw%sfkF-_4OU(s+^M;H*+uIi)Pf=vMq7mR zcA8#}O`ES^7dpDk@t7R32e&b1vUK0#hp5s_WO+Cn&NMepE88dLzH)Igkx^u2FkhR^ z$FpO>?TB7XPMpBN*2M#opG=1uJ2cCyg7u*>B5M741T$N!VAj>pI248vpY| z$?-=L`B&dFcX2Mrtww@*b~qRg4vkbF47g{yEHPpjn0DmUEuO;Xd|?2DEP%S5k7MqjQ*00Z2v79@&cxIGV}7DY$8ACDMk?ozgnXU8zy**unWvoEISl zqaEHBc*{8EwF`eKp1fZUa~K5{m8(2)%#>s=(jA!`n&k7weYS0)tJ3OwTTpLhC`>&2 zBFLtf2zLa%E$Qt4FXwGo6+BOVD7gA>Lxr2fvE}6H#nLb+r+jV%v#j8Jt;T1|mGck< zl6Bp?aymvounE@t;(lh|^=A#I-V!DDeRtNJ)^;J|--Kz)PBYyEi;g}ObD174%xTOf z++-i_HeWHsa8fCwuO01oh+ok(rkz0;wrWU~@+Iv98|QIataBFqdQooq@^+188+FK@ zGF_9cPMigZ)h~RQ$rQ)mcHd!~xd{#YIyeuWNqrC)9P#H!z&!ksQ21|xjX`h zQbQa9iZ!{EkNZ0i3v(iFQ{TGH6B6G+A{|X|oKS4-WK8sPNsjcjme#bS;t6B5;k=NT z`gqETR2T-HDiv;`PLLcISL&YvO~k;0+?!A6YI;da{WB>rR*5hk;(lqcJ(#Y{B1oy%80e@{mM1Cu5}ZU9o2qvIR1P!|2ir+z&UG*HewRS9 z^h0eZj9-Ps^B`;0MHe9#KG>TJYAQfbC-CR2%-v+>G|1&W7oWgKBdv4!yCbC7OqH8bK%bQK+3kC7%ZL3&%(eTMu?7CMr(!I>ctJ(8ny1xv7>a_iSfDp? zK*IYX2_p;gIYqB_-8Glb+ES(_zC~&0xle!>Ov0Tx57K%kNX#^d(KKq<__)3AW zj3lnRqS3SNmrj$mByf~91V0%%NOrdY2>BVkB=ls3gY=`3D@%(dOl-HnO7by>7ktUP zTR!+i*q7xE)98wNm8<)dTCZ+^G+PUR(S^Vus)1L!p~Pphx1A{cNMZqLssvcs{bTT4 zS;ML%*9&YK>=loYi+L01FIlS0Jzomqf04YwH12^k&BIUS>!hPH%?}K_zjs}HUKSQq z1n~DQ(;8AYkA~7T@rV79dq>zZ47!GGX@Gq_P@%&$eH0~Y=?f{a3o4XMn6FMfMk3T6O1n{{>z8;gxUlbj6P=FCu?j{Bv28A$jca^{V{8QJ@mT3ue z6pfUknus{%OYMt;w(l|U?DiePzQR*wYxji*ts}2VC6>F^RI@@m< zfbny>zHF3f&+OXeX&2K*+GZ6N4cm-!*IqI~HsBXf4(r3B zZD{4U^|>9|M1uDD#5N>FqMyfrg-bSd=wkof4465`lmU;ZJ*iK`TL9ze-rkdvr|d)u z7Ed?`(4G<+zS9BGw3#du8YCgKkow}JR6??U0S{@vb4B5{EOi6C1Qx<*CvV)qYk!y1 z+#GeNykSVS>^j?@6QvyX1Z~P{j2>f zyfO4!EdyYI&6CmOJGSxTnJ#sSB8sD{ERYulBtcnyPz;QFPJ$%2@4`2gx;8%Q@p3YD zX=LnQj`Z>dEz4*kj09Gf%qN5-q5!C*DYY${gq7hvAMWq3-2r%6->T zk6kGfjh=OBy+UFFwaaTEu|{1cIHS6xTO-<(76fpXCIReUM}=2)a{itpSqF8sPT*6P{zYIuIS}|r6d{cp+zUl1ea;H){X<9CX8eqKTD!$o;n zb0#UWD)i=0oWsYuOj(r22VddaGN-`0xMNO(xGbl}hdAB^Mr>|KLI-c6p5F_Ff*1ZD z-%~hZA^u8g*8Z__P9s$}ygmT}zwH+$=x3_63q_wngbWopTgP4$32qb8ttv!I8{sK} z%Mt<6$!Ck6RdV-UyP{Tks3Yn+vbCnuEpECFuK3c`%m)g%b(eB@i>JJ{Ft(Ww+doxV zvHI8ETgHwsgJ(cG`5ndU0oxF7ON7lc#R_|%ct#kTi&wfcvi(_K{pGu$_czt2SaSya zl-`zDDj$yH+0>z`kNgdvR^aY^#C-yBWy?{1is8QtA=CabJV@8;Qtc-yxtm^$N-R~r zt-tH%m9vh?*};XKC9-7aUUFMTuJnpv8ckvnA%;7Uw9|sH4{r9^77v^tZEr6 zMF?B9k;d2XIR|3to8KvX5>U__@x10tw@Yvxb5Ow0Uj`ML#artpR#`p>r;!qPWBxZe zZw`)Tb3&K;ON>%ZB#UZ?e>58wC#}m$jBDtA9N`x>(7SLlFj7ji2$QgIt)BXAW52$$ z<=JArC_Xbv>5?E>wlBX`Ra+#lZ1h{ecvU4)WJ**~CqK;}%JLtJYS*ar24W_(Z&p`F zu!x&eZc$(S`1d9-K>GHHW7?&pq7Vm`1``dmanAH^Pm0HrTk98EMaT%XGvfoai&Fh~ zwgc!Y&*V9#h0n$fZ5EeiVkswR>Y_b9o?I*TvAX{F*Y)wYKggFbGzR4!+&jF?LtZ%2 zA6thxfx3I#%oFzHjv^#RfujCTGOXUznX46$0X7e>1X89iMW=u3Eb;>4T7S*{*n3_J@_@Zp|ap6iAa!aI@ohI0)=6SJ8E)vZdlGQ3W6x?Ns~4?y-M{AS+V zQ>Qltl-Sh7PwG~Wr9+&{AmTL0UU<{o7L+emvwlppP=4cC!>e#`>UC+!hf0R(;bf>X zoP*#UI7;ARMNl(($$cX%*VPvdxgyB+)b3N%*B`u)L*4g`_vtc=7ehF4x}|lm4VO|a zMdzdzvn+OAV%vkPT2$2(@Vm|RbAeAvU@GC1_%zSak7&7~j2G>V@M+#nDzrZ;w)lQ` z)6LpQI1zrjC!jZ~^-YUF#k`OLL)N&X5N(*xw#k<#h0G%e+ioHo*si4~Bij!f&K(7G z7(Cf#A){`WEy){|Y!+`g-G+C)qzgzlZup$z$GBsl>FvK-*HU*Z-kx?q#4*S|99iOH zPCGB;J4)@6kd?4hGVd%%mhSe;QplD8@;=Vzk+U`Vt3CIT?v#E!!7o@KL2L|x}0Rsm1lfPyq0!FXB_C5cRjyqav`l?OJ Hwy*vN4=Ee6 literal 0 HcmV?d00001 From 9ecbe7861c975f52176d2bba809310794d8a0592 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Mon, 8 Dec 2025 22:21:05 +0100 Subject: [PATCH 339/363] chore: comment out api docs metadata generation and toc node for this --- docs/docfx.json | 65 +++++++++++++++++++++++-------------------------- docs/toc.yml | 4 +-- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/docs/docfx.json b/docs/docfx.json index 2ab3ec39..d8fa24b3 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -1,28 +1,27 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", - "metadata": [ - { - "src": [ - { - "files": [ - "../src/Mapster/Mapster.sln" - ], - "src": "../src" - } - ], - "includeExplicitInterfaceImplementations": false, - "disableDefaultFilter": false, - // "filter": "config/filterConfig.yml", - "properties": { - "TargetFramework" : "net9.0" - }, - "codeSourceBasePath": "../", - "noRestore": true, - "output": "api", - "memberLayout": "samePage", - "outputFormat": "apiPage" - } - ], + // "metadata": [ + // { + // "src": [ + // { + // "files": [ + // "**/Mapster.sln" + // ], + // "src": "../src" + // } + // ], + // "includeExplicitInterfaceImplementations": false, + // "disableDefaultFilter": false, + // // "filter": "config/filterConfig.yml", + // "properties": { + // "TargetFramework" : "net9.0" + // }, + // "noRestore": false, + // "output": "api", + // "memberLayout": "samePage", + // "outputFormat": "apiPage" + // } + // ], "build": { "globalMetadata": { "_appTitle": "Mapster - The Mapper of Your Domain", @@ -45,12 +44,12 @@ "_layout": "landing" }, "content": [ - { - "files": [ - "api/**.yml", - "api/index.md" - ] - }, + // { + // "files": [ + // "api/**.yml", + // "api/index.md" + // ] + // }, { "files": [ "articles/**.{md,yml}", @@ -61,10 +60,6 @@ "exclude": [ "**/.include/**" ] - }, - { - "rootTocPath": "releases", - "files":["releases/**.{md}"] } ], "resource": [ @@ -79,8 +74,8 @@ "overwrite": [ { "files":[ - "api/**.md", - "api/**.yml", + // "api/**.md", + // "api/**.yml", "articles/**.md", "articles/**.yml", "index.md" diff --git a/docs/toc.yml b/docs/toc.yml index af01d451..2139110e 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -6,5 +6,5 @@ href: index.md - name: Documentation href: articles/ -- name: API Reference - href: api/ +# - name: API Reference +# href: api/ From ced7ee03bb9b7a6722f54b93adc450153d6fe641 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Tue, 9 Dec 2025 15:42:57 +0100 Subject: [PATCH 340/363] docs: intendations, sorting TOC and re-add temp Reference --- docs/{articles/Home.md => api/Reference.md} | 75 +++++++++++--------- docs/articles/_Sidebar.md | 18 ++--- docs/articles/configuration/Configuration.md | 19 ++--- docs/articles/settings/toc.yml | 4 +- docs/articles/toc.yml | 32 ++++----- docs/articles/tools/toc.yml | 1 - 6 files changed, 77 insertions(+), 72 deletions(-) rename docs/{articles/Home.md => api/Reference.md} (75%) diff --git a/docs/articles/Home.md b/docs/api/Reference.md similarity index 75% rename from docs/articles/Home.md rename to docs/api/Reference.md index 6f8ee624..9626ce24 100644 --- a/docs/articles/Home.md +++ b/docs/api/Reference.md @@ -1,9 +1,13 @@ +--- +uid: Mapster.References +--- + # References ## Basic -| Method | Description | Link | -| ------------- |-----------------------| ----- | +| Method | Description | Link | +| -------------|-----------------------| ----- | | `src.Adapt()` | Mapping to new type | [basic](xref:Mapster.Mapping.BasicUsages) | | `src.Adapt(dest)` | Mapping to existing object | [basic](xref:Mapster.Mapping.BasicUsages) | | `query.ProjectToType()` | Mapping from queryable | [basic](xref:Mapster.Mapping.BasicUsages) | @@ -11,10 +15,10 @@ ### Mapper instance (for dependency injection) -| Method | Description | Link | -| ------------- |-----------------------| ----- | -| `IMapper mapper = new Mapper()` | Create mapper instance | [mappers](xref:Mapster.Mapping.Mappers) | -| `mapper.Map(src)` | Mapping to new type | | +| Method | Description | Link | +| -------------|----------------------| ----- | +| `IMapper mapper = new Mapper()`| Create mapper instance | [mappers](xref:Mapster.Mapping.Mappers) | +| `mapper.Map(src)` | Mapping to new type | | | `mapper.Map(src, dest)` | Mapping to existing object | | ### Builder (for complex mapping) @@ -30,34 +34,35 @@ | `.CreateMapToTargetExpression()` | Get mapping to existing object expression | | | `.CreateProjectionExpression()` | Get mapping from queryable expression | | -#### Config - -| Method | Description | Link | -| ------------- |-----------------------| ----- | -| `TypeAdapterConfig.GlobalSettings` | Global config | [config](xref:Mapster.Configuration.Overview) | -| `var config = new TypeAdapterConfig()` | Create new config instance | [config instance](xref:Mapster.Configuration.Instance) | -| `src.Adapt(config)` | Passing config to mapping | | -| `new Mapper(config)` | Passing config to mapper instance | | -| `src.BuildAdapter(config)` | Passing config to builder | | -| `config.RequireDestinationMemberSource` | Validate all properties are mapped | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | -| `config.RequireExplicitMapping` | Validate all type pairs are defined | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | +## Config + +| Method | Description | Link | +|----------------------------------------------|-----------------------------------------| ----- | +| `TypeAdapterConfig.GlobalSettings` | Global config | [config](xref:Mapster.Configuration.Overview) | +| `var config = new TypeAdapterConfig()` | Create new config instance | [config instance](xref:Mapster.Configuration.Instance) | +| `src.Adapt(config)` | Passing config to mapping | | +| `new Mapper(config)` | Passing config to mapper instance | | +| `src.BuildAdapter(config)` | Passing config to builder | | +| `config.RequireDestinationMemberSource` | Validate all properties are mapped | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | +| `config.RequireExplicitMapping` | Validate all type pairs are defined | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | | `config.AllowImplicitDestinationInheritance` | Use config from destination based class | [inheritance](xref:Mapster.Configuration.Inheritance) | -| `config.AllowImplicitSourceInheritance` | Use config from source based class | [inheritance](xref:Mapster.Configuration.Inheritance) | -| `config.SelfContainedCodeGeneration` | Generate all nested mapping in 1 method | [TextTemplate](xref:Mapster.Tools.TextTemplate) | -| `config.Compile()` | Validate mapping instruction & cache | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | -| `config.CompileProjection()` | Validate mapping instruction & cache for queryable | | -| `config.Clone()` | Copy config | [config instance](xref:Mapster.Configuration.Instance) | -| `config.Fork(forked => ...)` | Inline configuration | [config location](xref:Mapster.Configuration.Location) | - -#### Config scanning - -| Method | Description | Link | -| ------------- |-----------------------| ----- | -| `IRegister` | Interface for config scanning | [config location](xref:Mapster.Configuration.Location) | +| `config.AllowImplicitSourceInheritance` | Use config from source based class | [inheritance](xref:Mapster.Configuration.Inheritance) | +| `config.SelfContainedCodeGeneration` | Generate all nested mapping in 1 method | [TextTemplate](xref:Mapster.Tools.TextTemplate) | +| `config.Compile()` | Validate mapping instruction & cache | [config validation](xref:Mapster.Configuration.ValidationAndCompilation) | +| `config.CompileProjection()` | Validate mapping instruction & cache for queryable | | +| `config.Clone()` | Copy config | [config instance](xref:Mapster.Configuration.Instance) | +| `config.Fork(forked => ...)` | Inline configuration | [config location](xref:Mapster.Configuration.Location) | + +### Config scanning + +| Method | Description | Link | +|------------------------------|-------------------------------| ----- | +| `IRegister` | Interface for config scanning | [config location](xref:Mapster.Configuration.Location) | | `config.Scan(...assemblies)` | Scan for config in assemblies | [config location](xref:Mapster.Configuration.Location) | -| `config.Apply(...registers)` | Apply registers directly | [config location](xref:Mapster.Configuration.Location) | +| `config.Apply(...registers)` | Apply registers directly | [config location](xref:Mapster.Configuration.Location) | -#### Declare settings + +### Declare settings | Method | Description | Link | | ------------- |-----------------------| ----- | @@ -69,7 +74,7 @@ | `config.ForDestinationType()` | Get setting that applied to specific destination type | [config](xref:Mapster.Configuration.Overview) | | | Configuration for nested mapping | [nested mapping](xref:Mapster.Configuration.NestedMapping) | -#### Settings +## Settings | Method | Description | Apply to queryable | Link | | ------------- |-----------------------| ------------ | ----- | @@ -104,7 +109,7 @@ | `Unflattening` | Allow unflatten mapping | x |[2-ways & unflattening](xref:Mapster.Settings.Custom.TwoWaysMapping) | | `UseDestinationValue` | Use existing property object to map data | |[readonly-prop](xref:Mapster.Settings.Custom.ReadonlyProperty) | -#### Attributes +## Attributes | Annotation | Description | Link | | ------------- |-----------------------| ----- | @@ -114,7 +119,7 @@ | `[AdaptTo]` `[AdaptFrom]` `[AdaptTwoWays]` | Add setting on POCO class | [location](xref:Mapster.Configuration.Location#attributes) | | `[Mapper]` `[GenerateMapper]` `[PropertyType]` | Define setting for code generation | [Mapster.Tool](xref:Mapster.Tools.MapsterTool.Overview) | -#### Packages +## Packages | Packages | Method | Description | | ------ | ------------- |-----------------------| @@ -126,7 +131,7 @@ | [Immutable](xref:Mapster.Packages.Immutable) | `config.EnableImmutableMapping()` | mapping to immutable collection | | [Json.net](xref:Mapster.Packages.JsonNet) | `config.EnableJsonMapping()` | map json from/to poco and string | -#### Code Generation Tools +## Code Generation Tools | Plugin | Tool | Description | | ------ | ------------- |-----------------------| diff --git a/docs/articles/_Sidebar.md b/docs/articles/_Sidebar.md index bf59116d..22c58675 100644 --- a/docs/articles/_Sidebar.md +++ b/docs/articles/_Sidebar.md @@ -20,7 +20,14 @@ ## Settings -* Custom member matching logic +* [Constructor mapping](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) +* [Before & after mapping](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) +* [Setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) +* [Shallow & merge mapping](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) +* [Recursive & object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) +* [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) +* [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) +* **Custom member matching logic** * [Custom mapping](https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping) * [Custom naming convention](https://github.com/MapsterMapper/Mapster/wiki/Naming-convention) * [Setting by attributes](https://github.com/MapsterMapper/Mapster/wiki/Setting-by-attributes) @@ -29,13 +36,6 @@ * [Mapping readonly prop](https://github.com/MapsterMapper/Mapster/wiki/Mapping-readonly-prop) * [Mapping non-public members](https://github.com/MapsterMapper/Mapster/wiki/Mapping-non-public-members) * [Two ways & unflattening mapping](https://github.com/MapsterMapper/Mapster/wiki/Two-ways) -* [Constructor mapping](https://github.com/MapsterMapper/Mapster/wiki/Constructor-mapping) -* [Before & after mapping](https://github.com/MapsterMapper/Mapster/wiki/Before-after-mapping) -* [Setting values](https://github.com/MapsterMapper/Mapster/wiki/Setting-values) -* [Shallow & merge mapping](https://github.com/MapsterMapper/Mapster/wiki/Shallow-merge) -* [Recursive & object references](https://github.com/MapsterMapper/Mapster/wiki/Object-references) -* [Custom conversion logic](https://github.com/MapsterMapper/Mapster/wiki/Custom-conversion-logic) -* [Inheritance](https://github.com/MapsterMapper/Mapster/wiki/Config-inheritance) ## Plugins @@ -49,8 +49,8 @@ ## Tools +* [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) * [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) * [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation) * [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation) * [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation) -* [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) diff --git a/docs/articles/configuration/Configuration.md b/docs/articles/configuration/Configuration.md index 3fb21f0a..34a30549 100644 --- a/docs/articles/configuration/Configuration.md +++ b/docs/articles/configuration/Configuration.md @@ -2,6 +2,7 @@ uid: Mapster.Configuration.Overview title: "Configuration - Overview" --- + ## Setting per type pair @@ -13,17 +14,17 @@ TypeAdapterConfig .NewConfig() .Ignore(dest => dest.Age) .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); + src => string.Format("{0} {1}", src.FirstName, src.LastName)); ``` As an alternative to `NewConfig`, you can use `ForType` in the same way: ```csharp TypeAdapterConfig - .ForType() - .Ignore(dest => dest.Age) - .Map(dest => dest.FullName, - src => string.Format("{0} {1}", src.FirstName, src.LastName)); + .ForType() + .Ignore(dest => dest.Age) + .Map(dest => dest.FullName, + src => string.Format("{0} {1}", src.FirstName, src.LastName)); ``` `ForType` differs in that it will create a new mapping if one doesn't exist, but if the specified TSource => TDestination @@ -50,14 +51,14 @@ In the example below, when any source type and destination type are the same, we ```csharp TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType) - .Ignore("Id"); + .Ignore("Id"); ``` In this example, the config would only apply to Query Expressions (projections). ```csharp TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection) - .IgnoreAttribute(typeof(NotMapAttribute)); + .IgnoreAttribute(typeof(NotMapAttribute)); ``` ## Destination type only @@ -66,7 +67,7 @@ A setting can also be created without knowing the source type, by using `ForDest ```csharp TypeAdapterConfig.GlobalSettings.ForDestinationType() - .AfterMapping(dest => dest.Validate()); + .AfterMapping(dest => dest.Validate()); ``` NOTE: `ForDestinationType` above will always apply to all types assignable to `IValidator`. If destination class implements `IValidator`, it will also apply the `AfterMapping` config. @@ -77,5 +78,5 @@ If the mapping type is generic, you can create a setting by passing generic type ```csharp TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) - .Map("value", "Value"); + .Map("value", "Value"); ``` diff --git a/docs/articles/settings/toc.yml b/docs/articles/settings/toc.yml index fab3d549..2d4a0ea4 100644 --- a/docs/articles/settings/toc.yml +++ b/docs/articles/settings/toc.yml @@ -1,6 +1,4 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json -- name: Custom member matching logic - href: custom/toc.yml - name: Constructor mapping uid: Mapster.Settings.ConstructorMapping href: Constructor-mapping.md @@ -19,3 +17,5 @@ - name: Custom conversion logic uid: Mapster.Settings.CustomConversionLogic href: Custom-conversion-logic.md +- name: Custom member matching logic + href: custom/toc.yml \ No newline at end of file diff --git a/docs/articles/toc.yml b/docs/articles/toc.yml index 2d874dda..245cfb26 100644 --- a/docs/articles/toc.yml +++ b/docs/articles/toc.yml @@ -1,18 +1,18 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json # NOTE: starting from here you can add side navigation items by nesting them under "items" -- items: - - name: Mapping - href: mapping/toc.yml - topicHref: xref:Mapster.Mapping.BasicUsages - - name: Configuration - href: configuration/toc.yml - topicHref: xref:Mapster.Configuration.Overview - - name: Settings - href: settings/toc.yml - topicHref: xref:Mapster.Settings.Custom.Mapping - - name: Packages - href: packages/toc.yml - topicHref: xref:Mapster.Packages.Async - - name: Tools - href: tools/toc.yml - topicHref: xref:Mapster.Tools.MapsterTool.Overview \ No newline at end of file +items: +- name: Mapping + href: mapping/toc.yml + topicHref: xref:Mapster.Mapping.BasicUsages +- name: Configuration + href: configuration/toc.yml + topicHref: xref:Mapster.Configuration.Overview +- name: Settings + href: settings/toc.yml + topicHref: xref:Mapster.Settings.Custom.Mapping +- name: Packages + href: packages/toc.yml + topicHref: xref:Mapster.Packages.Async +- name: Tools + href: tools/toc.yml + topicHref: xref:Mapster.Tools.MapsterTool.Overview \ No newline at end of file diff --git a/docs/articles/tools/toc.yml b/docs/articles/tools/toc.yml index a206bd9a..88d5ca38 100644 --- a/docs/articles/tools/toc.yml +++ b/docs/articles/tools/toc.yml @@ -1,5 +1,4 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/docfx/main/schemas/toc.schema.json -# TODO: Decide if we want Tools to have a topicHref items: - name: Mapster Tool topicHref: xref:Mapster.Tools.MapsterTool.Overview From fe099c1cece2d7a2c0ef99e4c64f2fe53ed31beb Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Tue, 9 Dec 2025 15:59:27 +0100 Subject: [PATCH 341/363] chore: Match paths, add Reference TOC entry, add yaml header to index.md to enable xref referencing --- docs/docfx.json | 56 ++++++++++++++++++++++++------------------------- docs/index.md | 4 ++++ docs/toc.yml | 6 ++++-- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/docs/docfx.json b/docs/docfx.json index d8fa24b3..9099d79f 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -26,8 +26,8 @@ "globalMetadata": { "_appTitle": "Mapster - The Mapper of Your Domain", "_appName": "Mapster", - //"_appFaviconPath": "images/mapster-logo.png", - "_appLogoPath": "images/mapster-logo.png", + //"_appFaviconPath": "images/mapster-logo.png", // requires rescaling and eventually converting to .ico + // "_appLogoPath": "images/mapster-logo.png", // requires rescaling! "_copyrightFooter": "© 2025 Here comes your Name, All rights reserved.", "_ownerName": "Awesome Developer", "_githubProfileLink": "https://github.com/MapsterMapper", @@ -36,29 +36,42 @@ "_disableBreadcrumb": false, "_disableNextArticle": false, "_disableTocFilter": false, + "_disableToc": false, "_lang": "en", "_gitConribute": { "branch": "development" }, - "pdf": false, - "_layout": "landing" + "pdf": false }, "content": [ - // { - // "files": [ - // "api/**.yml", - // "api/index.md" - // ] - // }, { "files": [ - "articles/**.{md,yml}", + "articles/**.md", + "articles/toc.yml", + "articles/**/toc.yml", "toc.yml", - "index.md", + "index.md" + ], + "exclude": [ + "**/.include/**", + "articles/_Sidebar.md" + ] + }, + { + "src": "../", + "files": [ "README.md" + ] + }, + { + "files": [ + + "api/Reference.md" ], + // Include the following lines, if we are generating metadata API Docs "exclude": [ - "**/.include/**" + "api/**.yml", + "index.md" ] } ], @@ -71,21 +84,6 @@ "dot": true } ], - "overwrite": [ - { - "files":[ - // "api/**.md", - // "api/**.yml", - "articles/**.md", - "articles/**.yml", - "index.md" - ], - "exclude":[ - "obj/**", - "_site/**" - ] - } - ], "template": [ "default", "modern" @@ -105,7 +103,7 @@ }, "output": "_site", "sitemap": { - "baseUrl": "https://mapstermapper.github.io/Mapster", + "baseUrl": "https://devtkss.github.io/Mapster", "priority": 0.1, "changefreq": "monthly" } diff --git a/docs/index.md b/docs/index.md index 53c65a35..3e38bb07 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,2 +1,6 @@ +--- +uid: Mapster.Overview +_layout: landing +--- [!INCLUDE [landing-page](../README.md)] diff --git a/docs/toc.yml b/docs/toc.yml index 2139110e..b42c2d45 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -3,8 +3,10 @@ # IMPORTANT: Do not add the explicit toc.yml files under the appropriate folders here, as this would otherwhise attempt to show them in the top navigation instead of the side navigation TOCs. # See: https://dotnet.github.io/docfx/docs/table-of-contents.html#navigation-bar - name: Home + uid: Mapster.Overview href: index.md - name: Documentation href: articles/ -# - name: API Reference -# href: api/ +- name: Reference + uid: Mapster.References + href: api/Reference.md From 65927c4bd34b467a0afa550f69fa6393f8876885 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Tue, 9 Dec 2025 16:00:51 +0100 Subject: [PATCH 342/363] chore: Introduce .vscode settings file associations for docfx as jsonc and add recommended extensions --- .gitignore | 1 - {.settings => .vscode}/extensions.json | 4 +++- .vscode/settings.json | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) rename {.settings => .vscode}/extensions.json (64%) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 336ce443..434416f4 100644 --- a/.gitignore +++ b/.gitignore @@ -187,5 +187,4 @@ src/.idea # VS Code settings .vscode/launch.json -.vscode/settings.json .vscode/tasks.json diff --git a/.settings/extensions.json b/.vscode/extensions.json similarity index 64% rename from .settings/extensions.json rename to .vscode/extensions.json index 0e96c08b..a22d4740 100644 --- a/.settings/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,8 @@ { "recommendations": [ "DavidAnson.vscode-markdownlint", - "streetsidesoftware.code-spell-checker-cspell-bundled-dictionaries" + "streetsidesoftware.code-spell-checker-cspell-bundled-dictionaries", + "ms-vscode.powershell", + "joshbolduc.commitlint" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..781f0a8d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "*.agent.md": "chatagent", + "*.instructions.md": "instructions", + "*.prompt.md": "prompt", + "docfx.json": "jsonc" + } +} \ No newline at end of file From 28812a5651d0e250b5039688545f3e1dc17a307c Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Tue, 9 Dec 2025 16:15:30 +0100 Subject: [PATCH 343/363] docs(Reference): Add agent prompts for csharp xml doc generation see source: https://github.com/github/awesome-copilot --- .github/prompts/csharp-docs.prompt.md | 63 +++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/prompts/csharp-docs.prompt.md diff --git a/.github/prompts/csharp-docs.prompt.md b/.github/prompts/csharp-docs.prompt.md new file mode 100644 index 00000000..23687706 --- /dev/null +++ b/.github/prompts/csharp-docs.prompt.md @@ -0,0 +1,63 @@ +--- +agent: 'agent' +tools: ['changes', 'search/codebase', 'edit/editFiles', 'problems'] +description: 'Ensure that C# types are documented with XML comments and follow best practices for documentation.' +--- + +# C# Documentation Best Practices + +- Public members should be documented with XML comments. +- It is encouraged to document internal members as well, especially if they are complex or not self-explanatory. + +## Guidance for all APIs + +- Use `` to provide a brief, one sentence, description of what the type or member does. Start the summary with a present-tense, third-person verb. +- Use `` for additional information, which can include implementation details, usage notes, or any other relevant context. +- Use `` for language-specific keywords like `null`, `true`, `false`, `int`, `bool`, etc. +- Use `` for inline code snippets. +- Use `` for usage examples on how to use the member. + - Use `` for code blocks. `` tags should be placed within an `` tag. Add the language of the code example using the `language` attribute, for example, ``. +- Use `` to reference other types or members inline (in a sentence). +- Use `` for standalone (not in a sentence) references to other types or members in the "See also" section of the online docs. +- Use `` to inherit documentation from base classes or interfaces. + - Unless there is major behavior change, in which case you should document the differences. + +## Methods + +- Use `` to describe method parameters. + - The description should be a noun phrase that doesn't specify the data type. + - Begin with an introductory article. + - If the parameter is a flag enum, start the description with "A bitwise combination of the enumeration values that specifies...". + - If the parameter is a non-flag enum, start the description with "One of the enumeration values that specifies...". + - If the parameter is a Boolean, the wording should be of the form "`` to ...; otherwise, ``.". + - If the parameter is an "out" parameter, the wording should be of the form "When this method returns, contains .... This parameter is treated as uninitialized.". +- Use `` to reference parameter names in documentation. +- Use `` to describe type parameters in generic types or methods. +- Use `` to reference type parameters in documentation. +- Use `` to describe what the method returns. + - The description should be a noun phrase that doesn't specify the data type. + - Begin with an introductory article. + - If the return type is Boolean, the wording should be of the form "`` if ...; otherwise, ``.". + +## Constructors + +- The summary wording should be "Initializes a new instance of the class [or struct].". + +## Properties + +- The `` should start with: + - "Gets or sets..." for a read-write property. + - "Gets..." for a read-only property. + - "Gets [or sets] a value that indicates whether..." for properties that return a Boolean value. +- Use `` to describe the value of the property. + - The description should be a noun phrase that doesn't specify the data type. + - If the property has a default value, add it in a separate sentence, for example, "The default is ``". + - If the value type is Boolean, the wording should be of the form "`` if ...; otherwise, ``. The default is ...". + +## Exceptions + +- Use `` to document exceptions thrown by constructors, properties, indexers, methods, operators, and events. +- Document all exceptions thrown directly by the member. +- For exceptions thrown by nested members, document only the exceptions users are most likely to encounter. +- The description of the exception describes the condition under which it's thrown. + - Omit "Thrown if ..." or "If ..." at the beginning of the sentence. Just state the condition directly, for example "An error occurred when accessing a Message Queuing API." From 4b21f6eeed1c2ff251eed738fc6c22e8714ba49d Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Tue, 9 Dec 2025 16:16:13 +0100 Subject: [PATCH 344/363] ci(docs): Add docs deployment workflow --- .github/workflows/build-deploy-docs.yml | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/workflows/build-deploy-docs.yml diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml new file mode 100644 index 00000000..35eb23f2 --- /dev/null +++ b/.github/workflows/build-deploy-docs.yml @@ -0,0 +1,77 @@ +name: Build and Deploy DocFX + +on: + push: + branches: [ 'master', 'docs/*' ] + paths: + - 'docs/**' + - 'README.md' + # Uncomment this if API docs changes should trigger a rebuild of the documentation + # - 'src/**' + - '.github/workflows/build-deploy-docs.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build-docs: + if: ${{ github.ref == 'refs/heads/master' }} + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.x + 9.x + # - name: Show dotnet version + # run: | + # dotnet --list-sdks + # dotnet --list-runtimes + + # - name: Restore dependencies before docs deployment as recommended by docfx + # run: dotnet restore ./src/Mapster.sln + + # - name: Build with dotnet + # run: dotnet build ./src/Mapster.sln + + # - name: Run tests on .NET 9.0 + # run: dotnet test --verbosity normal ./src/Mapster.sln + + - name: Install DocFX as .NET tool + run: | + dotnet tool update -g docfx + + - name: Build docfx site + working-directory: docs + run: docfx docfx.json + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: 'docs/_site' + + deploy-docs: + needs: build-docs + if: ${{ needs.build-docs.result == 'success' && github.ref == 'refs/heads/master' }} + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From 46422aae59615e50cf32894f08bf2a50fed1418c Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Tue, 9 Dec 2025 16:16:30 +0100 Subject: [PATCH 345/363] ci: Add conventional-commits workflow --- .github/workflows/conventional-commits.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/conventional-commits.yml diff --git a/.github/workflows/conventional-commits.yml b/.github/workflows/conventional-commits.yml new file mode 100644 index 00000000..77a0a8c8 --- /dev/null +++ b/.github/workflows/conventional-commits.yml @@ -0,0 +1,21 @@ +name: Conventional Commits + +on: + pull_request: + branches: + - master + - development + - release/* + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + commitsar: + name: Validate for conventional commits + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Run commitsar + uses: aevea/commitsar@v0.20.2 From 80bdf7d200d7466f397533f954e46f390eda8ff5 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Tue, 9 Dec 2025 16:17:08 +0100 Subject: [PATCH 346/363] chore: Add initial Contributing guidelines --- CONTRIBUTING.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..9c1f1fdc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Contributing to Mapster + +Thank you for your interest in contributing! We welcome contributions from the community. + +## How to Contribute + +1. **Fork the repository** and create your branch from [`development`](https://github.com/MapsterMapper/Mapster/tree/development) +2. **Make your changes** following the existing code style +3. **Write tests** using [xUnit](https://xunit.net/) to ensure your changes work correctly +4. **Document your code** with XML comments and update [docs/articles/](./docs/articles/) if needed following the [DocFX](https://dotnet.github.io/docfx/) guidelines +5. **Commit with clear messages** following [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) (e.g., `feat:`, `fix:`, `docs:`) +6. **Submit a pull request** with a description of your changes + +## Reporting Issues + +Found a bug or have a feature request? Please [open an issue](https://github.com/MapsterMapper/Mapster/issues) with: + +- Clear description of the problem or request +- Steps to reproduce (for bugs) +- Code samples if applicable +- Environment details (Mapster version, .NET version) + +For questions, use [GitHub Discussions](https://github.com/MapsterMapper/Mapster/discussions). + +## Development Guidelines + +- Follow existing code conventions +- Add XML documentation for public APIs +- Write unit tests for new features and bug fixes +- Keep code clean, well-documented, and tested + +## License + +By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE). From 8fd6442189c16f86effb41964f02d5a9a11222ee Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Tue, 9 Dec 2025 16:18:01 +0100 Subject: [PATCH 347/363] docs(automation): Add powershell scripts to simplify local docfx clean and build actions --- docs/Build-Docs.ps1 | 45 +++++++++++++++++++++++++++++++++++ docs/Clean-ApiDocs.ps1 | 45 +++++++++++++++++++++++++++++++++++ docs/Clean-and-Build-Docs.ps1 | 44 ++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 docs/Build-Docs.ps1 create mode 100644 docs/Clean-ApiDocs.ps1 create mode 100644 docs/Clean-and-Build-Docs.ps1 diff --git a/docs/Build-Docs.ps1 b/docs/Build-Docs.ps1 new file mode 100644 index 00000000..e6bc559c --- /dev/null +++ b/docs/Build-Docs.ps1 @@ -0,0 +1,45 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Builds DocFX documentation with clean API regeneration. + +.DESCRIPTION + 1. Cleans obsolete API documentation files + 2. Regenerates metadata from current source code + 3. Builds the documentation site + +.EXAMPLE + .\Build-Docs.ps1 + .\Build-Docs.ps1 -Verbose + .\Build-Docs.ps1 -serve $true -openBrowser $true +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateSet('Quiet', 'Info', 'Warning', 'Error', 'Verbose')] + [string]$LogLevel = 'Warning', + [bool]$serve = $false, + [bool]$openBrowser = $false +) + +Set-Location $PSScriptRoot + +# Step 1: Clean obsolete API docs +Write-Information "Cleaning obsolete API documentation..." -InformationAction Continue +& .\Clean-ApiDocs.ps1 + +# Step 2: Run DocFX (metadata + build + pdf in one command) +Write-Information "Running DocFX..." -InformationAction Continue +docfx docfx.json --logLevel $LogLevel --serve:$serve --open-browser:$openBrowser + +if ($LASTEXITCODE -ne 0) { + Write-Error "DocFX build failed" + exit $LASTEXITCODE +} + +Write-Host "✓ Documentation built successfully!" -ForegroundColor Green +Write-Debug "Output: $PSScriptRoot\_site" + +# Step 3: Reset to original location +Pop-Location \ No newline at end of file diff --git a/docs/Clean-ApiDocs.ps1 b/docs/Clean-ApiDocs.ps1 new file mode 100644 index 00000000..a60065cc --- /dev/null +++ b/docs/Clean-ApiDocs.ps1 @@ -0,0 +1,45 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Cleans obsolete API documentation files before regenerating with DocFX. + +.DESCRIPTION + Removes all generated YAML files from the api/ directory and the _site/ + output folder to ensure only current types are documented and built. + Preserves any manually-created markdown files like api/index.md. + +.EXAMPLE + .\Clean-ApiDocs.ps1 + .\Clean-ApiDocs.ps1 -Verbose +#> + +[CmdletBinding()] +param() + +$apiPath = Join-Path $PSScriptRoot "api" +$sitePath = Join-Path $PSScriptRoot "_site" +$isVerboseOutput = $PSBoundParameters.ContainsKey('Verbose').Equals($true) + +# Step 1: Clean API folder +if (Test-Path $apiPath) { + Write-Information "Cleaning API documentation folder..." -InformationAction Continue + + # Remove all .yml files (generated API docs) + Get-ChildItem -Path $apiPath -Filter "*.yml" -File | Remove-Item -Force -Verbose:$isVerboseOutput + + Write-Host "✓ Cleaned obsolete API documentation files" -ForegroundColor Green +} else { + Write-Debug "API folder does not exist yet: $apiPath" +} + +# Step 2: Clean _site folder +if (Test-Path $sitePath) { + Write-Information "Cleaning output site folder..." -InformationAction Continue + Remove-Item -Path $sitePath -Recurse -Force -Verbose:$isVerboseOutput + Write-Host "✓ Cleaned output site folder" -ForegroundColor Green +} else { + Write-Debug "Output site folder does not exist yet: $sitePath" +} + +# Step 3: Reset to original location +Pop-Location \ No newline at end of file diff --git a/docs/Clean-and-Build-Docs.ps1 b/docs/Clean-and-Build-Docs.ps1 new file mode 100644 index 00000000..53db82ff --- /dev/null +++ b/docs/Clean-and-Build-Docs.ps1 @@ -0,0 +1,44 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Full documentation build: Clean, regenerate, and build. + +.DESCRIPTION + 1. Cleans obsolete API documentation files (Clean-ApiDocs.ps1) + 2. Builds documentation using Build-Docs.ps1 + +.EXAMPLE + .\Build-Docs-Full.ps1 + .\Build-Docs-Full.ps1 -Verbose + .\Build-Docs-Full.ps1 -serve $true -openBrowser $true +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateSet('Quiet', 'Info', 'Warning', 'Error', 'Verbose')] + [string]$LogLevel = 'Warning' +) + +# Step 1: Set the current location +Set-Location $PSScriptRoot + +# Step 2: Clean obsolete API docs +Write-Information "Cleaning obsolete API documentation..." -InformationAction Continue +& .\Clean-ApiDocs.ps1 + +# Step 3: Build documentation +Write-Information "Building documentation..." -InformationAction Continue + +& .\Build-Docs.ps1 -LogLevel $LogLevel -serve $true -open-browser $true + +if ($LASTEXITCODE -ne 0) { + Write-Error "Documentation build failed" + exit $LASTEXITCODE +} + +Write-Information "✓ Full documentation build completed successfully!" -InformationAction Continue +Write-Debug "Output: $PSScriptRoot\_site" + +# Step 4: Reset to original location +Pop-Location \ No newline at end of file From e2d6f534aefafd3ebd4fddbf0b6aa39710b6a8af Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Thu, 11 Dec 2025 15:56:30 +0100 Subject: [PATCH 348/363] chore: add and reference new scaled Mapster logo --- CONTRIBUTING.md | 2 + README.md | 2 +- docs/articles/.assets/mapster-logo.svg | 88 ++++++++++++++++++++++++++ docs/docfx.json | 6 +- 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 docs/articles/.assets/mapster-logo.svg diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9c1f1fdc..bc05351f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,5 @@ +![Mapster Icon](https://raw.githubusercontent.com/MapsterMapper/Mapster/master/docs/articles/.assets/mapster-logo.svg) + # Contributing to Mapster Thank you for your interest in contributing! We welcome contributions from the community. diff --git a/README.md b/README.md index f043abf4..516fcdab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Mapster Icon](https://cloud.githubusercontent.com/assets/5763993/26522718/d16f3e42-4330-11e7-9b78-f8c7402624e7.png) +![Mapster Icon](https://raw.githubusercontent.com/MapsterMapper/Mapster/master/docs/articles/.assets/mapster-logo.svg) # Mapster - The Mapper of Your Domain diff --git a/docs/articles/.assets/mapster-logo.svg b/docs/articles/.assets/mapster-logo.svg new file mode 100644 index 00000000..ae2543fd --- /dev/null +++ b/docs/articles/.assets/mapster-logo.svg @@ -0,0 +1,88 @@ + + + + + + Layer 1 + + + + + + + + + + + + + diff --git a/docs/docfx.json b/docs/docfx.json index 9099d79f..5a4a9338 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -26,8 +26,8 @@ "globalMetadata": { "_appTitle": "Mapster - The Mapper of Your Domain", "_appName": "Mapster", - //"_appFaviconPath": "images/mapster-logo.png", // requires rescaling and eventually converting to .ico - // "_appLogoPath": "images/mapster-logo.png", // requires rescaling! + "_appFaviconPath": "images/mapster-logo.svg", + "_appLogoPath": "images/mapster-logo.svg", "_copyrightFooter": "© 2025 Here comes your Name, All rights reserved.", "_ownerName": "Awesome Developer", "_githubProfileLink": "https://github.com/MapsterMapper", @@ -103,7 +103,7 @@ }, "output": "_site", "sitemap": { - "baseUrl": "https://devtkss.github.io/Mapster", + "baseUrl": "https://mapstermapper.github.io/Mapster", "priority": 0.1, "changefreq": "monthly" } From 8b9dd1b73a684b022bd6307c33c94aaecb1f51d4 Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Thu, 11 Dec 2025 15:57:40 +0100 Subject: [PATCH 349/363] chore: update Testing Guidelines with MSTest --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc05351f..382ea80f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Thank you for your interest in contributing! We welcome contributions from the c 1. **Fork the repository** and create your branch from [`development`](https://github.com/MapsterMapper/Mapster/tree/development) 2. **Make your changes** following the existing code style -3. **Write tests** using [xUnit](https://xunit.net/) to ensure your changes work correctly +3. **Write tests** using [MSTest](https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest) and [xUnit](https://xunit.net/) to ensure your changes work correctly 4. **Document your code** with XML comments and update [docs/articles/](./docs/articles/) if needed following the [DocFX](https://dotnet.github.io/docfx/) guidelines 5. **Commit with clear messages** following [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) (e.g., `feat:`, `fix:`, `docs:`) 6. **Submit a pull request** with a description of your changes From 1f970d3703c3e6a8aa041bf0cb512bdeb2cc40ee Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Thu, 11 Dec 2025 15:58:40 +0100 Subject: [PATCH 350/363] chore(nuget): Add Table with all Mapster provided NuGet Packages --- README.md | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 516fcdab..b52d5690 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,29 @@ Writing mapping methods is a machine job. Do not waste your time, let Mapster do it. -[![NuGet](https://img.shields.io/nuget/v/Mapster.svg)](https://www.nuget.org/packages/Mapster/latest) +## NuGet Sources + +### NuGet Packages + +| Package | Stable | Pre-release | +|---------|--------|-------------| +| **Mapster** | [![Mapster](https://img.shields.io/nuget/v/Mapster.svg?label=Mapster&color=informational)](https://www.nuget.org/packages/Mapster/latest) | [![Mapster](https://img.shields.io/nuget/vpre/Mapster.svg?label=Mapster&color=orange)](https://www.nuget.org/packages/Mapster) | +| **Mapster.Core** | [![Mapster.Core](https://img.shields.io/nuget/v/Mapster.Core.svg?label=Mapster.Core&color=informational)](https://www.nuget.org/packages/Mapster.Core/latest) | [![Mapster.Core](https://img.shields.io/nuget/vpre/Mapster.Core.svg?label=Mapster.Core&color=orange)](https://www.nuget.org/packages/Mapster.Core) | +| **Mapster.DependencyInjection** | [![Mapster.DependencyInjection](https://img.shields.io/nuget/v/Mapster.DependencyInjection.svg?label=Mapster.DependencyInjection&color=informational)](https://www.nuget.org/packages/Mapster.DependencyInjection/latest) | [![Mapster.DependencyInjection](https://img.shields.io/nuget/vpre/Mapster.DependencyInjection.svg?label=Mapster.DependencyInjection&color=orange)](https://www.nuget.org/packages/Mapster.DependencyInjection) | +| **Mapster.EFCore** | [![Mapster.EFCore](https://img.shields.io/nuget/v/Mapster.EFCore.svg?label=Mapster.EFCore&color=informational)](https://www.nuget.org/packages/Mapster.EFCore/latest) | [![Mapster.EFCore](https://img.shields.io/nuget/vpre/Mapster.EFCore.svg?label=Mapster.EFCore&color=orange)](https://www.nuget.org/packages/Mapster.EFCore) | +| **Mapster.EF6** | [![Mapster.EF6](https://img.shields.io/nuget/v/Mapster.EF6.svg?label=Mapster.EF6&color=informational)](https://www.nuget.org/packages/Mapster.EF6/latest) | [![Mapster.EF6](https://img.shields.io/nuget/vpre/Mapster.EF6.svg?label=Mapster.EF6&color=orange)](https://www.nuget.org/packages/Mapster.EF6) | +| **Mapster.JsonNet** | [![Mapster.JsonNet](https://img.shields.io/nuget/v/Mapster.JsonNet.svg?label=Mapster.JsonNet&color=informational)](https://www.nuget.org/packages/Mapster.JsonNet/latest) | [![Mapster.JsonNet](https://img.shields.io/nuget/vpre/Mapster.JsonNet.svg?label=Mapster.JsonNet&color=orange)](https://www.nuget.org/packages/Mapster.JsonNet) | +| **Mapster.Immutable** | [![Mapster.Immutable](https://img.shields.io/nuget/v/Mapster.Immutable.svg?label=Mapster.Immutable&color=informational)](https://www.nuget.org/packages/Mapster.Immutable/latest) | [![Mapster.Immutable](https://img.shields.io/nuget/vpre/Mapster.Immutable.svg?label=Mapster.Immutable&color=orange)](https://www.nuget.org/packages/Mapster.Immutable) | +| **Mapster.Diagnostics** | [![Mapster.Diagnostics](https://img.shields.io/nuget/v/Mapster.Diagnostics.svg?label=Mapster.Diagnostics&color=informational)](https://www.nuget.org/packages/Mapster.Diagnostics/latest) | | +| **ExpressionDebugger** | [![ExpressionDebugger](https://img.shields.io/nuget/v/ExpressionDebugger.svg?label=ExpressionDebugger&color=informational)](https://www.nuget.org/packages/ExpressionDebugger/latest) | | + +### DotNet Tools + +| Tool | Stable | Pre-release | +|------|--------|-------------| +| **Mapster.Tool** | [![Mapster.Tool](https://img.shields.io/nuget/v/Mapster.Tool.svg?label=Mapster.Tool&color=informational)](https://www.nuget.org/packages/Mapster.Tool/latest) | [![Mapster.Tool](https://img.shields.io/nuget/vpre/Mapster.Tool.svg?label=Mapster.Tool&color=orange)](https://www.nuget.org/packages/Mapster.Tool) | + +_Badges zeigen die jeweils aktuellste Stable-Version und die aktuellste Pre-Release-Version._ ## Installation @@ -152,7 +174,7 @@ Code generation allows you to There are currently two tools which you can choose based on your preferences. -- [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) ***NEW!*** +- [Mapster.Tool](https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool) _**NEW!**_ - [TextTemplate](https://github.com/MapsterMapper/Mapster/wiki/TextTemplate) ## Change logs @@ -170,5 +192,3 @@ https://github.com/MapsterMapper/Mapster/releases - **Resharper** makes Visual Studio a much better IDE - **Rider** is fast & powerful cross platform .NET IDE - -![image](https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/JetBrains_Logo_2016.svg/121px-JetBrains_Logo_2016.svg.png) From 0ea68ea48dfd9dfdcbc1ec234b58963232f43d5e Mon Sep 17 00:00:00 2001 From: DevTKSS Date: Thu, 11 Dec 2025 17:09:57 +0100 Subject: [PATCH 351/363] chore: Update paths, add svg extension to docfx resources --- .github/workflows/build-deploy-docs.yml | 2 +- CONTRIBUTING.md | 2 +- README.md | 3 ++- docs/articles/.assets/step-into-debugging.png | Bin 0 -> 19976 bytes docs/articles/packages/ExpressionDebugging.md | 2 +- docs/docfx.json | 4 ++-- .../.assets => images}/mapster-logo.svg | 0 7 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 docs/articles/.assets/step-into-debugging.png rename docs/{articles/.assets => images}/mapster-logo.svg (100%) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 35eb23f2..84bd2a3d 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -2,7 +2,7 @@ name: Build and Deploy DocFX on: push: - branches: [ 'master', 'docs/*' ] + branches: [ 'master' ] paths: - 'docs/**' - 'README.md' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 382ea80f..b8db01f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -![Mapster Icon](https://raw.githubusercontent.com/MapsterMapper/Mapster/master/docs/articles/.assets/mapster-logo.svg) +![Mapster Icon](https://raw.githubusercontent.com/MapsterMapper/Mapster/master/docs/images/mapster-logo.svg) # Contributing to Mapster diff --git a/README.md b/README.md index b52d5690..208e2074 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Mapster Icon](https://raw.githubusercontent.com/MapsterMapper/Mapster/master/docs/articles/.assets/mapster-logo.svg) +![Mapster Icon](https://raw.githubusercontent.com/MapsterMapper/Mapster/master/docs/images/mapster-logo.svg) # Mapster - The Mapper of Your Domain @@ -161,6 +161,7 @@ And you could gain up to 12x faster performance with: ### Step into debugging [Step-into debugging](https://mapstermapper.github.io/docs/packages/Diagnostics.html) lets you debug your mapping and inspect values just like your code. + ![inspect-generated-source-code-while-debugging](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) ### Code Generation diff --git a/docs/articles/.assets/step-into-debugging.png b/docs/articles/.assets/step-into-debugging.png new file mode 100644 index 0000000000000000000000000000000000000000..5183ce54f8d9247c43c30ff4e758a8aeefdd3e0a GIT binary patch literal 19976 zcmZTwcUY3`_gAwl%dBi#jxsezYN@%xkt_Gs+?uJmb0-coE6Z)>p1Hw++td)tO3giS zV{X8$I8fmC&|AIl_xk&pC&;+M3D?^vv{BR8$Nq4;6H&sAx!3RMcF@ zj{%=BPrtXLqGG2~QIOMrPQ5b85Nj}$u|~`)Dr&d=QvJZbtoojza~gNs-m?_@28S0X zUOf*yFYq?B_vh!N8Of(08@ab{X;P%RZwA|a%@KquUy*;StCDNMBvAS-`{LV|wNgDo z-7E!xPp{{fGVF#kyi~|Neq5^*(IbuM36ikS8d$Vl8$g$Rk@jDyL0RCcY75Esz&nYx zF()NYN0lT)HKTf~xC(|kN_$06* zjIv^~M;#AcCFOhn=LwH!Xh~+r|MS7ABO#Gc>D3(p@WQ=ayE&BU>T_uii@P`e(!?Gt z>qqI^CJ!mLuT{bFQ~sG}YwXES!im$D$McuijTuanj?wCc*vp9)dYyy}zQ{2M0wI zOG{6dbnWbvVppe%YinDpy)Z{GXiW?9T8pGvo$Rhfm7dn8l_~$+)nwoLycVCC@vAjY zzbpB^wk8qEEk5oF#Eeo$zTP*G+X8O#*Ug7KIO-9-CnZOgXC&5KSYi`jkG`;+#*{jZ zCQ6l@BNF=uuP~%Ex}_ErtXdXJ2`AkC!!F9tZ-vNqH=`rB$xF&wO9NUmHJXsaJ$KxZ z;87tC(kgmbsTo4Z8ikSkBE0m#mZ7V}9dX2ZXQo&9-GW(>rK#JY17^c{jHguU3QkDu zBiAHjil3u8(uT$TCI@15_3T%PK?~c=iyzB9pX4EPJ~x01XFk@+kNr&N+pcRorBx6K z7C-hXvsLe2+Q003d+CkHQ0utr+C!x9N^6?iGylS%A%vM_qsmxCOXni_bpgy&=DBHu zjnKe!aoR-(3$kR_qiRnf4X!uDxPqn)qtuPB)JB9TlP!h#sgJ&ipxX-tYaN|o=#7_r z5sUND2~|&E){v2BTJjBMTi9C1?~%wvS3;9dpnaSxcm`h65^(Fp{nqEDelv!6GLipw z)xmu~ulfbhM)*ShyGC+QqTyJ=j$QTmS$$*^duwou%7g45 zM@cP^J+v*U9zxpBA}cPi$pGhTes*q}_)HvMi!7B;J|u6j`C{QDtcAHpJLhnpsHkXD zT#)@7+VarqN3^|#{$yQJ+x=Gm0hT%#x$tVjoq32*@pkuh7FQG3qwAO_Th>1}Epdfc z>$^(Wg_JAw)QmDX=n_%t!NG* z2X%G*D|g{12m{vug$=R3*?jO*2)i6yuyAbyT`_BbD_3USZ^HWz6ck<%ndHu1?#!!$ zd=RUCAnf|CU>H%vCjQyOPQafJpAAP~d*&;Z8qp_yEaeeRATlkHosEzeM&~LG841zb zyTYAC$oh!#FM-;LDC)(Jb}EU7#y&@D1gkcUPoEyYpe@r5zAWD2-nMu`N(OWMt7&#I z4uzp_uqT5qS{WA;cTCKhpayLW$fxdR znC6QGos}M2)p1@V9Tnm3!v(cb(_LEv9l3eBwRnrp@teK2fB!uoTcY`|6%5L3md=A)lUS%x@ER zKIQD2tl<44t!(vs_>-a!s6sYRt_M8|+7eH{v-k$0!&h$;VX;>`xR_Bk3wkSxB=zV) z?QV`|4v@d*c*Dl=94o3C0dx{Tw5UK78=Gt@$JCyUg<=;%yWurBDQMv8g~$hENr=;YY%2ubD+Lc-JNr8QKP`!CZaz=~F@`ii3;(we24Ncs_o>8_%`ng|b z>-S6eE!k;H1@-U;|E^>&0}Q(R$`$OZKRyJo*JqF3UD!*b6W^1gER20FGsjD@f6mlX zjKzzp59vs&7Xo30e$}+Vw{4ceyDf)Z;xyE>Fg@Tc7K`$l7R}&F_b9v`5CotWb#rKxa8G}rhBg!K2a5HZA1>NKC=fM1miabmJm2cQ(r`f0- z#O)mRlp?+xGq4pVy6N+7y7j)Z?+Rl?A?=fjbZf3eqe2E2Jj@xn+Q1$p@u)ONn5&nj z#Y^-&46+0B0NL-pot-)snETBvM(ZNE<>|FFwG4n8*rR5}1pPqMEw|}p)9qK|ZoT{O zq}NCvq%rr?Ay?eRXTA@O;jKLFG#XfkA`PU32h2?Ag8HEHTZ>NVr=eSy=*;|K^Z6&f zWDW)#3`7;`n1j8(^S;7}e|A9S3cr8Vt7o?%=4OUaGpr*J`iJhJo4#Om5^*1_-!|Dx z-ZaXn&kU8i)Ap@BrDL#N%q%EPN>(d%R}9nZxx2IzWxeK3SG}=w1uIO3cR_TE4|osA zhtDvqV{fTw@|oDz87p1PN_51u=*QyAMU1GHO8vG%$MDXG)%w6(8uA>=NYS{x^ERu& zg3CcnnR$f1=?~pdJJ3g`q>FY8$A9R?J>_=DEW29cxgM(8A~tw#FNDj)J)~YW2sBYN zWXL|8uPiU4;SQqP(Ri=0MLsBd1zyb@zrZ}J#KVp8s|*zX(O;WU%sRAf&IsH8Jk;QB zzn3kWcw>Yji(elEH>1ZHqRO(#%@>J3_xJ-Vpu=g!H^!Y%WnF$#7x)bGwnX^%GeJ3l zOA?S^H>FQaN!^l>mV3&(8F5~o{xUlp0p{jdN7v!)tN9sF)APgCiSp)H-Kta^wiK!p z!nXQ&5XYc)@yA3w7A$hVXa*cTY)v}5e6?nbwv@SW-*=)Y>H}LzPD0j**=jZtr!%=l zdf;SYT)neA2vaUJ_0_y2;LzJ`O~mk|UamhQ%bp2{@l+6oXq3@pELr6mk591UFtPLV z)b|=^8S&?vAzBHM=>13PaNN|n1UuSYAGZ_K956XH=8~3WtDMz(|Lr4zf_S>^SWu35 z&@)FJr8DmcElwJ7e!lC5jpipIRI8=tb1o>CfW_p1g85}LQTerkg;X&?vz*r%SG}8A zMDplef}SP$fR@rfAD(uQZ?3o^uzKSL(p5V!BabvWHaS#6HxP#eaqnv{YHbIpRb5c` zkMJtswauf`Jlehpj@5rgo+^^+Jf9`sgGnjhH+c6klg^(%3uinAHP{^-*E4+~ef;9H zKmzb!~B9ZIQ`dFyKm| z#G!Xju0Mk_2Ea87UId}bUUDu^Oxg~OG=R)u4RzvjNQsauI%cIqWs4_Xu=baj?v?oD zFQ85vcBsRRL34DH8nQEjrj;+Yhx*tqfyy_yE4RPbJiXO(FRhWTX^}5-@Q}Nv_#q>& zDbZPDH|~kX7N%e8UX#>A9M~3X!PUp!5Axo|>kj@NQO*X_54m{K!&VV)XrdV=U z8>(vS@Z}uonL!)gZ`M)Rw(anx*+7|EY3!zXm~s#XIhDR!cD*T!c5qr23xP?lrJ2ZD zyrZ07oE|uTg^|agLr*4XlXOz*tGa25dyi+O&7;L5&1E+PG#2kiN?g8WpH}EHG@#Dc ze%VhdJ4&V;C%ziKVXe`g;wm`$wBV}kvFUeuLDFjRuj#ra_#WGq7fw8s!{5;p)Ln7e zR|@Hfk2Z4TO6t@yw%`CK+xwtsiFEY@_;jFEIfeR(#whVOxZFo-`F;)}h4 zYv~t1>J10IT8y1yY*LAHe^6%M5=R8ru5#5UHP4 zwO;YvOqN@DhmmE`O*%Qd@y+y~4w~>5lJ9q+%^oTgul3_*^EL8axl3Cj0|go#aB);P zzuoz63vQyyisOmrOJc}(oK9Y&-rCWAV_b_LvX8IHW_42GKW0m`mCaNkycU9cARb8z zVYbybdaiMF3|BxUwyo#q)U}Q-gh^ib$czoKEZXWlTn8`k%Ls?29NU@>E%+n=!iFCR zdybmg5negnGt^GJo?ow=5T>s^^4Y8>^ln_);$W57m83vwl>Jv8W1o5YTaa=CKLqd4 zfr=GpIzW2HO-eqtamnhd3@K25kePMl`N6bE=3@;}9!G{*-DKgMDY%)iHm~ulSFer6 z?j2#FPLZr~Hvb?Wo={S`a1w6@Zd4GL^+U8XX?>gNLT10V;Fdaw5@*Zm9=*Jw<=Ddj zodO&Uw|Oers%qJE_={q}RIWqt7~?TOyrU3!Z9f z9NCog6Q*+s;&WM@mKZe-*6^4{0R1Vy!rV=+tl%8|m10&9uJK?BUM zrSldmOBa)K4R>IIvm)g74<)CcG>+!ZjN)u=x%{5ec0^lp;$?Xe^AW9!Tcz1H$EGy z5OfB#WYtg6q|3*JX4Y^oc^aPY-!NZNkwIb_nTMWm2Tc*>1N;3(;&ZptMu*oKr5Yin zen#E$J@u?RCoSAeQ{mjc62c2rB||H`_fTrMpYnwdjTYp4i0oUYibsaY-6m{R*bXmY z$YjtP37fZpxchRxN4{*P3dYlC-f*A3lp!esY5E@JIiAe)gz!l1MYmXS_RI8KoQ~~% zMoi_cZyjEUQa5)zZm&B9j)VulIWbJYb~#3uOG~1v#zmIEK5IQBNiuFAsOaN2{%^R( zovwNE)9)v?1Z8H|+pfl;wVptB-+<=sJofXg}Sy<}^Rl&EcqNaGCS&`yrVSl)bHgSQSUGtU91GF%GTTw`pG|HEWhuu1bpFUAJ5oa8C&$n1**_}>(ya?jBlSN>hEJSGd)lnMWsgpZyAPc> zCB5vns!y;JHZb>hI74$>zmbY@*U+DFK$`a4O{?K~q;e0||1qogi-ywhVn?{^{ivGH?LxvPIZ0SHkzZw1p*D9W=v zSo-0%U@)(lz*5Au(BO1=os;zNL~&Ii;h;f1C;$u1lrHBuWK1XbWHyP zj?ka8iiXHJw}X}v^LH_BWmPybDSFWhx5dU)cW3Y#ASnMJFzl7UGvc7{viexIxsjsP ze;+Q(&PJ_tC|@m*JXxT|JR!A<8SqZ?`kwJV4~4(o(7}v>7(x6|YG57}@4~WwX_3EZ z^%boqzHl+nx?XHaEQJg6Zk|unBXT$Ri3<`rOXghCGEDlH((GF_k$e7E=tzq8(Ua^f zhrfdSG5>m{seN;EFNabA$J87>7Jr0tMbv-ppakIeu6K`h#@MI93)^X@!n|SrS%Tar z{w3r1O+_HP;Q=Xv{vb`ji7&sP3V|qmI$4(aWd44jP|84n>%LaJ_p7{J5_gBM(y~Y8 z-*nP|6Yxs6CPzPxSo~aUzL>Eb#>|04#+&A1dNEoNHi}TDKU)XOLV+L2zf`U+sa7U1I`=87oFWL1;XjaY9cv~$ zMx=v^EB+(DAvMZDS!p^q7?&2e>vmGJWquC$_jgHcnX(qWj6 z{eb?TM*+Am-x;2Dpla}kNM(q%U{)XWakhoO!dnQd90_ZsZazLM1}3GbB1jh+q8 z+Y-e$7QGBD3BI~57wqwqWLzHUCo(I(JF1UNu*!D6Ft70K?DLIpmm>7e7~ae4eA$ z6mro8Z&E6iW3-#z`y$*dci+No2*tuxWZCPZ&~M>g)2&|xf6G0%|Kluv6?7q58x#D8nDqpmmd!+9khj2+7 z>gU=ZKe*uQk)O8n-N~^Y$z65Pa{=aQ8)%p6RO~t?>gX($V=;t%?Vz%6g>{tePNHVx z?Qc~dw3(EXtIhb}3_%jmgDqT3J!3|>HPaZ|29GW~r&_o*N=W#hXq(k>j^U6;eg_ahu_?=#P{;`jb zzlA><3{};K%J#BhcwHPZX|`k?GpmeS_%Lal%2roaO?hm_s^>$^GkxxmMy%!2p%>Gp zXRq5PNR68Bete!d&aSf9ac;sDU62@Ryc#s5F5%lafX-B}jZQ{f!TzdgId<0vpZSW;mly3@jlw_a!;`Ju-y zvQD0x^CL`;_+e%`)5#Hnm11NJS;~^!(<9Kwon>BxCe@BLSiv>Rw&-^3r7Zg}Tx;7| zB3h#sPfjl91_<3URgCbsVSipw^xWKOh5YriXHeDW3z?gsCo?P6o1za;rf#m?cit^( zhx$7S)Nd2?N#IYybYX_+76yFxT*n%5$R+o@sBUfo)-9Fo~5%mspO zD!|K>qTYcSS&fib`AnHNy@@}UQ-TDqIw#_e=TV{d3fW7Xo}!Tgic;`dsh~8i~Su@eyD?Bkzb>NJ*LHrUqv5szGPL(PJdL^W-G|9u%Cs z^0=5_rwtjc(}LReFA}_a_6cF+ptsRv(tU%j>3pmt`-$<41SI{gXTb|Xi6;|6`Mj@`BeBbvQF^E>k+{Rw{@G-EDWuV{ z^8RtjWZ?RGH`C@))J))E4K0h43zAx8#O{ZN&zlC>RtLS}1Gd7F(gL-LwBin{vJjj8 zbZmM2n&z)_?NJvG{4PV4;mA9BUHi1^5+hq1k%vLagmT|?1XKT@v*(hNf#vizd;zZY zzIOM$522M$s+yfR<)?79_ESOAN@x$$_A%NIA2OSETduJ%l}lAZnk!cG8iwtYb%3=qNVwJ5r)grB&vkCHVmfyeh!n+RZu(9O@Vn=1xZ)8qp**#hZX^R;Ai3uE6e zvnFGS%R0yj+THMz1XnkpHj!37X#x(#wn{)H|mZq?jI2-d%!5!Ey2_eFybBK`cX=g?E() zTP=_ZzNU1^KY%YeO4@nH1ITykK#=}RvWUoC=c2Xm-`U=RN3Nt zPVUm(!hMv>n%ZU~1<>>J!|Pv7*54&m>@UPvRC3!xiJ-!WmW7WSHN$LA8wIFQIBi~u zQy_yMVLBY(h25ZJdj5={Rfsa&L~g;IrEJWeCO5h2i|>jM$kn|!FNqwBPiHss(PbwL!`L) zE>Z_;jxZ4{pgHWQEgiBA2Bl#_={la>e- zZnl~=?r4`-o?RybY^t)5D!=D;3L$W%7>-jsN@13b>yVj?S_V2mmM6&h63@DrFO_Dh zZP3jON$($FmQg3{B@?k6MlI=K{(=pG72SrWHWU)Cn60Yg}sMvP)TK_gGq5|t2r*w8L>IP$v)T_oLT-(jN5F)we><~ z$pDbrz<>_uBv{5P6SpK&Hg%%Laa(iXV}o7W?CDiT#qP=B5-4RJ&YF3SPBp=y z18X_b2N#XNb^6IHr`8kr6h;Q7G99(vW}Qic2J08XpxZ{qe#^e3vN`uRVz_UXLCQ=> zXV(W&12BU+6Y3^m5@|T1SZ`}ZBlwd-Qc9e^o~)Z2Z=kGl%nr85yE$xlsUEN1Y$}Ld zL<#o643hShw_D8$I+zO`hG6v=*mmmm-N=*`t`X#TQ-sw#cIX~=o#&!le{)}Ms?X>n z3_f`4Rl5DMZ4zcl$KGpn5Y6-PIg;ZjX~C<7bDA+1G+wU+i8UUze<$$tefRl;DYcQe z5$WA9X~kptA731OQIPcHV&auI3u!V{{Ivk=Z^kZ~^J$Xxr_6Q-t7vzF9(fj2FF8tb6 zs^@qf>*<|nf|Oq4!rt1F_yE<(R6EDjYbh7@UaZ~?8}@z{MzmU+_WAyXY~(h?>wn`@ z!e^7A)~`Qo=(fTTKax!ag*Dnl)5*gTW5XYRh|a7*j>)>=u}iQ<+nz6Xw$@{KLDUD8 z4h`cenKm>Bb<2#GF!sm#7f(v`m?HZ#9efMB(N3-w=ZRs>GQ1A6kDXW`4wj0_2gPpf z-9;?1Qdw1wUtjydF+XUUL~bJVO-7*U`{7sk7}?%VC?E8aA_h=-sqhE(wW)( zHs9``rhUXBAHiqHjImpEIVk(nl1cr^5@szG4CggI`_#3A4@7pm7((r#5(`sVpCQw= z;S|omIt(5(P)g#Pqt95=fjK|V0rb&=2Jw8`YEii9DaEg3IJ}b4B zsr$Qvl2A4*{xbstKl5Fm)xR3uK2HRRziLA)-bN&+?h4jhHg0`nV`o+ORXU*4TQ>T< zc*onqMay`TRXfjx1|s-m_Hk5$-#anUnm`4>+N;<<*h!R>7-uHw^klRd1m@i9u5*03 z+dSQuw2k__hAus-(P&QRM|vu?#lFiZ{eIzzYr1xtiA#&h+ch^QX_qu)P?Vb6W`JHX z(;2%3Z?>#J;$r74%#{y~yKY5vZV&A8E2|6Gx6&PTY5xS%zn%F`wLabc$v^;_ zQIG9Yr>c0Wu$X8P(gM{J$NE%wEuyC3vy|?&`nx_#WRtBj8ZLEI%c>xlcL2W@B;z@x z1cWyMxOh0y+k=e0Sg2XW#z^8?DS{yT)n4$Vu7yEOgBdOurQ~ZMI zQr_PzM|nF`MlZ-&DZIS@ByQVpml5CB@B_XyyK-uk>Ph&c+%GX&o$8%6wuaNst-+2C zaOae+Bi}qr1#6f)ZKc)u2MQXy(m&;ql7l@{+^*E+)fNQc%iiIx;}u=(Qjm`YK`EA& z`fm$wI!S01ZzPCz;fO+b_5x_F{~U_BzIN>H`~CTy$ERYtPBh)<)cuj6MHf(E^lax` z;l(wgkRUnMFy(VX_iDBjC-le-GVw0Dqf%Wi&}u&{NUaV5ODATWzn*YPHOK~DbINzN zpC^UUjBdU(QN*+Evx$Jn6ocfi_Aw(pVM8{^{cTQ3x?}0wakG<4jA+a^t}@t7^4$}JZ9(alQuApir;i!Da} zfxKirXbZXrhx2RR;rS<==t&PmwuCmpxT`O^GUmg=4@-yIKZpBj_ilySDw?6|Kw||9@gd(IN&y(BjaPO$DBwDi*wmH>ShENvLe#OqOUX#k2uk$HEb|cyDwkI{%L`5zW_X|3?z>TpjpjE*TvKR zr7@%g03hw><3s?&^5@P~ucmfis|acANE+_=PSS&HvIg;aBhT|3R$L}@K{q}0?jM*d zgckrhWtLc2iqs+B5Z1f)7ybfZyf9@M?oSB- z3bXpw@apNN)*{Fksd((~en;6?fa>^u&RQ|cjgd4z(XF;P?q58e=Fij!(=v}@&LB;T z{RQ1*rvP3rYJ8{u!yEvGyQu}>#D0NFQGXaqku*~P@YHELX3@`$vj~4DyyMpilYX1A z%H(g(`{oaj`PC@+EO~E-LGNz?85U|E2uL*&7_c#1x70sJ|M$vYzXF6jOi|28 zi$h}_U0(?vlUMumq!5o2LNk^E4&9-D$N~VpqX0S%XAk1k?1D+BU@6u}IOI0%#5U zd=!qzIv;&B)QS?-^V--{Z0JqOR245*WbSzWoyjK$~@L+aBkmiQsFHl=Q4j|@&HhF61s2z_92<5*kBE z5}q~Q0ZA9DS$LG%p?B8>Z*VF8st)LfP@dxTSX$$9ndjWr9oTaVRCQ7~iZhEok7j=~ zMsPshJ5#+VBMU zEq5wZV1aswu9ngDz;X!NbjhT~>P;QT>0j8#RNMh8sP|@%FoT^!)0AZteZl*FuRWvkQD@zi0RU;qXFl!ARlKBJ2M)lOFA12D1;WL6yE>NcQwBI_Wl zL}D{es*v}tk4uG7O$u?ZS9vm27LZxJ#UI%qMa?u}qYS=Dj(tE?*9ve3RgOK4+&3@S ze9OFnGS-C>BP?xx_o=bV0F{Rvf%cKKDz{_qgA>e@2YDsb0(#q&q{<$6wn&H=aaCOP+I~|;ofh0a@K#;5D4#0E?eIJ4Mpd!XWAQOJlSlI;MT`RduSULy4M6Se ztpECpQM+o({t6@S9|4G4GRMywTTSels3=$eM1F!^v@a#!5V&nzD?zfjG;j>3*5K%k zzOg$2ar=J1b^uMS{i*T1{HCB>;ASuTFTJ3mi32{l&o3F7#f-^MfHETvSrJ0K`Hq>Q_VQU7}A-etGYw#8&bT~$Uj!>N__i>3>in>L!( zJPVNI3=vchM)0!-3s&>|GTYG5i07B&ULBaSjioOqaSU~u92?P*7(oXY}eiBA%WvE zZ7$a)i~Op9X6plWGQk9A^$v~UYx~JJbZ^sM3)z-OKwnCD;4@{Fe*r?y~|K2=U_5yu5Olw#ZVaBm`SSHXg zn1E@%7&usgN@!N2eQ~s5xCC`%ysPOgxWvC8Rk-pWW1S4hKvrf+Lu8=yd2=9mx}8~P zig?GIGzHZt%?%5nC-}C#JpZd@SKAKoKl05$MRTxdJ~Lb9reOO#)58Up2v*`pqQZWx z_&|2^RX8*ssC?v{0$%@KvkBo6CW%aL?bonhh=Q4|xNVv_y+kacAz$XvJR`&$15r+8ev6=hH>o zJ1&mK1B)_*3*1X$jnPkA5>eZ4gn!&7OLT>WXFCY8?7|rZ(2(!F=ks$>AfBahPA+SkN4pUR zF%K79W$WS#m05;czIJSnTstC~y_;^;NY(&3Yk1$uon75r8w_O1%if%Q=u#swq%_WB zKz`Ho&5em@`lpfk)+?$Iq_Vd{)QLkU@4l6Yk~nhbWV~1VW&*PW7i9>a!Ypl#km`$v zlU}0OHx`q6d-81st0`=cK8=@}Xh+URlrT!J-V?_sfzh`=k>ncU242gNm1@~Jeg`Y> zq=U^Li~yE+k}JbKvlp-pdOvN4w=Mv3E=M9_IlLuE^3ns_dR# z+G$D&iLd-VFVQrzDgP8jO+)&Dra(FZYoC3O-7zB9cuMqbNkl-Lz^CnwyLGDX_NLwL zhAuQ~s#@fT@=oM3Xd$4l^qgq0StjE$R$AxYc|fdGKmlezY%Tsm-;ew~E?x3hEHlR~ zJH56c=|2$}KJ55%zrT#SN7H_5gzoJrLI!rLQI}>_=VyJBYO;a7q`G5s5_tYJEb!Z_ zoB9sx#P4p-jm1v9LSt*%1ITFFf2`yT53nC5sfum~vsG}_>t|PlcL557b!+;D%G`wz z*wGU>Yrb*2nYBlc)YNy1W|NQ=Y$FWswrJsYoU8{|gG28sYdMh!3J(4;_d5MGj1sMC zt~y1Ne=Om7Of;+RLu=mYl&+f2S!{Y8w|@wslg(s%|85NVs@C`T*K6Sv@zo9j#23Eb ztR^`MQn7Tt%eYzVw`+ns$WmRu1m^UO{)FUvm!b=QrRFr$XJ?3RKZK_FL@|#?7oqqk z>p`BDI<-I&ZQ}{vZuY6#w#7!2-lgvrZtD`S4Y*1vDxX_N+RgT--DhN^)5`E*W7`AK z{c@txc5erzZ@2B8e&y_28!)*(juN5Xz7l^Bp|omh&i%5d85>o^GT*F=8O zXWPiPkiK+olc~tbdgvr?xERS&Q2i?i#}(ZV#Qqfq@lah(*G8eepeV~mo60MRj!k!LeG z&``J$D@*t=HQ495U`LF8`=a}^+%G~a&a$slJYd`{nu&@xISQ<_n-?B_51rhv4L|&~ zAO&z$DXa#_Z`bx=Ycp7v>0k9JW5i0aLWfZ4OUXrwU*qKh{AqZf=i@(q6!`21Hvl*6 zmq_THrTu85L#q%@8SStxj!3|4SI96FvwmxeC}Ia@f`C;+-VQs*?kGz)GW!3?{6ZWr zu&=|1GxN=VQSr~qM<5t8zx*i@N#)BwgH|3q^V@AwM4hcCh{w3J^P1m(1_@th0nxxZ z&eJ4PR)l(MUQ+{C1NgJ*!@1uxA|a1X0&Z1l*Q_kgH^}Icd1~C!xHxfBH)9#k8LxdT zB(DrRP-BN+9dQljB`c~BS5{1bc8V%C%fuo#2Nsd^qSgj`n8Yj?H^8AWK6uBvp;j7X z?0w~6)?XD@=iumaj@yi0=B$;+*M$kD(NO841$0pQMFPf|Lr z^b7Cyr!y?NHcMw%SEJ>A`koIF3(7q$+>d-3?p3aQ)xuAiSQ@FS{9$VmYB2kf153${ z4cCg0GGaW6=Y{0RM37I&uigOxT({2I9;?Z-xsV7xRR`e0gG~Vpqcm>Cdlph%KovMe zjFF7L&32&58~YW=B#|PY`xVTM#Y|>_>Z$zrICeR!lr2k{76cd+QretkmBVZ6}GK-X9%1Y z+(Jwh1SwW=nrEfj4WXwD-Wm2HIj!@x0ikR@2gJ;ICx3cGIj7!q$kOy?jrLM76Rv=1 z)%$xj>5U@9!THL}_BUqc7A(%2OYTSP?4ja+BkUYS+1S)*+suqRH{K2&cuhWMpZr=`C z!Juc-bZ-C$vI0<=#ZTu7o<4~`@&296SXLGEL|bgN)WvR*EL@@F$K0X29cEe}Fp+_~ zYinFRxBaqlDe3{=Bbgj&?xn#K-c66C*^`4Ou9l;4A1)OX_eE;c9qZ2Anjfpi4q#JL z&+D^6AwA<*hLWDAxA&yu znefR5F?}#GHp3DJRSQcg#QM%y>mmUT^*v8G}( z?Ohk2rh6N_oWG@2UfhziTP!i1Zy`joVux=^6t{2^hGFQu!hp;)m%q$@cyNRGA;^`%`HeD7CXoBp}TU;lYoOh<{NO{vNGavGi0>@g#= zhYuviuj;;)bV1DL(HMpd{Mg2A`4vJOCCxyrP8_L%;UXbN0<9V&I+dj#EU>w455c9O<6Ur<9AcYFG`3@5neuRFxT#=CV0hwynar@N(n2Hm zpMDWx2{A7JE)s3FL>MJ#4ufUc1b}7wReRxO@C;3TY}-+odwuH@9uafuIz_+O!~dNzCG5@G=xo||2@46uddo;f2$uV-n3O)M_+s?zU<WPjI*6%PHlJ%uVy{7Icf~nZRiPvLH*SlEC*;qd-9XpMSMmK z;*5VN*Jc48azNPdi2793#kQRmkTjJoC}0VVvk9P!)Ak4f-!{YUvm0)DdeHo#D;o@y z6?S)QXCZ@%Nve{+km;@w|If?!%=@*APcjRjpdl@ks0$%GnXtfQ6hr7VV^ca*W9+{2 zhg~pW2S!YHPYhWQoheMWYwTbj!zs4JcCVPQjf8%qlmoU?8v;Nolu-HC4GSGokiWpG zgBZecvRBZ6uPCc+hE4WQm&MGPRD8iD}tmn;iw$=hZn$Pwz#{? ze$TaWVb)=2mrV5wh`%NzP3g8)*S^>QWZ0!YWn>ufgU`??S4o9MYu;kC5a`(AgNx{( zv`RfLrT$7)D0C>C0XV84St3lQ=hWJ`D|QHdN77d4H^jSd6}wil-ecTb1kf1%8F{BxH3fVl}Iu38M)XGxpa>9duN=L{G&sWiMa2n=|9 z^RGx9|7yvOIWnSniM7uo3<1;no=Wuh_L#KDP$|WF>I|IE*eu!8+YZ;xwgliLkaN?w zB)4_^cK(9Iah)B+N3qF!p9XX<*+Y1Tn(IBZI(}R{!p3j%Xn1Ef$?zoW2 zQA>CXLioa8P;7>alC8#7wXW0?FIj-TRVg#~38^fm4cf5XB#iq9G8BJ*x$TjO!ygC6 zk!=_UxPA*qn;uXeVlDrZuo`f%JSb#d7&V?Atv0liO>4QZ?y?2>RYH(D?ms zIQ{O4!rxWBkRQO@)Eb`cxk^@vDNKv;xo=)mm+vHV2J`Weq}V(Kps*L_ZjRGvaC?9a zCboLtim_?`mEKYgte*>5jq3<-feloeepK1RmX?4P&id$RV0mf&5Qtcc4X^I7#R0ur zX7A2KOgdzda1v=)ro1lJWXqkfz zTkuoI8B>iTf2A`A7`wUjD(-9n=c5OQ7m1@=1vyOH zdjrDw8bQl^V|)g7MNM`dN(oy3AyKxEVxM#@fGo1s??q5N)bP^eR6zujlv$G{xqS8| zyyibDc8U?82SmJ|Gk@CL3z}yG$17)8elg{QVb%*S}K0pYsjVj=~5)slDllTs;(|B~YRNrg`j$4mt9B<-d~et=9qL zL@oRD<;kjZFHGBichUb-HUC@6b*{owLJ_b^dv7WQ%6Guh=w8X^4ew9?-m@|XUflra zMLB?B#k@NcxQP7r>dEtJ({k%S7k|5vcz#|>&b93sTYrkh?>lI&DC=elA2xUdip-lu zU2akFFSh!pS1HSjtnYdJFCyII*H!!X{o#98@FL>vBxtLe)14g=z#<*E3+Zo>(e)4g zmwY}U3dE50z`@V;`OCo@9~SDz{@U}q-D>8Qi~kzFO@OUu0CvV|fEiBt%T~@G_Ah;2 z?D@y>R+$@N%U58}=Jzj)9|z;&x3Q?1{QGUcZQ6%7)>96g3QmQkQieuF&JP9eC;XUH z@`2^=(#VVLNHe~PpoJS#%c~#fc5gW<`=S0rU))3P-K+Lo&y<7N@<1FoY(6_ApX>Ga z@cjFij9}&}v;!wd`4%jBx9`WJ?zOAF=hi#M-?qEKrCw`rwC?JS8!vp5q~Td*K^M>) zs}~qwUl%(&@2K&4o5^)&xnU*&bDRw7Ho-Z-N{yd~N2ErF9pylzmn;!imzQjt0-5Ut zmXe_PWuVE)mqkNBhiX74<{K{o_m)pTH*FFu?toGq!0DU%oLx;_h!{`^umYAamu?0b z!Tbu$bg)q?_`D6MtpyxEjkk*SLTuS{P%s1bH#WZH(t_7eK&QL}I>kHP8#3<;Nz6yw zfa?y-VjW#!ZUZ_+V=mAsFLN{{kz9NOSZp3&ro95x0f&`Npo1p#=jy5=C0hY$po?#m zu!@2wCZP?+5};F-ZeE%K_dg>`k}`1EYWBJXN(j?9rT`BD**_Y1qk%_E%KP!3v1a!? W-wjWHod=%q#^CAd=d#Wzp$P!Upu}eY literal 0 HcmV?d00001 diff --git a/docs/articles/packages/ExpressionDebugging.md b/docs/articles/packages/ExpressionDebugging.md index 0de03e83..e32f41dd 100644 --- a/docs/articles/packages/ExpressionDebugging.md +++ b/docs/articles/packages/ExpressionDebugging.md @@ -23,7 +23,7 @@ Now in your mapping code (only in `DEBUG` mode). var dto = poco.Adapt(); //<--- you can step-into this function!! ``` -![step-into-debugging-screenshot](https://cloud.githubusercontent.com/assets/5763993/26521773/180427b6-431b-11e7-9188-10c01fa5ba5c.png) +![step-into-debugging-screenshot](../.assets/step-into-debugging.png) ## Using internal classes or members diff --git a/docs/docfx.json b/docs/docfx.json index 5a4a9338..4220df2e 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -78,8 +78,8 @@ "resource": [ { "files": [ - "images/*.{png,ico}", - "articles/.assets/*.{png,gif}" + "images/*.{png,ico,svg}", + "articles/.assets/*.{png,gif,svg}" ], "dot": true } diff --git a/docs/articles/.assets/mapster-logo.svg b/docs/images/mapster-logo.svg similarity index 100% rename from docs/articles/.assets/mapster-logo.svg rename to docs/images/mapster-logo.svg From b1676497dc66bd5157d159075864c580d9832356 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 14 Dec 2025 11:49:40 +0500 Subject: [PATCH 352/363] feat(bench): add benchmark to development --- .../Benchmark.Development.csproj | 26 +++++ .../Benchmark.Development.snk | Bin 0 -> 596 bytes .../Benchmarks/Config.cs | 54 +++++++++ .../Benchmarks/TestAll.cs | 30 +++++ .../Benchmarks/TestComplexTypes.cs | 26 +++++ .../Benchmarks/TestSimpleTypes.cs | 26 +++++ src/Benchmark.Development/Classes/Customer.cs | 41 +++++++ src/Benchmark.Development/Classes/Foo.cs | 32 ++++++ .../Directory.Build.props | 18 +++ src/Benchmark.Development/Program.cs | 11 ++ src/Benchmark.Development/TestAdaptHelper.cs | 105 ++++++++++++++++++ src/Mapster.sln | 6 + 12 files changed, 375 insertions(+) create mode 100644 src/Benchmark.Development/Benchmark.Development.csproj create mode 100644 src/Benchmark.Development/Benchmark.Development.snk create mode 100644 src/Benchmark.Development/Benchmarks/Config.cs create mode 100644 src/Benchmark.Development/Benchmarks/TestAll.cs create mode 100644 src/Benchmark.Development/Benchmarks/TestComplexTypes.cs create mode 100644 src/Benchmark.Development/Benchmarks/TestSimpleTypes.cs create mode 100644 src/Benchmark.Development/Classes/Customer.cs create mode 100644 src/Benchmark.Development/Classes/Foo.cs create mode 100644 src/Benchmark.Development/Directory.Build.props create mode 100644 src/Benchmark.Development/Program.cs create mode 100644 src/Benchmark.Development/TestAdaptHelper.cs diff --git a/src/Benchmark.Development/Benchmark.Development.csproj b/src/Benchmark.Development/Benchmark.Development.csproj new file mode 100644 index 00000000..572bdf29 --- /dev/null +++ b/src/Benchmark.Development/Benchmark.Development.csproj @@ -0,0 +1,26 @@ + + + + Exe + net10.0 + true + enable + enable + True + Benchmark.Development.snk + False + 7.4.0 + 12.0 + + + + + + + + + + + + + diff --git a/src/Benchmark.Development/Benchmark.Development.snk b/src/Benchmark.Development/Benchmark.Development.snk new file mode 100644 index 0000000000000000000000000000000000000000..beae8ece53cb310defad6c2729d3c6bfe93c88ff GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098mFb5}c)VvsS19hm9+)LYgiAma0ebK1!QT6HH9+X zkMNacZ@?@xo!u2jB@!jOn1BNA$gF z;PjWmQv`(2PTuY>9aMg587L7~RUYg7Bfne%@UMccxHqBlI2i3&GG`vw&!+kLS@dAH zcHe`z@w%C2eSz{7*-5dZqRp3XEBL_GlyJr15KzZq%JNZ;sG)6f+1P}*8F44MB}mn; zxHC5HbhH4T#ts7Yh_+O-)BfMepT%}BL;cCbH6%w^S*fPUruE)zv9%nS(b={?sq4ps zNIBD88ZFiv>LmMayAR&Z%DWG&^oGO77-yg?bi39l(o2Um<+uu#ScL19MuI|4_;dVs z34LbmS9I6sqTCTRL64DYGXn!LMdZ?ds%dOPuny(_fooInstance, Iterations); + TestAdaptHelper.TestMapsterAdapter(_customerInstance, Iterations); + } + + [GlobalSetup(Target = nameof(MapsterTest))] + public void SetupMapster() + { + _fooInstance = TestAdaptHelper.SetupFooInstance(); + _customerInstance = TestAdaptHelper.SetupCustomerInstance(); + TestAdaptHelper.ConfigureMapster(_fooInstance, MapsterCompilerType.Default); + TestAdaptHelper.ConfigureMapster(_customerInstance, MapsterCompilerType.Default); + } + } +} \ No newline at end of file diff --git a/src/Benchmark.Development/Benchmarks/TestComplexTypes.cs b/src/Benchmark.Development/Benchmarks/TestComplexTypes.cs new file mode 100644 index 00000000..62d7a764 --- /dev/null +++ b/src/Benchmark.Development/Benchmarks/TestComplexTypes.cs @@ -0,0 +1,26 @@ +using Benchmark.Classes; +using BenchmarkDotNet.Attributes; + +namespace Benchmark.Benchmarks +{ + public class TestComplexTypes + { + private Customer _customerInstance; + + [Params(1000, 10_000, 100_000, 1_000_000)] + public int Iterations { get; set; } + + [Benchmark] + public void MapsterTest() + { + TestAdaptHelper.TestMapsterAdapter(_customerInstance, Iterations); + } + + [GlobalSetup(Target = nameof(MapsterTest))] + public void SetupMapster() + { + _customerInstance = TestAdaptHelper.SetupCustomerInstance(); + TestAdaptHelper.ConfigureMapster(_customerInstance, MapsterCompilerType.Default); + } + } +} \ No newline at end of file diff --git a/src/Benchmark.Development/Benchmarks/TestSimpleTypes.cs b/src/Benchmark.Development/Benchmarks/TestSimpleTypes.cs new file mode 100644 index 00000000..8678a8ec --- /dev/null +++ b/src/Benchmark.Development/Benchmarks/TestSimpleTypes.cs @@ -0,0 +1,26 @@ +using Benchmark.Classes; +using BenchmarkDotNet.Attributes; + +namespace Benchmark.Benchmarks +{ + public class TestSimpleTypes + { + private Foo _fooInstance; + + [Params(1000, 10_000, 100_000, 1_000_000)] + public int Iterations { get; set; } + + [Benchmark] + public void MapsterTest() + { + TestAdaptHelper.TestMapsterAdapter(_fooInstance, Iterations); + } + + [GlobalSetup(Target = nameof(MapsterTest))] + public void SetupMapster() + { + _fooInstance = TestAdaptHelper.SetupFooInstance(); + TestAdaptHelper.ConfigureMapster(_fooInstance, MapsterCompilerType.Default); + } + } +} \ No newline at end of file diff --git a/src/Benchmark.Development/Classes/Customer.cs b/src/Benchmark.Development/Classes/Customer.cs new file mode 100644 index 00000000..5fac9cef --- /dev/null +++ b/src/Benchmark.Development/Classes/Customer.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace Benchmark.Classes +{ + public class Address + { + public int Id { get; set; } + public string Street { get; set; } + public string City { get; set; } + public string Country { get; set; } + } + + public class AddressDTO + { + public int Id { get; set; } + public string City { get; set; } + public string Country { get; set; } + } + + public class Customer + { + public int Id { get; set; } + public string Name { get; set; } + public decimal? Credit { get; set; } + public Address Address { get; set; } + public Address HomeAddress { get; set; } + public Address[] Addresses { get; set; } + public ICollection
WorkAddresses { get; set; } + } + + public class CustomerDTO + { + public int Id { get; set; } + public string Name { get; set; } + public Address Address { get; set; } + public AddressDTO HomeAddress { get; set; } + public AddressDTO[] Addresses { get; set; } + public List WorkAddresses { get; set; } + public string AddressCity { get; set; } + } +} diff --git a/src/Benchmark.Development/Classes/Foo.cs b/src/Benchmark.Development/Classes/Foo.cs new file mode 100644 index 00000000..063541b8 --- /dev/null +++ b/src/Benchmark.Development/Classes/Foo.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +namespace Benchmark.Classes +{ + public class Foo + { + public string Name { get; set; } + + public int Int32 { get; set; } + + public long Int64 { set; get; } + + public int? NullInt { get; set; } + + public float Floatn { get; set; } + + public double Doublen { get; set; } + + public DateTime DateTime { get; set; } + + public Foo Foo1 { get; set; } + + public IEnumerable Foos { get; set; } + + public Foo[] FooArr { get; set; } + + public int[] IntArr { get; set; } + + public IEnumerable Ints { get; set; } + } +} diff --git a/src/Benchmark.Development/Directory.Build.props b/src/Benchmark.Development/Directory.Build.props new file mode 100644 index 00000000..8c415f63 --- /dev/null +++ b/src/Benchmark.Development/Directory.Build.props @@ -0,0 +1,18 @@ + + + + false + + + + chaowlert;eric_swann;andrerav + Copyright (c) $([System.DateTime]::Now.ToString(`yyyy`)) Chaowlert Chaisrichalermpol, Eric Swann, Andreas Ravnestad + false + + MIT + false + true + false + 12 + + \ No newline at end of file diff --git a/src/Benchmark.Development/Program.cs b/src/Benchmark.Development/Program.cs new file mode 100644 index 00000000..32e641ed --- /dev/null +++ b/src/Benchmark.Development/Program.cs @@ -0,0 +1,11 @@ +using Benchmark.Benchmarks; +using BenchmarkDotNet.Running; + +var switcher = new BenchmarkSwitcher(new[] + { + typeof(TestSimpleTypes), + typeof(TestComplexTypes), + typeof(TestAll), + }); + +switcher.Run(args, new Config()); diff --git a/src/Benchmark.Development/TestAdaptHelper.cs b/src/Benchmark.Development/TestAdaptHelper.cs new file mode 100644 index 00000000..c6e06739 --- /dev/null +++ b/src/Benchmark.Development/TestAdaptHelper.cs @@ -0,0 +1,105 @@ +using Benchmark.Classes; +using Mapster; +using System.Linq.Expressions; + +namespace Benchmark +{ + public static class TestAdaptHelper + { + + public static Customer SetupCustomerInstance() + { + return new Customer + { + Address = new Address { City = "istanbul", Country = "turkey", Id = 1, Street = "istiklal cad." }, + HomeAddress = new Address { City = "istanbul", Country = "turkey", Id = 2, Street = "istiklal cad." }, + Id = 1, + Name = "Eduardo Najera", + Credit = 234.7m, + WorkAddresses = new List
+ { + new Address {City = "istanbul", Country = "turkey", Id = 5, Street = "istiklal cad."}, + new Address {City = "izmir", Country = "turkey", Id = 6, Street = "konak"} + }, + Addresses = new[] + { + new Address {City = "istanbul", Country = "turkey", Id = 3, Street = "istiklal cad."}, + new Address {City = "izmir", Country = "turkey", Id = 4, Street = "konak"} + } + }; + } + + public static Foo SetupFooInstance() + { + return new Foo + { + Name = "foo", + Int32 = 12, + Int64 = 123123, + NullInt = 16, + DateTime = DateTime.Now, + Doublen = 2312112, + Foo1 = new Foo { Name = "foo one" }, + Foos = new List + { + new Foo {Name = "j1", Int64 = 123, NullInt = 321}, + new Foo {Name = "j2", Int32 = 12345, NullInt = 54321}, + new Foo {Name = "j3", Int32 = 12345, NullInt = 54321} + }, + FooArr = new[] + { + new Foo {Name = "a1"}, + new Foo {Name = "a2"}, + new Foo {Name = "a3"} + }, + IntArr = new[] { 1, 2, 3, 4, 5 }, + Ints = new[] { 7, 8, 9 } + }; + } + + private static readonly Func _defaultCompiler = TypeAdapterConfig.GlobalSettings.Compiler; + + private static void SetupCompiler(MapsterCompilerType type) + { + TypeAdapterConfig.GlobalSettings.Compiler = type switch + { + MapsterCompilerType.Default => _defaultCompiler, + // MapsterCompilerType.Roslyn => exp => exp.CompileWithDebugInfo(), + // MapsterCompilerType.FEC => exp => exp.CompileFast(), + _ => throw new ArgumentOutOfRangeException(nameof(type)), + }; + } + public static void ConfigureMapster(Foo fooInstance, MapsterCompilerType type) + { + SetupCompiler(type); + TypeAdapterConfig.GlobalSettings.Compile(typeof(Foo), typeof(Foo)); //recompile + fooInstance.Adapt(); //exercise + } + + public static void ConfigureMapster(Customer customerInstance, MapsterCompilerType type) + { + SetupCompiler(type); + TypeAdapterConfig.GlobalSettings.Compile(typeof(Customer), typeof(CustomerDTO)); //recompile + customerInstance.Adapt(); //exercise + } + + public static void TestMapsterAdapter(TSrc item, int iterations) + where TSrc : class + where TDest : class, new() + { + Loop(item, get => get.Adapt(), iterations); + } + + private static void Loop(T item, Action action, int iterations) + { + for (var i = 0; i < iterations; i++) action(item); + } + } + + public enum MapsterCompilerType + { + Default, + Roslyn, + FEC, + } +} \ No newline at end of file diff --git a/src/Mapster.sln b/src/Mapster.sln index befd616f..57696e45 100644 --- a/src/Mapster.sln +++ b/src/Mapster.sln @@ -63,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TemplateTest", "TemplateTes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mapster.Tool.Tests", "Mapster.Tool.Tests\Mapster.Tool.Tests.csproj", "{E64E9CEB-8FB2-4012-BBA8-4C2B99FD54C1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark.Development", "Benchmark.Development\Benchmark.Development.csproj", "{5F29425E-DFC6-48C2-945A-FC5D91260C07}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -161,6 +163,10 @@ Global {E64E9CEB-8FB2-4012-BBA8-4C2B99FD54C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {E64E9CEB-8FB2-4012-BBA8-4C2B99FD54C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {E64E9CEB-8FB2-4012-BBA8-4C2B99FD54C1}.Release|Any CPU.Build.0 = Release|Any CPU + {5F29425E-DFC6-48C2-945A-FC5D91260C07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F29425E-DFC6-48C2-945A-FC5D91260C07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F29425E-DFC6-48C2-945A-FC5D91260C07}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F29425E-DFC6-48C2-945A-FC5D91260C07}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 865d695bb1fe9ef1cd400f739979510d536f264f Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 15 Dec 2025 19:46:03 +0500 Subject: [PATCH 353/363] fix: Map To Target Null regression --- src/Mapster/TypeAdapter.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 7a42ab4a..218045bf 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -105,8 +105,9 @@ public static TDestination Adapt(this TSource source, TDe if (sourceType == typeof(object)) // Infinity loop in ObjectAdapter if Runtime Type of source is Object return destination; - if (typeof(TSource) == typeof(object) || typeof(TDestination) == typeof(object)) - return UpdateFuncFromPackedinObject(source, destination, config, sourceType, destinationType); + if (typeof(TSource) == typeof(object) || typeof(TDestination) == typeof(object)) + if(sourceType != null && destinationType != null) + return UpdateFuncFromPackedinObject(source, destination, config, sourceType, destinationType); var fn = config.GetMapToTargetFunction(); return fn(source, destination); From 3c774725df8666e2bb4c38562ac41e9eaefabc90 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 15 Dec 2025 22:24:23 +0500 Subject: [PATCH 354/363] feat(bench): add in benchmark reference to dev mapster --- .../Benchmark.Development.csproj | 17 ++++++++++++++--- src/Benchmark.Development/Benchmarks/Config.cs | 18 ++++++++++++------ src/Benchmark.Development/MapsterVersion.cs | 12 ++++++++++++ src/Mapster/Directory.Build.props | 14 ++++++++++++++ 4 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 src/Benchmark.Development/MapsterVersion.cs create mode 100644 src/Mapster/Directory.Build.props diff --git a/src/Benchmark.Development/Benchmark.Development.csproj b/src/Benchmark.Development/Benchmark.Development.csproj index 572bdf29..2213d7a4 100644 --- a/src/Benchmark.Development/Benchmark.Development.csproj +++ b/src/Benchmark.Development/Benchmark.Development.csproj @@ -3,7 +3,7 @@ Exe net10.0 - true + true enable enable True @@ -20,7 +20,18 @@ - - + + + + + + + + + + + + + diff --git a/src/Benchmark.Development/Benchmarks/Config.cs b/src/Benchmark.Development/Benchmarks/Config.cs index 554581ab..87dad0fe 100644 --- a/src/Benchmark.Development/Benchmarks/Config.cs +++ b/src/Benchmark.Development/Benchmarks/Config.cs @@ -1,4 +1,5 @@ -using BenchmarkDotNet.Columns; +using Benchmark.Development; +using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Exporters; @@ -31,23 +32,28 @@ public Config() AddColumn(BaselineRatioColumn.RatioMean); AddColumnProvider(DefaultColumnProviders.Metrics); + - string[] targetVersions = [ - "7.4.0", - "9.0.0-pre01", - ]; - foreach (var version in targetVersions) + foreach (var version in MapsterVersion.Get()) { AddJob(Job.ShortRun .WithLaunchCount(1) .WithWarmupCount(2) .WithIterationCount(10) + .WithCustomBuildConfiguration("nuget-bench") .WithMsBuildArguments($"/p:SciVersion={version}") .WithId($"v{version}") ); } + AddJob(Job.ShortRun + .WithLaunchCount(1) + .WithWarmupCount(2) + .WithIterationCount(10) + .WithCustomBuildConfiguration("developer-bench") + .WithId("developer")); + Options |= ConfigOptions.JoinSummary; } } diff --git a/src/Benchmark.Development/MapsterVersion.cs b/src/Benchmark.Development/MapsterVersion.cs new file mode 100644 index 00000000..bfca6a69 --- /dev/null +++ b/src/Benchmark.Development/MapsterVersion.cs @@ -0,0 +1,12 @@ +namespace Benchmark.Development +{ + internal static class MapsterVersion + { + + internal static string[] Get() => + [ + "7.4.0", + "9.0.0-pre01" + ]; + } +} diff --git a/src/Mapster/Directory.Build.props b/src/Mapster/Directory.Build.props new file mode 100644 index 00000000..50586750 --- /dev/null +++ b/src/Mapster/Directory.Build.props @@ -0,0 +1,14 @@ + + + + false + + + true + false + + + true + false + + \ No newline at end of file From 1abcb041742976ef704bf36a8f656e718adeb0a5 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 22 Dec 2025 14:12:39 +0500 Subject: [PATCH 355/363] chore!: retarget to netstandard2.0 --- src/Mapster.Core/Mapster.Core.csproj | 4 ++-- src/Sample.CodeGen/Sample.CodeGen.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index c3d159a9..e7ba5620 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -1,9 +1,9 @@  Lightweight library for Mapster and Mapster CodeGen - net9.0;net8.0; + netstandard2.0 Mapster.Core - mapster + Mapster 9.0.0-pre01 enable true diff --git a/src/Sample.CodeGen/Sample.CodeGen.csproj b/src/Sample.CodeGen/Sample.CodeGen.csproj index 9c0bf793..78092145 100644 --- a/src/Sample.CodeGen/Sample.CodeGen.csproj +++ b/src/Sample.CodeGen/Sample.CodeGen.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; enable From 0d0019446c4844ea06ed30cb568e85560a06a91a Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 22 Dec 2025 14:13:34 +0500 Subject: [PATCH 356/363] chore: target to net10 --- .github/workflows/dotnet-buildandtest.yml | 3 ++- src/ExpressionDebugger/ExpressionDebugger.csproj | 2 +- src/ExpressionTranslator/ExpressionTranslator.csproj | 2 +- src/Mapster.Async.Tests/Mapster.Async.Tests.csproj | 2 +- src/Mapster.Async/Mapster.Async.csproj | 2 +- .../Mapster.DependencyInjection.Tests.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj | 6 +++--- src/Mapster.Tests/Mapster.Tests.csproj | 2 +- src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 4 ++-- src/Sample.AspNetCore/Sample.AspNetCore.csproj | 2 +- src/TemplateTest/TemplateTest.csproj | 2 +- 21 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.github/workflows/dotnet-buildandtest.yml b/.github/workflows/dotnet-buildandtest.yml index 89798aba..32648a71 100644 --- a/.github/workflows/dotnet-buildandtest.yml +++ b/.github/workflows/dotnet-buildandtest.yml @@ -19,11 +19,12 @@ jobs: dotnet-version: | 8.x 9.x + 10.x - name: Show dotnet version run: | dotnet --list-sdks dotnet --list-runtimes - name: Build with dotnet run: dotnet build ./src/Mapster.sln - - name: Run tests on .NET 9.0 + - name: Run tests on .NET 10.0 run: dotnet test --verbosity normal ./src/Mapster.sln diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index bc3bd8d3..5b182918 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0 + net10.0;net9.0;net8.0; True Chaowlert Chaisrichalermpol Step into debugging from linq expressions diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index ece11ace..819ee568 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0 + net10.0;net9.0;net8.0 True Chaowlert Chaisrichalermpol Translate from linq expressions to C# code diff --git a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj index f2f0bbf1..0e4001f1 100644 --- a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj +++ b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; false diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index 75b9eebf..ae04977c 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; Async supports for Mapster true Mapster;Async diff --git a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj index 53f9a5be..36ab3717 100644 --- a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj +++ b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; false diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 1b945dce..013ea7e6 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; Dependency Injection supports for Mapster true Mapster;DependencyInjection diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index 16e42db5..7458113e 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; EF6 plugin for Mapster true Mapster;EF6 diff --git a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj index d3729dfd..8c6e5896 100644 --- a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj +++ b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; false diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index 43cd952f..75a57a01 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; EFCore plugin for Mapster true Mapster;EFCore diff --git a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj index ca5fda1f..981bbe08 100644 --- a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj +++ b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; false diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index 6127eb37..1139acfe 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; Immutable collection supports for Mapster true Mapster;Immutable diff --git a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj index 2b0c5b6c..7fefc86d 100644 --- a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj +++ b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; false diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 1d7cda43..d0b265cf 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; Json.net conversion supports for Mapster true Mapster;Json.net diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index 21deb5a6..26d50312 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -1,9 +1,9 @@  - net9.0;net8.0; + netstandard2.0 Source generator to generate mapping using Mapster - source-generator;mapster + Mapster;source-generator true Mapster.SourceGenerator.snk https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json ;$(RestoreAdditionalProjectSources) @@ -23,7 +23,7 @@ - + diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index 438405ee..7cab31e1 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -1,7 +1,7 @@ - net9.0;net8.0; + net10.0;net9.0;net8.0; false Mapster.Tests Mapster.Tests.snk diff --git a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj index e440ff10..7f85e555 100644 --- a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj +++ b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0 + net10.0;net9.0;net8.0 enable enable diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 1dfc90eb..58188644 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -2,7 +2,7 @@ Exe - net9.0;net8.0; + net10.0;net9.0;net8.0; true true dotnet-mapster diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index f8299fd2..580a3a81 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -4,11 +4,11 @@ A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Copyright (c) 2016 Chaowlert Chaisrichalermpol, Eric Swann chaowlert;eric_swann - net9.0;net8.0; + net10.0;net9.0;net8.0; Mapster A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Mapster - Mapper;AutoMapper;Fast;Mapping + Mapster;Mapper;AutoMapper;Fast;Mapping icon.png https://cloud.githubusercontent.com/assets/5763993/26522718/d16f3e42-4330-11e7-9b78-f8c7402624e7.png https://github.com/MapsterMapper/Mapster diff --git a/src/Sample.AspNetCore/Sample.AspNetCore.csproj b/src/Sample.AspNetCore/Sample.AspNetCore.csproj index c24c9428..a681b6fd 100644 --- a/src/Sample.AspNetCore/Sample.AspNetCore.csproj +++ b/src/Sample.AspNetCore/Sample.AspNetCore.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; diff --git a/src/TemplateTest/TemplateTest.csproj b/src/TemplateTest/TemplateTest.csproj index 96caad44..a2b7df8a 100644 --- a/src/TemplateTest/TemplateTest.csproj +++ b/src/TemplateTest/TemplateTest.csproj @@ -1,7 +1,7 @@  - net9.0;net8.0; + net10.0;net9.0;net8.0; enable false From 538f8c9adfaa98e1ddf894bef83c34336198f057 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 22 Dec 2025 14:28:58 +0500 Subject: [PATCH 357/363] chore(version): Updated version numbers to 10.0.0-pre01 --- src/ExpressionDebugger/ExpressionDebugger.csproj | 2 +- src/ExpressionTranslator/ExpressionTranslator.csproj | 2 +- src/Mapster.Async/Mapster.Async.csproj | 2 +- src/Mapster.Core/Mapster.Core.csproj | 2 +- .../Mapster.DependencyInjection.csproj | 2 +- src/Mapster.EF6/Mapster.EF6.csproj | 2 +- src/Mapster.EFCore/Mapster.EFCore.csproj | 2 +- src/Mapster.Immutable/Mapster.Immutable.csproj | 2 +- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 2 +- src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj | 2 +- src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Mapster.csproj | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index 5b182918..59e98815 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -12,7 +12,7 @@ True true ExpressionDebugger.snk - 9.0.0-pre01 + 10.0.0-pre01 https://github.com/chaowlert/ExpressionDebugger/blob/master/LICENSE 8.0 enable diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index 819ee568..d2da32b5 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -12,7 +12,7 @@ True true ExpressionTranslator.snk - 9.0.0-pre01 + 10.0.0-pre01 ExpressionDebugger MIT icon.png diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index ae04977c..8c980a39 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -7,7 +7,7 @@ Mapster;Async true Mapster.Async.snk - 9.0.0-pre01 + 10.0.0-pre01 diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj index e7ba5620..715f5974 100644 --- a/src/Mapster.Core/Mapster.Core.csproj +++ b/src/Mapster.Core/Mapster.Core.csproj @@ -4,7 +4,7 @@ netstandard2.0 Mapster.Core Mapster - 9.0.0-pre01 + 10.0.0-pre01 enable true true diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 013ea7e6..246ad210 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -7,7 +7,7 @@ Mapster;DependencyInjection true Mapster.DependencyInjection.snk - 9.0.0-pre01 + 10.0.0-pre01 diff --git a/src/Mapster.EF6/Mapster.EF6.csproj b/src/Mapster.EF6/Mapster.EF6.csproj index 7458113e..acfe07f5 100644 --- a/src/Mapster.EF6/Mapster.EF6.csproj +++ b/src/Mapster.EF6/Mapster.EF6.csproj @@ -8,7 +8,7 @@ True true Mapster.EF6.snk - 9.0.0-pre01 + 10.0.0-pre01 diff --git a/src/Mapster.EFCore/Mapster.EFCore.csproj b/src/Mapster.EFCore/Mapster.EFCore.csproj index 75a57a01..d721aa9e 100644 --- a/src/Mapster.EFCore/Mapster.EFCore.csproj +++ b/src/Mapster.EFCore/Mapster.EFCore.csproj @@ -8,7 +8,7 @@ True true Mapster.EFCore.snk - 9.0.0-pre01 + 10.0.0-pre01 diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index 1139acfe..1f36f685 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -7,7 +7,7 @@ Mapster;Immutable true Mapster.Immutable.snk - 9.0.0-pre01 + 10.0.0-pre01 enable diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index d0b265cf..3201ca43 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -7,7 +7,7 @@ Mapster;Json.net true Mapster.JsonNet.snk - 9.0.0-pre01 + 10.0.0-pre01 diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index 26d50312..b4b88947 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -7,7 +7,7 @@ true Mapster.SourceGenerator.snk https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json ;$(RestoreAdditionalProjectSources) - 9.0.0-pre01 + 10.0.0-pre01 enable false diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 58188644..8abc6c96 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -10,7 +10,7 @@ Mapster;Tool true Mapster.Tool.snk - 9.0.0-pre01 + 10.0.0-pre01 enable diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index 580a3a81..bc6f2dd1 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -16,7 +16,7 @@ true Mapster - 9.0.0-pre01 + 10.0.0-pre01 enable 1701;1702;8618 From 40de2b0a1c56ac098128f1332286193368cd75b6 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 28 Dec 2025 08:31:35 +0500 Subject: [PATCH 358/363] fix: SignAssembly Mapster --- src/Mapster/Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster/Directory.Build.props b/src/Mapster/Directory.Build.props index 50586750..184391ef 100644 --- a/src/Mapster/Directory.Build.props +++ b/src/Mapster/Directory.Build.props @@ -1,7 +1,7 @@ - - false + true + Mapster.snk true From 1b0a148f0f48c31a3dacf4911fe597dcb3707ddc Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 29 Dec 2025 13:47:42 +0500 Subject: [PATCH 359/363] fix(test): add IsTestProject project property Co-authored-by: DevTKSS --- src/Mapster.Async.Tests/Mapster.Async.Tests.csproj | 2 +- .../Mapster.DependencyInjection.Tests.csproj | 2 +- src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj | 2 +- src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj | 2 +- src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj | 2 +- src/Mapster.Tests/Mapster.Tests.csproj | 1 + src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj | 2 +- src/TemplateTest/TemplateTest.csproj | 2 +- 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj index 0e4001f1..c031e5cf 100644 --- a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj +++ b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj @@ -2,7 +2,7 @@ net10.0;net9.0;net8.0; - + true false diff --git a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj index 36ab3717..4a3b0ed0 100644 --- a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj +++ b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj @@ -2,7 +2,7 @@ net10.0;net9.0;net8.0; - + true false diff --git a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj index 8c6e5896..94549270 100644 --- a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj +++ b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj @@ -2,7 +2,7 @@ net10.0;net9.0;net8.0; - + true false diff --git a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj index 981bbe08..027742ff 100644 --- a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj +++ b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj @@ -2,7 +2,7 @@ net10.0;net9.0;net8.0; - + true false diff --git a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj index 7fefc86d..b4c020b6 100644 --- a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj +++ b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj @@ -2,7 +2,7 @@ net10.0;net9.0;net8.0; - + true false diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index 7cab31e1..d6b78546 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -2,6 +2,7 @@ net10.0;net9.0;net8.0; + true false Mapster.Tests Mapster.Tests.snk diff --git a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj index 7f85e555..702c02e6 100644 --- a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj +++ b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj @@ -4,7 +4,7 @@ net10.0;net9.0;net8.0 enable enable - + true false diff --git a/src/TemplateTest/TemplateTest.csproj b/src/TemplateTest/TemplateTest.csproj index a2b7df8a..ae4dff09 100644 --- a/src/TemplateTest/TemplateTest.csproj +++ b/src/TemplateTest/TemplateTest.csproj @@ -3,7 +3,7 @@ net10.0;net9.0;net8.0; enable - + true false From 28a13ca7f21b947fe44a0f45265427cf0dbc0e57 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 31 Dec 2025 11:16:11 +0500 Subject: [PATCH 360/363] feat: add class Ctor AutoMapping by default --- src/Mapster.Tests/WhenMappingPrimitives.cs | 1 + .../WhenMappingRecordRegression.cs | 64 +++++++++++++++++++ src/Mapster/Adapters/ClassAdapter.cs | 2 +- src/Mapster/Utils/ReflectionUtils.cs | 5 ++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingPrimitives.cs b/src/Mapster.Tests/WhenMappingPrimitives.cs index 1e8fb91e..b41207e6 100644 --- a/src/Mapster.Tests/WhenMappingPrimitives.cs +++ b/src/Mapster.Tests/WhenMappingPrimitives.cs @@ -60,6 +60,7 @@ public void ValueType_String_Object_Is_Always_Primitive() targetDto.Obj.ShouldBeSameAs(sourceDto.Obj); } + [Ignore] [TestMethod] public void Immutable_Class_With_No_Mapping_Should_Error() { diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 9ba98adb..77099693 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -462,6 +462,50 @@ public void RequiredProperty() result.LastName.ShouldBe(source.LastName); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/842 + /// + [TestMethod] + public void ClassCtorAutomapingWorking() + { + var source = new TestRecord() { X = 100 }; + var result = source.Adapt(); + + result.X.ShouldBe(100); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/842 + /// + [TestMethod] + public void ClassCustomCtorWitoutMapNotWorking() + { + TypeAdapterConfig.GlobalSettings.Clear(); + + var source = new TestRecord() { X = 100 }; + + Should.Throw(() => + { + source.Adapt(); + }); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/842 + /// + [TestMethod] + public void ClassCustomCtorWithMapWorking() + { + TypeAdapterConfig.NewConfig() + .Map("y", src => src.X); + + + var source = new TestRecord() { X = 100 }; + var result = source.Adapt(); + + result.X.ShouldBe(100); + } + #region NowNotWorking /// @@ -868,5 +912,25 @@ sealed record TestSealedRecord() sealed record TestSealedRecordPositional(int X); + class AutoCtorDestX + { + public AutoCtorDestX(int x) + { + X = x; + } + + public int X { get; } + } + + class AutoCtorDestYx + { + public AutoCtorDestYx(int y) + { + X = y; + } + + public int X { get; } + } + #endregion TestClasses } diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index de39dec2..85d2c79b 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -54,7 +54,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E { //new TDestination(src.Prop1, src.Prop2) - if (arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) + if (arg.DestinationType.isDefaultCtor() || arg.GetConstructUsing() != null && arg.Settings.MapToConstructor == null) return base.CreateInstantiationExpression(source, destination, arg); ClassMapping? classConverter; diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 5060eb2f..9abe09f8 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -440,5 +440,10 @@ public static bool IsAbstractOrNotPublicCtor(this Type type) return false; } + + public static bool isDefaultCtor(this Type type) + { + return type.GetConstructor(new Type[] { }) is not null ? true : false; + } } } \ No newline at end of file From 82aa199521c5ed9aaddc43b734198cb90fa8094f Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 1 Jan 2026 11:38:28 +0500 Subject: [PATCH 361/363] fix(test): add test with Insaider memeber using custom ctor --- .../WhenMappingRecordRegression.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 77099693..a42166f7 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -506,6 +506,23 @@ public void ClassCustomCtorWithMapWorking() result.X.ShouldBe(100); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/842 + /// + [TestMethod] + public void ClassCustomCtorInsaiderUpdateWorking() + { + TypeAdapterConfig.NewConfig() + .Map("y", src => src.X); + + var source = new InsaiderData() { X = new TestRecord() { X = 100 } }; + var destination = new InsaiderWithCtorDestYx(); // nullable insaider + source.Adapt(destination); + + destination.X.X.ShouldBe(100); + } + + #region NowNotWorking /// @@ -919,7 +936,7 @@ public AutoCtorDestX(int x) X = x; } - public int X { get; } + public int X { get; set; } } class AutoCtorDestYx @@ -932,5 +949,15 @@ public AutoCtorDestYx(int y) public int X { get; } } + class InsaiderData + { + public TestRecord X { set; get; } + } + + class InsaiderWithCtorDestYx + { + public AutoCtorDestYx X { set; get; } + } + #endregion TestClasses } From b3c94dc1531e1ad0c102b40770c79861d27a5e28 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 1 Jan 2026 11:47:46 +0500 Subject: [PATCH 362/363] fix(test): fix misprints --- src/Mapster.Tests/WhenMappingRecordRegression.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index a42166f7..7e4dc97b 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -510,13 +510,13 @@ public void ClassCustomCtorWithMapWorking() /// https://github.com/MapsterMapper/Mapster/issues/842 /// [TestMethod] - public void ClassCustomCtorInsaiderUpdateWorking() + public void ClassCustomCtorInsiderUpdateWorking() { TypeAdapterConfig.NewConfig() .Map("y", src => src.X); - var source = new InsaiderData() { X = new TestRecord() { X = 100 } }; - var destination = new InsaiderWithCtorDestYx(); // nullable insaider + var source = new InsiderData() { X = new TestRecord() { X = 100 } }; + var destination = new InsiderWithCtorDestYx(); // null insider source.Adapt(destination); destination.X.X.ShouldBe(100); @@ -949,12 +949,12 @@ public AutoCtorDestYx(int y) public int X { get; } } - class InsaiderData + class InsiderData { public TestRecord X { set; get; } } - class InsaiderWithCtorDestYx + class InsiderWithCtorDestYx { public AutoCtorDestYx X { set; get; } } From aa61244ad0abec17816fc00e7252a40cf75166b5 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 1 Jan 2026 13:58:27 +0500 Subject: [PATCH 363/363] feat: add AutoProperty without setter automapping when MapToTarget cases --- .../WhenMappingRecordRegression.cs | 15 +++++++++++ src/Mapster/Adapters/BaseClassAdapter.cs | 16 ++++++++++++ src/Mapster/Adapters/ClassAdapter.cs | 26 ++++++++++++++----- src/Mapster/Adapters/RecordTypeAdapter.cs | 16 +----------- 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 7e4dc97b..bdb9b833 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -522,6 +522,21 @@ public void ClassCustomCtorInsiderUpdateWorking() destination.X.X.ShouldBe(100); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/842 + /// + [TestMethod] + public void ClassUpdateAutoPropertyWitoutSetterWorking() + { + var source = new TestRecord() { X = 100 }; + var patch = new TestRecord() { X = 200 }; + var result = source.Adapt(); + + patch.Adapt(result); + + result.X.ShouldBe(200); + } + #region NowNotWorking diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index d134330c..a50c4b73 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -320,6 +320,22 @@ protected virtual ClassModel GetOnlyRequiredPropertySetterModel(CompileArgument return null; } + protected static Expression SetValueTypeAutoPropertyByReflection(MemberMapping member, Expression adapt, ClassModel checkmodel) + { + var modDesinationMemeberName = $"<{member.DestinationMember.Name}>k__BackingField"; + if (checkmodel.Members.Any(x => x.Name == modDesinationMemeberName) == false) // Property is not autoproperty + return Expression.Empty(); + var typeofExpression = Expression.Constant(member.Destination!.Type); + var getPropertyMethod = typeof(Type).GetMethod("GetField", new[] { typeof(string), typeof(BindingFlags) })!; + var getPropertyExpression = Expression.Call(typeofExpression, getPropertyMethod, + Expression.Constant(modDesinationMemeberName), Expression.Constant(BindingFlags.Instance | BindingFlags.NonPublic)); + var setValueMethod = + typeof(FieldInfo).GetMethod("SetValue", new[] { typeof(object), typeof(object) })!; + var memberAsObject = adapt.To(typeof(object)); + return Expression.Call(getPropertyExpression, setValueMethod, + new[] { member.Destination, memberAsObject }); + } + #endregion } } diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 85d2c79b..9e44105d 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -104,14 +104,22 @@ protected override Expression CreateBlockExpression(Expression source, Expressio Dictionary, Expression>>? conditions = null; foreach (var member in members) { - if (!member.UseDestinationValue && member.DestinationMember.SetterModifier == AccessModifier.None) - continue; - var destMember = arg.MapType == MapType.MapToTarget || member.UseDestinationValue ? member.DestinationMember.GetExpression(destination) : null; var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member, destMember); + + if (!member.UseDestinationValue && member.DestinationMember.SetterModifier == AccessModifier.None) + { + if (member.DestinationMember is PropertyModel && arg.MapType == MapType.MapToTarget) + adapt = SetValueTypeAutoPropertyByReflection(member, adapt, classModel); + else + continue; + if (adapt == Expression.Empty()) + continue; + } + if (!member.UseDestinationValue) { if (arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull()) @@ -132,10 +140,14 @@ protected override Expression CreateBlockExpression(Expression source, Expressio //Todo Try catch block should be removed after pull request approved try { - var destinationPropertyInfo = (PropertyInfo)member.DestinationMember.Info!; - adapt = destinationPropertyInfo.IsInitOnly() - ? SetValueByReflection(member, (MemberExpression)adapt) - : member.DestinationMember.SetExpression(destination, adapt); + if (member.DestinationMember.SetterModifier != AccessModifier.None) + { + var destinationPropertyInfo = (PropertyInfo)member.DestinationMember.Info!; + adapt = destinationPropertyInfo.IsInitOnly() + ? SetValueByReflection(member, (MemberExpression)adapt) + : member.DestinationMember.SetExpression(destination, adapt); + } + } catch (Exception e) { diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index 77f75f92..06a3c421 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -294,21 +294,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio return lines.Count > 0 ? (Expression)Expression.Block(lines) : Expression.Empty(); } - protected static Expression SetValueTypeAutoPropertyByReflection(MemberMapping member, Expression adapt, ClassModel checkmodel) - { - var modDesinationMemeberName = $"<{member.DestinationMember.Name}>k__BackingField"; - if (checkmodel.Members.Any(x => x.Name == modDesinationMemeberName) == false) // Property is not autoproperty - return Expression.Empty(); - var typeofExpression = Expression.Constant(member.Destination!.Type); - var getPropertyMethod = typeof(Type).GetMethod("GetField", new[] { typeof(string), typeof(BindingFlags) })!; - var getPropertyExpression = Expression.Call(typeofExpression, getPropertyMethod, - Expression.Constant(modDesinationMemeberName), Expression.Constant(BindingFlags.Instance | BindingFlags.NonPublic)); - var setValueMethod = - typeof(FieldInfo).GetMethod("SetValue", new[] { typeof(object), typeof(object) })!; - var memberAsObject = adapt.To(typeof(object)); - return Expression.Call(getPropertyExpression, setValueMethod, - new[] { member.Destination, memberAsObject }); - } + } }