Struct handling with readonly fields (wip)
This commit is contained in:
parent
b44f5b2fe5
commit
9a719a0226
|
@ -11,7 +11,7 @@ namespace MapTo
|
||||||
internal ClassMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
|
internal ClassMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
|
||||||
: base(compilation, sourceGenerationOptions, typeSyntax) { }
|
: base(compilation, sourceGenerationOptions, typeSyntax) { }
|
||||||
|
|
||||||
protected override ImmutableArray<MappedProperty> GetMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
|
protected override ImmutableArray<MappedProperty> GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
|
||||||
{
|
{
|
||||||
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
||||||
|
|
||||||
|
@ -23,5 +23,18 @@ namespace MapTo
|
||||||
.Where(mappedProperty => mappedProperty is not null)
|
.Where(mappedProperty => mappedProperty is not null)
|
||||||
.ToImmutableArray()!;
|
.ToImmutableArray()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ImmutableArray<MappedProperty> GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
|
||||||
|
{
|
||||||
|
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
||||||
|
|
||||||
|
return typeSymbol
|
||||||
|
.GetAllMembers()
|
||||||
|
.OfType<IPropertySymbol>()
|
||||||
|
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol))
|
||||||
|
.Select(property => MapProperty(typeSymbol, sourceProperties, property))
|
||||||
|
.Where(mappedProperty => mappedProperty is not null)
|
||||||
|
.ToImmutableArray()!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
using MapTo.Sources;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MapTo.Extensions
|
||||||
|
{
|
||||||
|
internal static class CommonExtensions
|
||||||
|
{
|
||||||
|
internal static SourceBuilder WriteComment(this SourceBuilder builder, string comment)
|
||||||
|
{
|
||||||
|
return builder.WriteLine($"// {comment}");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SourceBuilder WriteMappedProperties(this SourceBuilder builder, System.Collections.Immutable.ImmutableArray<MappedProperty> mappedProperties)
|
||||||
|
{
|
||||||
|
foreach (var item in mappedProperties)
|
||||||
|
{
|
||||||
|
builder.WriteComment($"Name: {item.Name}");
|
||||||
|
builder.WriteComment($"Type: {item.Type}");
|
||||||
|
builder.WriteComment($"MappedSourcePropertyTypeName: {item.MappedSourcePropertyTypeName}");
|
||||||
|
builder.WriteComment($"IsEnumerable: {item.IsEnumerable}");
|
||||||
|
builder.WriteComment($"SourcePropertyName: {item.SourcePropertyName}");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,12 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
namespace MapTo
|
namespace MapTo
|
||||||
{
|
{
|
||||||
|
internal static class MappingContextExtensions
|
||||||
|
{
|
||||||
|
internal static ImmutableArray<MappedProperty> GetReadOnlyMappedProperties(this ImmutableArray<MappedProperty> mappedProperties) => mappedProperties.Where(p => p.isReadOnly).ToImmutableArray()!;
|
||||||
|
internal static ImmutableArray<MappedProperty> GetWritableMappedProperties(this ImmutableArray<MappedProperty> mappedProperties) => mappedProperties.Where(p => !p.isReadOnly).ToImmutableArray()!;
|
||||||
|
}
|
||||||
|
|
||||||
internal abstract class MappingContext
|
internal abstract class MappingContext
|
||||||
{
|
{
|
||||||
private readonly List<SymbolDisplayPart> _ignoredNamespaces;
|
private readonly List<SymbolDisplayPart> _ignoredNamespaces;
|
||||||
|
@ -101,7 +107,8 @@ namespace MapTo
|
||||||
return sourceProperties.SingleOrDefault(p => p.Name == propertyName);
|
return sourceProperties.SingleOrDefault(p => p.Name == propertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract ImmutableArray<MappedProperty> GetMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass);
|
protected abstract ImmutableArray<MappedProperty> GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass);
|
||||||
|
protected abstract ImmutableArray<MappedProperty> GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass);
|
||||||
|
|
||||||
protected INamedTypeSymbol? GetSourceTypeSymbol(TypeDeclarationSyntax typeDeclarationSyntax, SemanticModel? semanticModel = null) =>
|
protected INamedTypeSymbol? GetSourceTypeSymbol(TypeDeclarationSyntax typeDeclarationSyntax, SemanticModel? semanticModel = null) =>
|
||||||
GetSourceTypeSymbol(typeDeclarationSyntax.GetAttribute(MapFromAttributeSource.AttributeName), semanticModel);
|
GetSourceTypeSymbol(typeDeclarationSyntax.GetAttribute(MapFromAttributeSource.AttributeName), semanticModel);
|
||||||
|
@ -138,7 +145,6 @@ namespace MapTo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
string? converterFullyQualifiedName = null;
|
string? converterFullyQualifiedName = null;
|
||||||
var converterParameters = ImmutableArray<string>.Empty;
|
var converterParameters = ImmutableArray<string>.Empty;
|
||||||
ITypeSymbol? mappedSourcePropertyType = null;
|
ITypeSymbol? mappedSourcePropertyType = null;
|
||||||
|
@ -165,10 +171,38 @@ namespace MapTo
|
||||||
sourceProperty.Name,
|
sourceProperty.Name,
|
||||||
ToQualifiedDisplayName(mappedSourcePropertyType),
|
ToQualifiedDisplayName(mappedSourcePropertyType),
|
||||||
ToQualifiedDisplayName(enumerableTypeArgumentType),
|
ToQualifiedDisplayName(enumerableTypeArgumentType),
|
||||||
(sourceProperty as IPropertySymbol).IsReadOnly);
|
(property as IPropertySymbol).IsReadOnly);
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
protected virtual MappedProperty? MapPropertySimple(ISymbol sourceTypeSymbol, ISymbol property)
|
||||||
|
{
|
||||||
|
if (!property.TryGetTypeSymbol(out var propertyType))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string? converterFullyQualifiedName = null;
|
||||||
|
var converterParameters = ImmutableArray<string>.Empty;
|
||||||
|
ITypeSymbol? mappedSourcePropertyType = null;
|
||||||
|
ITypeSymbol? enumerableTypeArgumentType = null;
|
||||||
|
|
||||||
|
|
||||||
|
AddUsingIfRequired(propertyType);
|
||||||
|
AddUsingIfRequired(enumerableTypeArgumentType);
|
||||||
|
AddUsingIfRequired(mappedSourcePropertyType);
|
||||||
|
|
||||||
|
return new MappedProperty(
|
||||||
|
property.Name,
|
||||||
|
ToQualifiedDisplayName(propertyType) ?? propertyType.Name,
|
||||||
|
converterFullyQualifiedName,
|
||||||
|
converterParameters.ToImmutableArray(),
|
||||||
|
property.Name,
|
||||||
|
ToQualifiedDisplayName(mappedSourcePropertyType),
|
||||||
|
ToQualifiedDisplayName(enumerableTypeArgumentType),
|
||||||
|
(property as IPropertySymbol).IsReadOnly);
|
||||||
|
;
|
||||||
|
}
|
||||||
protected bool TryGetMapTypeConverter(ISymbol property, IPropertySymbol sourceProperty, out string? converterFullyQualifiedName,
|
protected bool TryGetMapTypeConverter(ISymbol property, IPropertySymbol sourceProperty, out string? converterFullyQualifiedName,
|
||||||
out ImmutableArray<string> converterParameters)
|
out ImmutableArray<string> converterParameters)
|
||||||
{
|
{
|
||||||
|
@ -265,7 +299,7 @@ namespace MapTo
|
||||||
var isTypeInheritFromMappedBaseClass = IsTypeInheritFromMappedBaseClass(semanticModel);
|
var isTypeInheritFromMappedBaseClass = IsTypeInheritFromMappedBaseClass(semanticModel);
|
||||||
var shouldGenerateSecondaryConstructor = ShouldGenerateSecondaryConstructor(semanticModel, sourceTypeSymbol);
|
var shouldGenerateSecondaryConstructor = ShouldGenerateSecondaryConstructor(semanticModel, sourceTypeSymbol);
|
||||||
|
|
||||||
var mappedProperties = GetMappedProperties(typeSymbol, sourceTypeSymbol, isTypeInheritFromMappedBaseClass);
|
var mappedProperties = GetSourceMappedProperties(typeSymbol, sourceTypeSymbol, isTypeInheritFromMappedBaseClass);
|
||||||
if (!mappedProperties.Any())
|
if (!mappedProperties.Any())
|
||||||
{
|
{
|
||||||
AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyFoundError(TypeSyntax.GetLocation(), typeSymbol, sourceTypeSymbol));
|
AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyFoundError(TypeSyntax.GetLocation(), typeSymbol, sourceTypeSymbol));
|
||||||
|
@ -274,6 +308,8 @@ namespace MapTo
|
||||||
|
|
||||||
AddUsingIfRequired(mappedProperties.Any(p => p.IsEnumerable), "System.Linq");
|
AddUsingIfRequired(mappedProperties.Any(p => p.IsEnumerable), "System.Linq");
|
||||||
|
|
||||||
|
var allProperties = GetTypeMappedProperties(sourceTypeSymbol, typeSymbol , isTypeInheritFromMappedBaseClass);
|
||||||
|
|
||||||
return new MappingModel(
|
return new MappingModel(
|
||||||
SourceGenerationOptions,
|
SourceGenerationOptions,
|
||||||
TypeSyntax.GetNamespace(),
|
TypeSyntax.GetNamespace(),
|
||||||
|
@ -284,11 +320,14 @@ namespace MapTo
|
||||||
sourceTypeIdentifierName,
|
sourceTypeIdentifierName,
|
||||||
sourceTypeSymbol.ToDisplayString(),
|
sourceTypeSymbol.ToDisplayString(),
|
||||||
mappedProperties,
|
mappedProperties,
|
||||||
|
allProperties,
|
||||||
isTypeInheritFromMappedBaseClass,
|
isTypeInheritFromMappedBaseClass,
|
||||||
Usings,
|
Usings,
|
||||||
shouldGenerateSecondaryConstructor);
|
shouldGenerateSecondaryConstructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private INamedTypeSymbol? GetTypeConverterBaseInterface(ITypeSymbol converterTypeSymbol, ISymbol property, IPropertySymbol sourceProperty)
|
private INamedTypeSymbol? GetTypeConverterBaseInterface(ITypeSymbol converterTypeSymbol, ISymbol property, IPropertySymbol sourceProperty)
|
||||||
{
|
{
|
||||||
if (!property.TryGetTypeSymbol(out var propertyType))
|
if (!property.TryGetTypeSymbol(out var propertyType))
|
||||||
|
|
|
@ -44,7 +44,8 @@ namespace MapTo
|
||||||
string SourceNamespace,
|
string SourceNamespace,
|
||||||
string SourceTypeIdentifierName,
|
string SourceTypeIdentifierName,
|
||||||
string SourceTypeFullName,
|
string SourceTypeFullName,
|
||||||
ImmutableArray<MappedProperty> MappedProperties,
|
ImmutableArray<MappedProperty> SourceProperties,
|
||||||
|
ImmutableArray<MappedProperty> TypeProperties,
|
||||||
bool HasMappedBaseClass,
|
bool HasMappedBaseClass,
|
||||||
ImmutableArray<string> Usings,
|
ImmutableArray<string> Usings,
|
||||||
bool GenerateSecondaryConstructor
|
bool GenerateSecondaryConstructor
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace MapTo
|
||||||
internal RecordMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
|
internal RecordMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
|
||||||
: base(compilation, sourceGenerationOptions, typeSyntax) { }
|
: base(compilation, sourceGenerationOptions, typeSyntax) { }
|
||||||
|
|
||||||
protected override ImmutableArray<MappedProperty> GetMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
|
protected override ImmutableArray<MappedProperty> GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
|
||||||
{
|
{
|
||||||
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
||||||
return typeSymbol.GetMembers()
|
return typeSymbol.GetMembers()
|
||||||
|
@ -24,5 +24,19 @@ namespace MapTo
|
||||||
.Where(mappedProperty => mappedProperty is not null)
|
.Where(mappedProperty => mappedProperty is not null)
|
||||||
.ToImmutableArray()!;
|
.ToImmutableArray()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ImmutableArray<MappedProperty> GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
|
||||||
|
{
|
||||||
|
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
||||||
|
return typeSymbol.GetMembers()
|
||||||
|
.OfType<IMethodSymbol>()
|
||||||
|
.OrderByDescending(s => s.Parameters.Length)
|
||||||
|
.First(s => s.Name == ".ctor")
|
||||||
|
.Parameters
|
||||||
|
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol))
|
||||||
|
.Select(property => MapProperty(typeSymbol, sourceProperties, property))
|
||||||
|
.Where(mappedProperty => mappedProperty is not null)
|
||||||
|
.ToImmutableArray()!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,35 +21,19 @@ namespace MapTo.Sources
|
||||||
.WriteLine($"partial class {model.TypeIdentifierName}")
|
.WriteLine($"partial class {model.TypeIdentifierName}")
|
||||||
.WriteOpeningBracket();
|
.WriteOpeningBracket();
|
||||||
|
|
||||||
// Class body
|
|
||||||
/*if (model.GenerateSecondaryConstructor)
|
|
||||||
{
|
|
||||||
builder
|
|
||||||
.GenerateSecondaryConstructor(model)
|
|
||||||
.WriteLine();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.GeneratePrivateConstructor(model)
|
.GeneratePublicConstructor(model)
|
||||||
.WriteLine();
|
.WriteLine();
|
||||||
|
|
||||||
if(!PropertiesAreReadOnly(model))
|
if(!AllPropertiesReadOnly(model))
|
||||||
{
|
{
|
||||||
builder.GenerateUpdateMethod(model);
|
builder.GenerateUpdateMethod(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
//.GenerateFactoryMethod(model)
|
|
||||||
.GenerateUpdateMethod(model)
|
|
||||||
|
|
||||||
// End class declaration
|
|
||||||
.WriteClosingBracket()
|
.WriteClosingBracket()
|
||||||
.WriteLine()
|
.WriteLine()
|
||||||
|
|
||||||
// Extension class declaration
|
|
||||||
//.GenerateSourceTypeExtensionClass(model)
|
|
||||||
|
|
||||||
// End namespace declaration
|
|
||||||
.WriteClosingBracket();
|
.WriteClosingBracket();
|
||||||
|
|
||||||
return new(builder.ToString(), $"{model.Namespace}.{model.TypeIdentifierName}.g.cs");
|
return new(builder.ToString(), $"{model.Namespace}.{model.TypeIdentifierName}.g.cs");
|
||||||
|
@ -74,7 +58,8 @@ namespace MapTo.Sources
|
||||||
.WriteLine($" : this(new {MappingContextSource.ClassName}(), {sourceClassParameterName}) {{ }}");
|
.WriteLine($" : this(new {MappingContextSource.ClassName}(), {sourceClassParameterName}) {{ }}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SourceBuilder GeneratePrivateConstructor(this SourceBuilder builder, MappingModel model)
|
|
||||||
|
private static SourceBuilder GeneratePublicConstructor(this SourceBuilder builder, MappingModel model)
|
||||||
{
|
{
|
||||||
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
||||||
const string mappingContextParameterName = "context";
|
const string mappingContextParameterName = "context";
|
||||||
|
@ -82,25 +67,27 @@ namespace MapTo.Sources
|
||||||
var baseConstructor = /*model.HasMappedBaseClass ? $" : base({mappingContextParameterName}, {sourceClassParameterName})" :*/ string.Empty;
|
var baseConstructor = /*model.HasMappedBaseClass ? $" : base({mappingContextParameterName}, {sourceClassParameterName})" :*/ string.Empty;
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.WriteLine($"public {model.TypeIdentifierName}({model.SourceType} {sourceClassParameterName}){baseConstructor}")
|
.WriteLine($"public {model.TypeIdentifierName}({model.SourceType} {sourceClassParameterName}){baseConstructor}")
|
||||||
.WriteOpeningBracket()
|
.WriteOpeningBracket()
|
||||||
//.WriteLine($"if ({mappingContextParameterName} == null) throw new ArgumentNullException(nameof({mappingContextParameterName}));")
|
//.WriteLine($"if ({mappingContextParameterName} == null) throw new ArgumentNullException(nameof({mappingContextParameterName}));")
|
||||||
//.WriteLine($"if ({sourceClassParameterName} == null) throw new ArgumentNullException(nameof({sourceClassParameterName}));")
|
//.WriteLine($"if ({sourceClassParameterName} == null) throw new ArgumentNullException(nameof({sourceClassParameterName}));")
|
||||||
//.WriteLine()
|
//.WriteLine()
|
||||||
//.WriteLine($"{mappingContextParameterName}.{MappingContextSource.RegisterMethodName}({sourceClassParameterName}, this);")
|
//.WriteLine($"{mappingContextParameterName}.{MappingContextSource.RegisterMethodName}({sourceClassParameterName}, this);")
|
||||||
.WriteLine().
|
|
||||||
|
|
||||||
WriteProperties( model, sourceClassParameterName, mappingContextParameterName);
|
.WriteProperties( model.SourceProperties, sourceClassParameterName, mappingContextParameterName, false);
|
||||||
|
|
||||||
// End constructor declaration
|
// End constructor declaration
|
||||||
return builder.WriteClosingBracket();
|
return builder.WriteClosingBracket();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SourceBuilder WriteProperties(this SourceBuilder builder, MappingModel model,
|
private static SourceBuilder WriteProperties(this SourceBuilder builder, System.Collections.Immutable.ImmutableArray<MappedProperty> properties,
|
||||||
string? sourceClassParameterName, string mappingContextParameterName)
|
string? sourceClassParameterName, string mappingContextParameterName, bool fromUpdate)
|
||||||
{
|
{
|
||||||
foreach (var property in model.MappedProperties)
|
|
||||||
|
foreach (var property in properties)
|
||||||
{
|
{
|
||||||
|
if (property.isReadOnly && fromUpdate) continue;
|
||||||
|
|
||||||
if (property.TypeConverter is null)
|
if (property.TypeConverter is null)
|
||||||
{
|
{
|
||||||
if (property.IsEnumerable)
|
if (property.IsEnumerable)
|
||||||
|
@ -130,9 +117,9 @@ namespace MapTo.Sources
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool PropertiesAreReadOnly(MappingModel model)
|
private static bool AllPropertiesReadOnly(MappingModel model)
|
||||||
{
|
{
|
||||||
foreach (var property in model.MappedProperties)
|
foreach (var property in model.SourceProperties)
|
||||||
{
|
{
|
||||||
if (!property.isReadOnly) return false;
|
if (!property.isReadOnly) return false;
|
||||||
}
|
}
|
||||||
|
@ -140,19 +127,6 @@ namespace MapTo.Sources
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SourceBuilder GenerateFactoryMethod(this SourceBuilder builder, MappingModel model)
|
|
||||||
{
|
|
||||||
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
|
||||||
|
|
||||||
return builder
|
|
||||||
.GenerateConvertorMethodsXmlDocs(model, sourceClassParameterName)
|
|
||||||
.WriteLineIf(model.Options.SupportNullableStaticAnalysis, $"[return: NotNullIfNotNull(\"{sourceClassParameterName}\")]")
|
|
||||||
.WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static {model.TypeIdentifierName}{model.Options.NullableReferenceSyntax} From({model.SourceType}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteLine($"return {sourceClassParameterName} == null ? null : {MappingContextSource.ClassName}.{MappingContextSource.FactoryMethodName}<{model.SourceType}, {model.TypeIdentifierName}>({sourceClassParameterName});")
|
|
||||||
.WriteClosingBracket();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SourceBuilder GenerateUpdateMethod(this SourceBuilder builder, MappingModel model)
|
private static SourceBuilder GenerateUpdateMethod(this SourceBuilder builder, MappingModel model)
|
||||||
{
|
{
|
||||||
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
||||||
|
@ -161,7 +135,7 @@ namespace MapTo.Sources
|
||||||
.GenerateUpdaterMethodsXmlDocs(model, sourceClassParameterName)
|
.GenerateUpdaterMethodsXmlDocs(model, sourceClassParameterName)
|
||||||
.WriteLine($"public void Update({model.SourceType}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})")
|
.WriteLine($"public void Update({model.SourceType}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})")
|
||||||
.WriteOpeningBracket()
|
.WriteOpeningBracket()
|
||||||
.WriteProperties( model, sourceClassParameterName,"context" )
|
.WriteProperties( model.SourceProperties.GetWritableMappedProperties(), sourceClassParameterName,"context", true )
|
||||||
.WriteClosingBracket();
|
.WriteClosingBracket();
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
|
@ -198,15 +172,6 @@ namespace MapTo.Sources
|
||||||
.WriteLine($"/// <param name=\"{sourceClassParameterName}\">The instance of <see cref=\"{model.SourceType}\"/> to use as source.</param>");
|
.WriteLine($"/// <param name=\"{sourceClassParameterName}\">The instance of <see cref=\"{model.SourceType}\"/> to use as source.</param>");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SourceBuilder GenerateSourceTypeExtensionClass(this SourceBuilder builder, MappingModel model)
|
|
||||||
{
|
|
||||||
return builder
|
|
||||||
.WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static partial class {model.SourceTypeIdentifierName}To{model.TypeIdentifierName}Extensions")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.GenerateSourceTypeExtensionMethod(model)
|
|
||||||
.WriteClosingBracket();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SourceBuilder GenerateSourceTypeExtensionMethod(this SourceBuilder builder, MappingModel model)
|
private static SourceBuilder GenerateSourceTypeExtensionMethod(this SourceBuilder builder, MappingModel model)
|
||||||
{
|
{
|
||||||
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
||||||
|
|
|
@ -96,9 +96,9 @@ namespace MapTo.Sources
|
||||||
private static SourceBuilder WriteProperties(this SourceBuilder builder, MappingModel model, string sourceClassParameterName,
|
private static SourceBuilder WriteProperties(this SourceBuilder builder, MappingModel model, string sourceClassParameterName,
|
||||||
string mappingContextParameterName)
|
string mappingContextParameterName)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < model.MappedProperties.Length; i++)
|
for (var i = 0; i < model.SourceProperties.Length; i++)
|
||||||
{
|
{
|
||||||
var property = model.MappedProperties[i];
|
var property = model.SourceProperties[i];
|
||||||
if (property.TypeConverter is null)
|
if (property.TypeConverter is null)
|
||||||
{
|
{
|
||||||
if (property.IsEnumerable)
|
if (property.IsEnumerable)
|
||||||
|
@ -123,7 +123,7 @@ namespace MapTo.Sources
|
||||||
$"{property.Name}: new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.SourcePropertyName}, {parameters})");
|
$"{property.Name}: new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.SourcePropertyName}, {parameters})");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < model.MappedProperties.Length - 1)
|
if (i < model.SourceProperties.Length - 1)
|
||||||
{
|
{
|
||||||
builder.Write(", ");
|
builder.Write(", ");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using MapTo.Extensions;
|
using MapTo.Extensions;
|
||||||
using static MapTo.Sources.Constants;
|
using static MapTo.Sources.Constants;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MapTo.Sources
|
namespace MapTo.Sources
|
||||||
{
|
{
|
||||||
|
@ -19,12 +20,18 @@ namespace MapTo.Sources
|
||||||
|
|
||||||
// Class declaration
|
// Class declaration
|
||||||
.WriteLine($"partial struct {model.TypeIdentifierName}")
|
.WriteLine($"partial struct {model.TypeIdentifierName}")
|
||||||
.WriteOpeningBracket();
|
.WriteOpeningBracket()
|
||||||
|
.WriteLine()
|
||||||
|
|
||||||
// Class body
|
// Class body
|
||||||
|
.GeneratePublicConstructor(model);
|
||||||
|
|
||||||
builder
|
if (!AllPropertiesAreReadOnly(model))
|
||||||
.GeneratePrivateConstructor(model)
|
{
|
||||||
|
builder.GenerateUpdateMethod(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
.WriteLine()
|
.WriteLine()
|
||||||
// End class declaration
|
// End class declaration
|
||||||
.WriteClosingBracket()
|
.WriteClosingBracket()
|
||||||
|
@ -35,6 +42,16 @@ namespace MapTo.Sources
|
||||||
|
|
||||||
return new(builder.ToString(), $"{model.Namespace}.{model.TypeIdentifierName}.g.cs");
|
return new(builder.ToString(), $"{model.Namespace}.{model.TypeIdentifierName}.g.cs");
|
||||||
}
|
}
|
||||||
|
private static bool AllPropertiesAreReadOnly(MappingModel model)
|
||||||
|
{
|
||||||
|
foreach (var property in model.SourceProperties)
|
||||||
|
{
|
||||||
|
if (!property.isReadOnly) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static SourceBuilder GenerateSecondaryConstructor(this SourceBuilder builder, MappingModel model)
|
private static SourceBuilder GenerateSecondaryConstructor(this SourceBuilder builder, MappingModel model)
|
||||||
{
|
{
|
||||||
|
@ -55,31 +72,47 @@ namespace MapTo.Sources
|
||||||
.WriteLine($" : this(new {MappingContextSource.ClassName}(), {sourceClassParameterName}) {{ }}");
|
.WriteLine($" : this(new {MappingContextSource.ClassName}(), {sourceClassParameterName}) {{ }}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SourceBuilder GeneratePrivateConstructor(this SourceBuilder builder, MappingModel model)
|
|
||||||
|
private static SourceBuilder GeneratePublicConstructor(this SourceBuilder builder, MappingModel model)
|
||||||
{
|
{
|
||||||
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
||||||
const string mappingContextParameterName = "context";
|
const string mappingContextParameterName = "context";
|
||||||
|
|
||||||
var baseConstructor = model.HasMappedBaseClass ? $" : base({mappingContextParameterName}, {sourceClassParameterName})" : string.Empty;
|
var baseConstructor = /*model.HasMappedBaseClass ? $" : base({mappingContextParameterName}, {sourceClassParameterName})" :*/ string.Empty;
|
||||||
|
|
||||||
|
var readOnlyProperties = model.TypeProperties.GetReadOnlyMappedProperties();
|
||||||
|
|
||||||
|
var readOnlyFields = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < readOnlyProperties.Length; i++)
|
||||||
|
{
|
||||||
|
var property = readOnlyProperties[i];
|
||||||
|
readOnlyFields += $"{property.Type} {property.SourcePropertyName.ToCamelCase()}";
|
||||||
|
if (i != readOnlyProperties.Length - 1) readOnlyFields += " ,";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.WriteLine($"public {model.TypeIdentifierName}({MappingContextSource.ClassName} {mappingContextParameterName}, {model.SourceType} {sourceClassParameterName}){baseConstructor}")
|
.WriteLine($"public {model.TypeIdentifierName}({model.SourceType} {sourceClassParameterName}{(string.IsNullOrEmpty(readOnlyFields) ? "" : $", {readOnlyFields}")}){baseConstructor}")
|
||||||
.WriteOpeningBracket()
|
.WriteOpeningBracket()
|
||||||
.WriteLine()
|
.TryWriteProperties(model.SourceProperties, readOnlyProperties, sourceClassParameterName, mappingContextParameterName, false);
|
||||||
.WriteLine($"{mappingContextParameterName}.{MappingContextSource.RegisterMethodName}({sourceClassParameterName}, this);")
|
|
||||||
.WriteLine().
|
|
||||||
|
|
||||||
WriteProperties( model, sourceClassParameterName, mappingContextParameterName);
|
|
||||||
|
|
||||||
// End constructor declaration
|
// End constructor declaration
|
||||||
return builder.WriteClosingBracket();
|
return builder.WriteClosingBracket();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SourceBuilder WriteProperties(this SourceBuilder builder, MappingModel model,
|
private static SourceBuilder TryWriteProperties(this SourceBuilder builder, System.Collections.Immutable.ImmutableArray<MappedProperty> properties, System.Collections.Immutable.ImmutableArray<MappedProperty>? otherProperties,
|
||||||
string? sourceClassParameterName, string mappingContextParameterName)
|
string? sourceClassParameterName, string mappingContextParameterName, bool fromUpdate)
|
||||||
{
|
{
|
||||||
foreach (var property in model.MappedProperties)
|
if (fromUpdate)
|
||||||
{
|
{
|
||||||
|
properties = properties.GetWritableMappedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
if (property.isReadOnly && fromUpdate) continue;
|
||||||
|
|
||||||
if (property.TypeConverter is null)
|
if (property.TypeConverter is null)
|
||||||
{
|
{
|
||||||
if (property.IsEnumerable)
|
if (property.IsEnumerable)
|
||||||
|
@ -91,7 +124,7 @@ namespace MapTo.Sources
|
||||||
{
|
{
|
||||||
builder.WriteLine(property.MappedSourcePropertyTypeName is null
|
builder.WriteLine(property.MappedSourcePropertyTypeName is null
|
||||||
? $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"
|
? $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"
|
||||||
: $"{property.Name} = {mappingContextParameterName}.{MappingContextSource.MapMethodName}<{property.MappedSourcePropertyTypeName}, {property.Type}>({sourceClassParameterName}.{property.SourcePropertyName});");
|
: "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -105,12 +138,39 @@ namespace MapTo.Sources
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (otherProperties == null) return builder;
|
||||||
|
|
||||||
|
foreach (var property in otherProperties)
|
||||||
|
{
|
||||||
|
|
||||||
|
builder.WriteLine(property.MappedSourcePropertyTypeName is null
|
||||||
|
? $"{property.Name} = {property.SourcePropertyName.ToCamelCase()};"
|
||||||
|
: "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static SourceBuilder GenerateUpdateMethod(this SourceBuilder builder, MappingModel model)
|
||||||
|
{
|
||||||
|
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.GenerateUpdaterMethodsXmlDocs(model, sourceClassParameterName)
|
||||||
|
.WriteLine($"public void Update({model.SourceType} {sourceClassParameterName})")
|
||||||
|
.WriteOpeningBracket()
|
||||||
|
.TryWriteProperties(model.SourceProperties, null, sourceClassParameterName, "context", true)
|
||||||
|
.WriteClosingBracket();
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
private static SourceBuilder GenerateUpdaterMethodsXmlDocs(this SourceBuilder builder, MappingModel model, string sourceClassParameterName)
|
private static SourceBuilder GenerateUpdaterMethodsXmlDocs(this SourceBuilder builder, MappingModel model, string sourceClassParameterName)
|
||||||
{
|
{
|
||||||
if (!model.Options.GenerateXmlDocument)
|
if (!model.Options.GenerateXmlDocument)
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
using static MapTo.Sources.Constants;
|
||||||
|
|
||||||
|
namespace MapTo.Sources
|
||||||
|
{
|
||||||
|
internal static class ReadOnlyPropertyAttributeSource
|
||||||
|
{
|
||||||
|
internal const string AttributeName = "ReadOnlyProperty";
|
||||||
|
internal const string AttributeClassName = AttributeName + "Attribute";
|
||||||
|
internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName;
|
||||||
|
|
||||||
|
internal static SourceCode Generate(SourceGenerationOptions options)
|
||||||
|
{
|
||||||
|
var builder = new SourceBuilder()
|
||||||
|
.WriteLine(GeneratedFilesHeader)
|
||||||
|
.WriteLine("using System;")
|
||||||
|
.WriteLine()
|
||||||
|
.WriteLine($"namespace {RootNamespace}")
|
||||||
|
.WriteOpeningBracket();
|
||||||
|
|
||||||
|
if (options.GenerateXmlDocument)
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.WriteLine("/// <summary>")
|
||||||
|
.WriteLine("/// Specifies that the annotated property should be excluded.")
|
||||||
|
.WriteLine("/// </summary>");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
|
.WriteLine("[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]")
|
||||||
|
.WriteLine($"public sealed class {AttributeClassName} : Attribute {{ }}")
|
||||||
|
.WriteClosingBracket();
|
||||||
|
|
||||||
|
return new(builder.ToString(), $"{AttributeClassName}.g.cs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ namespace MapTo
|
||||||
internal StructMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
|
internal StructMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
|
||||||
: base(compilation, sourceGenerationOptions, typeSyntax) { }
|
: base(compilation, sourceGenerationOptions, typeSyntax) { }
|
||||||
|
|
||||||
protected override ImmutableArray<MappedProperty> GetMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass)
|
protected override ImmutableArray<MappedProperty> GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass)
|
||||||
{
|
{
|
||||||
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
||||||
|
|
||||||
|
@ -23,5 +23,18 @@ namespace MapTo
|
||||||
.Where(mappedProperty => mappedProperty is not null)
|
.Where(mappedProperty => mappedProperty is not null)
|
||||||
.ToImmutableArray()!;
|
.ToImmutableArray()!;
|
||||||
}
|
}
|
||||||
|
protected override ImmutableArray<MappedProperty> GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass)
|
||||||
|
{
|
||||||
|
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
||||||
|
|
||||||
|
return sourceTypeSymbol
|
||||||
|
.GetAllMembers()
|
||||||
|
.OfType<IPropertySymbol>()
|
||||||
|
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol))
|
||||||
|
.Select(property => MapPropertySimple(typeSymbol, property))
|
||||||
|
.Where(mappedProperty => mappedProperty is not null)
|
||||||
|
.ToImmutableArray()!;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,5 @@ namespace TestConsoleApp.Data.Models
|
||||||
|
|
||||||
public string EmployeeCode { get; set; }
|
public string EmployeeCode { get; set; }
|
||||||
|
|
||||||
public Manager Manager { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace TestConsoleApp.Data.Models
|
|
||||||
{
|
|
||||||
public class Manager: Employee
|
|
||||||
{
|
|
||||||
public int Level { get; set; }
|
|
||||||
|
|
||||||
public IEnumerable<Employee> Employees { get; set; } = Array.Empty<Employee>();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TestConsoleApp.ViewModels;
|
||||||
|
using MapTo;
|
||||||
|
|
||||||
|
namespace TestConsoleApp.Data.Models
|
||||||
|
{
|
||||||
|
[MapFrom(typeof(MyStructViewModel))]
|
||||||
|
public partial struct MyStruct
|
||||||
|
{
|
||||||
|
public int SomeInt { get; set; }
|
||||||
|
|
||||||
|
public string ReadOnlyString { get; }
|
||||||
|
|
||||||
|
public MyStruct(int someInt, string readOnlyString)
|
||||||
|
{
|
||||||
|
SomeInt = someInt;
|
||||||
|
ReadOnlyString = readOnlyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
namespace TestConsoleApp.Data.Models
|
|
||||||
{
|
|
||||||
public class Profile
|
|
||||||
{
|
|
||||||
public string FirstName { get; set; }
|
|
||||||
|
|
||||||
public string LastName { get; set; }
|
|
||||||
|
|
||||||
public string FullName => $"{FirstName} {LastName}";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
|
using TestConsoleApp.ViewModels;
|
||||||
|
using MapTo;
|
||||||
|
|
||||||
namespace TestConsoleApp.Data.Models
|
namespace TestConsoleApp.Data.Models
|
||||||
{
|
{
|
||||||
public class User
|
[MapFrom(typeof(UserViewModel))]
|
||||||
|
public partial class User
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
public DateTimeOffset RegisteredAt { get; set; }
|
public DateTimeOffset RegisteredAt { get; set; }
|
||||||
|
|
||||||
public Profile Profile { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,6 @@
|
||||||
using MapTo;
|
using MapTo;
|
||||||
using TestConsoleApp.Data.Models;
|
using TestConsoleApp.Data.Models;
|
||||||
using TestConsoleApp.ViewModels;
|
using TestConsoleApp.ViewModels;
|
||||||
using TestConsoleApp.ViewModels2;
|
|
||||||
|
|
||||||
namespace TestConsoleApp
|
namespace TestConsoleApp
|
||||||
{
|
{
|
||||||
|
@ -11,7 +10,6 @@ namespace TestConsoleApp
|
||||||
private static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
//UserTest();
|
//UserTest();
|
||||||
CyclicReferenceTest();
|
|
||||||
|
|
||||||
// EmployeeManagerTest();
|
// EmployeeManagerTest();
|
||||||
Console.WriteLine("done");
|
Console.WriteLine("done");
|
||||||
|
@ -19,74 +17,23 @@ namespace TestConsoleApp
|
||||||
|
|
||||||
private static void EmployeeManagerTest()
|
private static void EmployeeManagerTest()
|
||||||
{
|
{
|
||||||
var manager1 = new Manager
|
|
||||||
{
|
|
||||||
Id = 1,
|
|
||||||
EmployeeCode = "M001",
|
|
||||||
Level = 100
|
|
||||||
};
|
|
||||||
|
|
||||||
var manager2 = new Manager
|
|
||||||
{
|
|
||||||
Id = 2,
|
|
||||||
EmployeeCode = "M002",
|
|
||||||
Level = 100,
|
|
||||||
Manager = manager1
|
|
||||||
};
|
|
||||||
|
|
||||||
var employee1 = new Employee
|
var employee1 = new Employee
|
||||||
{
|
{
|
||||||
Id = 101,
|
Id = 101,
|
||||||
EmployeeCode = "E101",
|
EmployeeCode = "E101",
|
||||||
Manager = manager1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var employee2 = new Employee
|
var employee2 = new Employee
|
||||||
{
|
{
|
||||||
Id = 102,
|
Id = 102,
|
||||||
EmployeeCode = "E102",
|
EmployeeCode = "E102",
|
||||||
Manager = manager2
|
|
||||||
};
|
};
|
||||||
|
|
||||||
manager1.Employees = new[] { employee1, manager2 };
|
|
||||||
manager2.Employees = new[] { employee2 };
|
|
||||||
|
|
||||||
manager1.ToManagerViewModel();
|
|
||||||
employee1.ToEmployeeViewModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ManagerViewModel CyclicReferenceTest()
|
|
||||||
{
|
|
||||||
var manager1 = new Manager
|
|
||||||
{
|
|
||||||
Id = 1,
|
|
||||||
EmployeeCode = "M001",
|
|
||||||
Level = 100
|
|
||||||
};
|
|
||||||
|
|
||||||
manager1.Manager = manager1;
|
|
||||||
return manager1.ToManagerViewModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UserTest()
|
|
||||||
{
|
|
||||||
var user = new User
|
|
||||||
{
|
|
||||||
Id = 1234,
|
|
||||||
RegisteredAt = DateTimeOffset.Now,
|
|
||||||
Profile = new Profile
|
|
||||||
{
|
|
||||||
FirstName = "John",
|
|
||||||
LastName = "Doe"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var vm = user.ToUserViewModel();
|
|
||||||
|
|
||||||
Console.WriteLine("Key: {0}", vm.Key);
|
|
||||||
Console.WriteLine("RegisteredAt: {0}", vm.RegisteredAt);
|
|
||||||
Console.WriteLine("FirstName: {0}", vm.Profile.FirstName);
|
|
||||||
Console.WriteLine("LastName: {0}", vm.Profile.LastName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,13 @@
|
||||||
using MapTo;
|
using MapTo;
|
||||||
using TestConsoleApp.Data.Models;
|
using TestConsoleApp.Data.Models;
|
||||||
using TestConsoleApp.ViewModels2;
|
|
||||||
|
|
||||||
namespace TestConsoleApp.ViewModels
|
namespace TestConsoleApp.ViewModels
|
||||||
{
|
{
|
||||||
[MapFrom(typeof(Employee))]
|
[MapFrom(typeof(Employee))]
|
||||||
public partial class EmployeeViewModel
|
public partial class EmployeeViewModel
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; }
|
||||||
|
|
||||||
public string EmployeeCode { get; set; }
|
|
||||||
|
|
||||||
public ManagerViewModel Manager { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using MapTo;
|
|
||||||
using TestConsoleApp.Data.Models;
|
|
||||||
using TestConsoleApp.ViewModels;
|
|
||||||
|
|
||||||
namespace TestConsoleApp.ViewModels2
|
|
||||||
{
|
|
||||||
[MapFrom(typeof(Manager))]
|
|
||||||
public partial class ManagerViewModel : EmployeeViewModel
|
|
||||||
{
|
|
||||||
public int Level { get; set; }
|
|
||||||
|
|
||||||
public IEnumerable<EmployeeViewModel> Employees { get; set; } = Array.Empty<EmployeeViewModel>();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TestConsoleApp.Data.Models;
|
||||||
|
using MapTo;
|
||||||
|
|
||||||
|
namespace TestConsoleApp.ViewModels
|
||||||
|
{
|
||||||
|
[MapFrom(typeof(MyStruct))]
|
||||||
|
|
||||||
|
public partial struct MyStructViewModel
|
||||||
|
{
|
||||||
|
public int SomeInt { get; set; }
|
||||||
|
|
||||||
|
public MyStructViewModel(int someInt)
|
||||||
|
{
|
||||||
|
SomeInt = someInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
using MapTo;
|
|
||||||
using TestConsoleApp.Data.Models;
|
|
||||||
|
|
||||||
namespace TestConsoleApp.ViewModels
|
|
||||||
{
|
|
||||||
[MapFrom(typeof(Profile))]
|
|
||||||
public partial class ProfileViewModel
|
|
||||||
{
|
|
||||||
public string FirstName { get; }
|
|
||||||
|
|
||||||
public string LastName { get; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,18 +7,8 @@ namespace TestConsoleApp.ViewModels
|
||||||
[MapFrom(typeof(User))]
|
[MapFrom(typeof(User))]
|
||||||
public partial class UserViewModel
|
public partial class UserViewModel
|
||||||
{
|
{
|
||||||
[MapProperty(SourcePropertyName = nameof(User.Id))]
|
public int Id { get; set; }
|
||||||
[MapTypeConverter(typeof(IdConverter))]
|
|
||||||
public string Key { get; }
|
|
||||||
|
|
||||||
public DateTimeOffset RegisteredAt { get; set; }
|
public DateTimeOffset RegisteredAt { get; }
|
||||||
|
|
||||||
// [IgnoreProperty]
|
|
||||||
public ProfileViewModel Profile { get; set; }
|
|
||||||
|
|
||||||
private class IdConverter : ITypeConverter<int, string>
|
|
||||||
{
|
|
||||||
public string Convert(int source, object[]? converterParameters) => $"{source:X}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue