Skip to content

Commit 7384622

Browse files
authored
Merge pull request #375 from andrei-traktatovich/feature/init-readonly-props-in-non-readonly-interface
Init read-only properties when mapping with a non-readonly interface fixes #374
2 parents 46de551 + f21d8ea commit 7384622

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

src/Mapster.Tests/WhenMappingToInterface.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,32 @@ public void MapToInheritedInterfaceWithoutProperties()
235235
idto.UnmappedSource.ShouldBeNull();
236236
}
237237

238+
[TestMethod]
239+
public void MappingToInterfaceWithWritableProps()
240+
{
241+
var source = new PropertyInitializationTestSource { Property1 = 42, Property2 = 43 };
242+
var target = source.Adapt<IInterfaceWithWritableProperties>();
243+
244+
target.ShouldNotBeNull();
245+
target.ShouldSatisfyAllConditions(
246+
() => target.Property1.ShouldBe(source.Property1),
247+
() => target.Property2.ShouldBe(source.Property2)
248+
);
249+
}
250+
251+
[TestMethod]
252+
public void MappingToInteraceWithReadonlyProps_AllPropsInitialized()
253+
{
254+
var source = new PropertyInitializationTestSource { Property1 = 42, Property2 = 43 };
255+
var target = source.Adapt<IReadonlyInterface>();
256+
257+
target.ShouldNotBeNull();
258+
target.ShouldSatisfyAllConditions(
259+
() => target.Property1.ShouldBe(source.Property1),
260+
() => target.Property2.ShouldBe(source.Property2)
261+
);
262+
}
263+
238264
public interface IInheritedDtoWithoutProperties : IInheritedDto
239265
{
240266
}
@@ -325,5 +351,24 @@ public class ComplexDto
325351
public IEnumerable<int> Ints { get; set; }
326352
public int[] IntArr { get; set; }
327353
}
354+
355+
public interface IReadonlyInterface
356+
{
357+
int Property1 { get; }
358+
int Property2 { get; }
359+
}
360+
361+
public interface IInterfaceWithWritableProperties
362+
{
363+
int Property1 { get; set; }
364+
int Property2 { get; }
365+
}
366+
367+
public class PropertyInitializationTestSource
368+
{
369+
public int Property1 { get; set; }
370+
public int Property2 { get; set; }
371+
}
372+
328373
}
329374
}

src/Mapster/Utils/DynamicTypeGenerator.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ private static Type CreateTypeForInterface(Type interfaceType)
5858

5959
var args = new List<FieldBuilder>();
6060
int propCount = 0;
61+
var hasReadonlyProps = false;
62+
6163
foreach (Type currentInterface in interfaceType.GetAllInterfaces())
6264
{
6365
builder.AddInterfaceImplementation(currentInterface);
@@ -66,8 +68,8 @@ private static Type CreateTypeForInterface(Type interfaceType)
6668
propCount++;
6769
FieldBuilder propField = builder.DefineField("_" + MapsterHelper.CamelCase(prop.Name), prop.PropertyType, FieldAttributes.Private);
6870
CreateProperty(currentInterface, builder, prop, propField);
69-
if (!prop.CanWrite)
70-
args.Add(propField);
71+
if (!prop.CanWrite) hasReadonlyProps = true;
72+
args.Add(propField);
7173
}
7274
foreach (MethodInfo method in currentInterface.GetMethods())
7375
{
@@ -82,7 +84,7 @@ private static Type CreateTypeForInterface(Type interfaceType)
8284
if (propCount == 0)
8385
throw new InvalidOperationException($"No default constructor for type '{interfaceType.Name}', please use 'ConstructUsing' or 'MapWith'");
8486

85-
if (args.Count == propCount)
87+
if (hasReadonlyProps)
8688
{
8789
var ctorBuilder = builder.DefineConstructor(MethodAttributes.Public,
8890
CallingConventions.Standard,

src/Mapster/Utils/ReflectionUtils.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ public static bool IsRecordType(this Type type)
170170

171171
var props = type.GetFieldsAndProperties().ToList();
172172

173-
//interface, all props must be readonly
173+
//interface with readonly props
174174
if (type.GetTypeInfo().IsInterface &&
175-
props.All(p => p.SetterModifier != AccessModifier.Public))
175+
props.Any(p => p.SetterModifier != AccessModifier.Public))
176176
return true;
177177

178178
//1 constructor

0 commit comments

Comments
 (0)