Simplified using statements and fix nested mappings.

This commit is contained in:
Mohammadreza Taikandi 2021-02-17 08:32:23 +00:00
parent 6ad7ea83f9
commit c1a763a474
13 changed files with 234 additions and 84 deletions

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MapTo.Sources;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@ -68,7 +67,13 @@ namespace MapTo.Extensions
targetProperty.NullableAnnotation == NullableAnnotation.Annotated));
}
public static INamedTypeSymbol GetTypeByMetadataNameOrThrow(this Compilation compilation, string fullyQualifiedMetadataName) =>
public static INamedTypeSymbol GetTypeByMetadataNameOrThrow(this Compilation compilation, string fullyQualifiedMetadataName) =>
compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ?? throw new TypeLoadException($"Unable to find '{fullyQualifiedMetadataName}' type.");
public static bool IsGenericEnumerable(this Compilation compilation, ITypeSymbol typeSymbol) =>
typeSymbol is INamedTypeSymbol { IsGenericType: true } &&
compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T).Equals(typeSymbol.OriginalDefinition, SymbolEqualityComparer.Default);
public static bool IsArray(this Compilation compilation, ITypeSymbol typeSymbol) => typeSymbol is IArrayTypeSymbol;
}
}

View File

@ -12,16 +12,19 @@ namespace MapTo
{
private readonly ClassDeclarationSyntax _classSyntax;
private readonly Compilation _compilation;
private readonly List<Diagnostic> _diagnostics;
private readonly INamedTypeSymbol _ignorePropertyAttributeTypeSymbol;
private readonly INamedTypeSymbol _mapFromAttributeTypeSymbol;
private readonly INamedTypeSymbol _mapPropertyAttributeTypeSymbol;
private readonly INamedTypeSymbol _mapTypeConverterAttributeTypeSymbol;
private readonly SourceGenerationOptions _sourceGenerationOptions;
private readonly INamedTypeSymbol _typeConverterInterfaceTypeSymbol;
private readonly List<string> _usings;
internal MappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, ClassDeclarationSyntax classSyntax)
{
Diagnostics = ImmutableArray<Diagnostic>.Empty;
_diagnostics = new List<Diagnostic>();
_usings = new List<string> { "System" };
_sourceGenerationOptions = sourceGenerationOptions;
_classSyntax = classSyntax;
_compilation = compilation;
@ -32,26 +35,28 @@ namespace MapTo
_mapPropertyAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapPropertyAttributeSource.FullyQualifiedName);
_mapFromAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapFromAttributeSource.FullyQualifiedName);
AddUsingIfRequired(sourceGenerationOptions.SupportNullableReferenceTypes, "System.Diagnostics.CodeAnalysis");
Initialize();
}
public MappingModel? Model { get; private set; }
public ImmutableArray<Diagnostic> Diagnostics { get; private set; }
public IEnumerable<Diagnostic> Diagnostics => _diagnostics;
private void Initialize()
{
var semanticModel = _compilation.GetSemanticModel(_classSyntax.SyntaxTree);
if (!(semanticModel.GetDeclaredSymbol(_classSyntax) is INamedTypeSymbol classTypeSymbol))
{
ReportDiagnostic(DiagnosticProvider.TypeNotFoundError(_classSyntax.GetLocation(), _classSyntax.Identifier.ValueText));
_diagnostics.Add(DiagnosticProvider.TypeNotFoundError(_classSyntax.GetLocation(), _classSyntax.Identifier.ValueText));
return;
}
var sourceTypeSymbol = GetSourceTypeSymbol(_classSyntax, semanticModel);
if (sourceTypeSymbol is null)
{
ReportDiagnostic(DiagnosticProvider.MapFromAttributeNotFoundError(_classSyntax.GetLocation()));
_diagnostics.Add(DiagnosticProvider.MapFromAttributeNotFoundError(_classSyntax.GetLocation()));
return;
}
@ -62,10 +67,13 @@ namespace MapTo
var mappedProperties = GetMappedProperties(classTypeSymbol, sourceTypeSymbol, isClassInheritFromMappedBaseClass);
if (!mappedProperties.Any())
{
ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyFoundError(_classSyntax.GetLocation(), classTypeSymbol, sourceTypeSymbol));
_diagnostics.Add(DiagnosticProvider.NoMatchingPropertyFoundError(_classSyntax.GetLocation(), classTypeSymbol, sourceTypeSymbol));
return;
}
AddUsingIfRequired(sourceTypeSymbol);
AddUsingIfRequired(mappedProperties.Any(p => p.IsEnumerable), "System.Linq");
Model = new MappingModel(
_sourceGenerationOptions,
_classSyntax.GetNamespace(),
@ -75,7 +83,8 @@ namespace MapTo
sourceClassName,
sourceTypeSymbol.ToString(),
mappedProperties.ToImmutableArray(),
isClassInheritFromMappedBaseClass);
isClassInheritFromMappedBaseClass,
_usings.ToImmutableArray());
}
private bool IsClassInheritFromMappedBaseClass(SemanticModel semanticModel)
@ -103,60 +112,73 @@ namespace MapTo
string? converterFullyQualifiedName = null;
var converterParameters = ImmutableArray<string>.Empty;
string? mappedSourcePropertyType = null;
ITypeSymbol? mappedSourcePropertyType = null;
ITypeSymbol? enumerableTypeArgumentType = null;
if (!_compilation.HasCompatibleTypes(sourceProperty, property))
{
if (!TryGetMapTypeConverter(property, sourceProperty, out converterFullyQualifiedName, out converterParameters) &&
!TryGetNestedObjectMappings(property, out mappedSourcePropertyType))
!TryGetNestedObjectMappings(property, out mappedSourcePropertyType, out enumerableTypeArgumentType))
{
continue;
}
}
mappedProperties.Add(new MappedProperty(
property.Name,
property.Type.Name,
converterFullyQualifiedName,
converterParameters.ToImmutableArray(),
sourceProperty.Name,
mappedSourcePropertyType));
AddUsingIfRequired(property.Type);
AddUsingIfRequired(sourceTypeSymbol);
AddUsingIfRequired(enumerableTypeArgumentType);
AddUsingIfRequired(mappedSourcePropertyType);
mappedProperties.Add(
new MappedProperty(
property.Name,
property.Type.Name,
converterFullyQualifiedName,
converterParameters.ToImmutableArray(),
sourceProperty.Name,
mappedSourcePropertyType?.Name,
enumerableTypeArgumentType?.Name));
}
return mappedProperties.ToImmutableArray();
}
private bool TryGetNestedObjectMappings(IPropertySymbol property, out string? mappedSourcePropertyType)
private bool TryGetNestedObjectMappings(IPropertySymbol property, out ITypeSymbol? mappedSourcePropertyType, out ITypeSymbol? enumerableTypeArgument)
{
mappedSourcePropertyType = null;
enumerableTypeArgument = null;
if (!Diagnostics.IsEmpty)
if (!_diagnostics.IsEmpty())
{
return false;
}
var nestedSourceMapFromAttribute = property.Type.GetAttribute(_mapFromAttributeTypeSymbol);
if (nestedSourceMapFromAttribute is null)
var mapFromAttribute = property.Type.GetAttribute(_mapFromAttributeTypeSymbol);
if (mapFromAttribute is null && property.Type is INamedTypeSymbol namedTypeSymbol && _compilation.IsGenericEnumerable(property.Type))
{
ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
return false;
enumerableTypeArgument = namedTypeSymbol.TypeArguments.First();
mapFromAttribute = enumerableTypeArgument.GetAttribute(_mapFromAttributeTypeSymbol);
}
if (!(nestedSourceMapFromAttribute.ApplicationSyntaxReference?.GetSyntax() is AttributeSyntax nestedAttributeSyntax))
mappedSourcePropertyType = mapFromAttribute?.ConstructorArguments.First().Value as INamedTypeSymbol;
if (mappedSourcePropertyType is null && enumerableTypeArgument is null)
{
ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
return false;
_diagnostics.Add(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
}
var nestedSourceTypeSymbol = GetSourceTypeSymbol(nestedAttributeSyntax);
if (nestedSourceTypeSymbol is null)
{
ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
return false;
}
return _diagnostics.IsEmpty();
}
mappedSourcePropertyType = nestedSourceTypeSymbol.Name;
return true;
private void AddUsingIfRequired(ISymbol? namedTypeSymbol) =>
AddUsingIfRequired(namedTypeSymbol?.ContainingNamespace.IsGlobalNamespace == false, namedTypeSymbol?.ContainingNamespace.ToDisplayString());
private void AddUsingIfRequired(bool condition, string? ns)
{
if (condition && ns is not null && ns != _classSyntax.GetNamespace() && !_usings.Contains(ns))
{
_usings.Add(ns);
}
}
private bool TryGetMapTypeConverter(IPropertySymbol property, IPropertySymbol sourceProperty, out string? converterFullyQualifiedName, out ImmutableArray<string> converterParameters)
@ -164,7 +186,7 @@ namespace MapTo
converterFullyQualifiedName = null;
converterParameters = ImmutableArray<string>.Empty;
if (!Diagnostics.IsEmpty)
if (!_diagnostics.IsEmpty())
{
return false;
}
@ -178,7 +200,7 @@ namespace MapTo
var baseInterface = GetTypeConverterBaseInterface(converterTypeSymbol, property, sourceProperty);
if (baseInterface is null)
{
ReportDiagnostic(DiagnosticProvider.InvalidTypeConverterGenericTypesError(property, sourceProperty));
_diagnostics.Add(DiagnosticProvider.InvalidTypeConverterGenericTypesError(property, sourceProperty));
return false;
}
@ -216,11 +238,6 @@ namespace MapTo
: converterParameter.Values.Where(v => v.Value is not null).Select(v => v.Value!.ToSourceCodeString()).ToImmutableArray();
}
private void ReportDiagnostic(Diagnostic diagnostic)
{
Diagnostics = Diagnostics.Add(diagnostic);
}
private INamedTypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classDeclarationSyntax, SemanticModel? semanticModel = null) =>
GetSourceTypeSymbol(classDeclarationSyntax.GetAttribute(MapFromAttributeSource.AttributeName), semanticModel);

View File

@ -8,12 +8,16 @@ namespace MapTo
internal record SourceCode(string Text, string HintName);
internal record MappedProperty(
string Name,
string Name,
string Type,
string? TypeConverter,
string? TypeConverter,
ImmutableArray<string> TypeConverterParameters,
string SourcePropertyName,
string? MappedSourcePropertyTypeName);
string? MappedSourcePropertyTypeName,
string? EnumerableTypeArgument)
{
public bool IsEnumerable => EnumerableTypeArgument is not null;
}
internal record MappingModel (
SourceGenerationOptions Options,
@ -24,7 +28,8 @@ namespace MapTo
string SourceClassName,
string SourceClassFullName,
ImmutableArray<MappedProperty> MappedProperties,
bool HasMappedBaseClass
bool HasMappedBaseClass,
ImmutableArray<string> Usings
);
internal record SourceGenerationOptions(

View File

@ -1,4 +1,5 @@
using MapTo.Extensions;
using System.Linq;
using MapTo.Extensions;
using static MapTo.Sources.Constants;
namespace MapTo.Sources
@ -42,9 +43,8 @@ namespace MapTo.Sources
private static SourceBuilder WriteUsings(this SourceBuilder builder, MappingModel model)
{
return builder
.WriteLine("using System;")
.WriteLineIf(model.Options.SupportNullableReferenceTypes, "using System.Diagnostics.CodeAnalysis;");
model.Usings.Sort().ForEach(u => builder.WriteLine($"using {u};"));
return builder;
}
private static SourceBuilder GenerateConstructor(this SourceBuilder builder, MappingModel model)
@ -64,7 +64,7 @@ namespace MapTo.Sources
var baseConstructor = model.HasMappedBaseClass ? $" : base({sourceClassParameterName})" : string.Empty;
builder
.WriteLine($"{model.Options.ConstructorAccessModifier.ToLowercaseString()} {model.ClassName}({model.SourceClassFullName} {sourceClassParameterName}){baseConstructor}")
.WriteLine($"{model.Options.ConstructorAccessModifier.ToLowercaseString()} {model.ClassName}({model.SourceClassName} {sourceClassParameterName}){baseConstructor}")
.WriteOpeningBracket()
.WriteLine($"if ({sourceClassParameterName} == null) throw new ArgumentNullException(nameof({sourceClassParameterName}));")
.WriteLine();
@ -73,9 +73,16 @@ namespace MapTo.Sources
{
if (property.TypeConverter is null)
{
builder.WriteLine(property.MappedSourcePropertyTypeName is null
? $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"
: $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName}.To{property.Type}();");
if (property.IsEnumerable)
{
builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName}.Select({property.MappedSourcePropertyTypeName}To{property.EnumerableTypeArgument}Extensions.To{property.EnumerableTypeArgument}).ToList();");
}
else
{
builder.WriteLine(property.MappedSourcePropertyTypeName is null
? $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"
: $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName}.To{property.Type}();");
}
}
else
{
@ -98,7 +105,7 @@ namespace MapTo.Sources
return builder
.GenerateConvertorMethodsXmlDocs(model, sourceClassParameterName)
.WriteLineIf(model.Options.SupportNullableReferenceTypes, $"[return: NotNullIfNotNull(\"{sourceClassParameterName}\")]")
.WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static {model.ClassName}{model.Options.NullableReferenceSyntax} From({model.SourceClassFullName}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})")
.WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static {model.ClassName}{model.Options.NullableReferenceSyntax} From({model.SourceClassName}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})")
.WriteOpeningBracket()
.WriteLine($"return {sourceClassParameterName} == null ? null : new {model.ClassName}({sourceClassParameterName});")
.WriteClosingBracket();
@ -136,7 +143,7 @@ namespace MapTo.Sources
return builder
.GenerateConvertorMethodsXmlDocs(model, sourceClassParameterName)
.WriteLineIf(model.Options.SupportNullableReferenceTypes, $"[return: NotNullIfNotNull(\"{sourceClassParameterName}\")]")
.WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static {model.ClassName}{model.Options.NullableReferenceSyntax} To{model.ClassName}(this {model.SourceClassFullName}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})")
.WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static {model.ClassName}{model.Options.NullableReferenceSyntax} To{model.ClassName}(this {model.SourceClassName}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})")
.WriteOpeningBracket()
.WriteLine($"return {sourceClassParameterName} == null ? null : new {model.ClassName}({sourceClassParameterName});")
.WriteClosingBracket();

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MapTo.Extensions;
using MapTo.Sources;
using Microsoft.CodeAnalysis;
@ -96,11 +95,19 @@ namespace MapTo.Tests
return builder.ToString();
}
internal static string[] GetEmployeeManagerSourceText(Func<string> employeeClassSource = null, Func<string> managerClassSource = null, Func<string> employeeViewModelSource = null, Func<string> managerViewModelSource = null)
internal static string[] GetEmployeeManagerSourceText(Func<string> employeeClassSource = null, Func<string> managerClassSource = null, Func<string> employeeViewModelSource = null, Func<string> managerViewModelSource = null, bool useDifferentViewModelNamespace = false)
{
return new[]
{
employeeClassSource?.Invoke() ?? @"
employeeClassSource?.Invoke() ?? DefaultEmployeeClassSource(),
managerClassSource?.Invoke() ?? DefaultManagerClassSource(),
employeeViewModelSource?.Invoke() ??
DefaultEmployeeViewModelSource(useDifferentViewModelNamespace),
managerViewModelSource?.Invoke() ?? DefaultManagerViewModelSource(useDifferentViewModelNamespace)
};
static string DefaultEmployeeClassSource() =>
@"
using System;
using System.Collections.Generic;
using System.Text;
@ -115,8 +122,10 @@ namespace Test.Data.Models
public Manager Manager { get; set; }
}
}".Trim(),
managerClassSource?.Invoke() ?? @"using System;
}".Trim();
static string DefaultManagerClassSource() =>
@"using System;
using System.Collections.Generic;
using System.Text;
@ -129,8 +138,28 @@ namespace Test.Data.Models
public IEnumerable<Employee> Employees { get; set; } = Array.Empty<Employee>();
}
}
".Trim(),
employeeViewModelSource?.Invoke() ?? @"
".Trim();
static string DefaultEmployeeViewModelSource(bool useDifferentNamespace) => useDifferentNamespace
? @"
using MapTo;
using Test.Data.Models;
using Test.ViewModels2;
namespace Test.ViewModels
{
[MapFrom(typeof(Employee))]
public partial class EmployeeViewModel
{
public int Id { get; set; }
public string EmployeeCode { get; set; }
public ManagerViewModel Manager { get; set; }
}
}
".Trim()
: @"
using MapTo;
using Test.Data.Models;
@ -146,8 +175,28 @@ namespace Test.ViewModels
public ManagerViewModel Manager { get; set; }
}
}
".Trim(),
managerViewModelSource?.Invoke() ?? @"
".Trim();
static string DefaultManagerViewModelSource(bool useDifferentNamespace) => useDifferentNamespace
? @"
using System;
using System.Collections.Generic;
using MapTo;
using Test.Data.Models;
using Test.ViewModels;
namespace Test.ViewModels2
{
[MapFrom(typeof(Manager))]
public partial class ManagerViewModel : EmployeeViewModel
{
public int Level { get; set; }
public IEnumerable<EmployeeViewModel> Employees { get; set; } = Array.Empty<EmployeeViewModel>();
}
}
".Trim()
: @"
using System;
using System.Collections.Generic;
using MapTo;
@ -162,8 +211,7 @@ namespace Test.ViewModels
public IEnumerable<EmployeeViewModel> Employees { get; set; } = Array.Empty<EmployeeViewModel>();
}
}".Trim()
};
}".Trim();
}
internal static PropertyDeclarationSyntax GetPropertyDeclarationSyntax(SyntaxTree syntaxTree, string targetPropertyName, string targetClass = "Foo")

View File

@ -52,7 +52,7 @@ namespace MapTo
var expectedResult = @"
partial class Foo
{
public Foo(Test.Models.Baz baz)
public Foo(Baz baz)
{
if (baz == null) throw new ArgumentNullException(nameof(baz));

View File

@ -40,7 +40,7 @@ namespace MapTo.Tests.Infrastructure
driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var generateDiagnostics);
generateDiagnostics.ShouldBeSuccessful();
generateDiagnostics.ShouldBeSuccessful(ignoreDiagnosticsIds: new[] { "MT" });
outputCompilation.GetDiagnostics().ShouldBeSuccessful(outputCompilation);
return (outputCompilation, generateDiagnostics);

View File

@ -60,7 +60,7 @@ namespace MapTo
var expectedResult = @"
partial class Foo
{
public Foo(Test.Models.Baz baz)
public Foo(Baz baz)
{
if (baz == null) throw new ArgumentNullException(nameof(baz));

View File

@ -75,14 +75,14 @@ namespace MapTo
var expectedExtension = @"
internal static partial class BazToFooExtensions
{
internal static Foo ToFoo(this Test.Models.Baz baz)
internal static Foo ToFoo(this Baz baz)
{
return baz == null ? null : new Foo(baz);
}
}".Trim();
var expectedFactory = @"
internal static Foo From(Test.Models.Baz baz)
internal static Foo From(Baz baz)
{
return baz == null ? null : new Foo(baz);
}".Trim();
@ -129,7 +129,7 @@ namespace Test
{
partial class Foo
{
public Foo(Test.Baz baz)
public Foo(Baz baz)
{
if (baz == null) throw new ArgumentNullException(nameof(baz));
@ -199,7 +199,7 @@ namespace Test
{
partial class Foo
{
public Foo(Test.Baz baz)
public Foo(Baz baz)
{
if (baz == null) throw new ArgumentNullException(nameof(baz));
@ -249,6 +249,7 @@ namespace Test
const string expectedResult = @"
// <auto-generated />
using Bazaar;
using System;
namespace Test
@ -271,7 +272,7 @@ namespace Test
const string expectedResult = @"
partial class Foo
{
public Foo(Test.Models.Baz baz)
public Foo(Baz baz)
{
if (baz == null) throw new ArgumentNullException(nameof(baz));
@ -296,7 +297,7 @@ namespace Test
var source = GetSourceText();
const string expectedResult = @"
public static Foo From(Test.Models.Baz baz)
public static Foo From(Baz baz)
{
return baz == null ? null : new Foo(baz);
}
@ -319,7 +320,7 @@ namespace Test
const string expectedResult = @"
public static partial class BazToFooExtensions
{
public static Foo ToFoo(this Test.Models.Baz baz)
public static Foo ToFoo(this Baz baz)
{
return baz == null ? null : new Foo(baz);
}
@ -356,7 +357,7 @@ namespace Test
var expectedResult = @"
partial class Foo
{
public Foo(Test.Baz baz)
public Foo(Baz baz)
{
if (baz == null) throw new ArgumentNullException(nameof(baz));
@ -442,7 +443,7 @@ namespace Test
const string expectedResult = @"
partial class Foo
{
public Foo(Test.Models.Baz baz)
public Foo(Baz baz)
{
if (baz == null) throw new ArgumentNullException(nameof(baz));
@ -468,12 +469,76 @@ namespace Test
var sources = GetEmployeeManagerSourceText();
const string expectedResult = @"
public ManagerViewModel(Test.Data.Models.Manager manager) : base(manager)
public ManagerViewModel(Manager manager) : base(manager)
{
if (manager == null) throw new ArgumentNullException(nameof(manager));
Level = manager.Level;
}
";
// Act
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(sources, analyzerConfigOptions: DefaultAnalyzerOptions);
// Assert
diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult);
}
[Fact]
public void When_SourceTypeHasEnumerablePropertiesWithMapFromAttribute_Should_CreateANewEnumerableWithMappedObjects()
{
// Arrange
var sources = GetEmployeeManagerSourceText();
const string expectedResult = @"
using System;
using System.Collections.Generic;
using System.Linq;
using Test.Data.Models;
namespace Test.ViewModels
{
partial class ManagerViewModel
{
public ManagerViewModel(Manager manager) : base(manager)
{
if (manager == null) throw new ArgumentNullException(nameof(manager));
Level = manager.Level;
Employees = manager.Employees.Select(EmployeeToEmployeeViewModelExtensions.ToEmployeeViewModel).ToList();
";
// Act
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(sources, analyzerConfigOptions: DefaultAnalyzerOptions);
// Assert
diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult);
}
[Fact]
public void When_SourceTypeHasEnumerablePropertiesWithMapFromAttributeInDifferentNamespaces_Should_CreateANewEnumerableWithMappedObjectsAndImportNamespace()
{
// Arrange
var sources = GetEmployeeManagerSourceText(useDifferentViewModelNamespace: true);
const string expectedResult = @"
using System;
using System.Collections.Generic;
using System.Linq;
using Test.Data.Models;
using Test.ViewModels;
namespace Test.ViewModels2
{
partial class ManagerViewModel
{
public ManagerViewModel(Manager manager) : base(manager)
{
if (manager == null) throw new ArgumentNullException(nameof(manager));
Level = manager.Level;
Employees = manager.Employees.Select(EmployeeToEmployeeViewModelExtensions.ToEmployeeViewModel).ToList();
";
// Act

View File

@ -219,7 +219,7 @@ namespace Test
var expectedResult = @"
partial class Foo
{
public Foo(Test.Models.Baz baz)
public Foo(Baz baz)
{
if (baz == null) throw new ArgumentNullException(nameof(baz));

View File

@ -1,6 +1,7 @@
using System;
using TestConsoleApp.Data.Models;
using TestConsoleApp.ViewModels;
using TestConsoleApp.ViewModels2;
namespace TestConsoleApp
{
@ -41,8 +42,8 @@ namespace TestConsoleApp
manager1.Employees = new[] { employee1, manager2 };
manager2.Employees = new[] { employee2 };
var manager1ViewModel = manager1.ToManagerViewModel();
int a = 0;
manager1.ToManagerViewModel();
employee1.ToEmployeeViewModel();
}
private static void UserTest()

View File

@ -1,5 +1,6 @@
using MapTo;
using TestConsoleApp.Data.Models;
using TestConsoleApp.ViewModels2;
namespace TestConsoleApp.ViewModels
{

View File

@ -2,8 +2,9 @@
using System.Collections.Generic;
using MapTo;
using TestConsoleApp.Data.Models;
using TestConsoleApp.ViewModels;
namespace TestConsoleApp.ViewModels
namespace TestConsoleApp.ViewModels2
{
[MapFrom(typeof(Manager))]
public partial class ManagerViewModel : EmployeeViewModel