【问题标题】:Checking Automapper usage via Roslyn通过 Roslyn 检查 Automapper 的使用情况
【发布时间】:2016-12-16 19:56:46
【问题描述】:

我有一个定义如下的通用方法,它封装了对 AutoMapper 的调用。

public TOut CreateRequest<TOut, TModelIn>(TModelIn data)
{
  ...
  return Mapper.Map<TModelIn, TOut>();
}

如果我调用Mapper.AssertConfigurationIsValid,那么我可以检查我的地图是否设置正确,但是我无法检查是否有人在未定义地图时添加了一行代码来尝试地图。

所以我希望能够扫描我的程序集并找到对上述方法的所有调用,提取正在使用的泛型类型,然后将这些类型连接到 Mapper.Map&lt;Type1, Type2&gt;(); 调用中。然后我可以调用Mapper.AssertConfigurationIsValid方法,并确保我的代码中的所有Maps确实已经被映射并且是有效的。

我们的想法是将其添加到 UnitTest 中,这样我就可以在让用户去测试它以查看会发生什么之前确定映射。

[更新] 我一直在考虑在我的单元测试中使用 Roslyn 来做到这一点。有谁知道如何通过 Roslyn 找到方法的调用,无论是直接调用还是通过参数列表?

【问题讨论】:

  • 你可以在 Visual Studio 的 find all 方法中做正则表达式(或者偷懒,只找到所有的 'CreateRequest
  • 我需要它在单元测试中自动运行,而不是手动运行。
  • 我不确定您可以更改代码的哪一部分。你能不能修改泛型方法?如果可以的话,我建议启用嵌入到方法中的自动测试功能。如果您愿意,我可以尝试详细说明答案。

标签: c# automapper roslyn


【解决方案1】:

我设法通过Rosyln 实现了这一目标,但这并不是一件容易的事。 我最终在我的解决方案中加载了所有文档,在它们中搜索 MemberAccessExpressionSyntax 类型,并提取了带有 GenericNameSyntax 且标识符为 CreateRequest 的那些。

然后我可以从 TypeListArgument 中获取每个参数,我知道其中可能有 1、2 或 3。我只想要具有 3 的实例,因此可以将它们读取为 IdentifixNameSyntax 对象,并使用Identifier 为我提供 AutoMapper 映射所需的类名。

然后我必须使用反射从程序集中查找类或枚举的名称,以提供可以传递给 AutoMapperType

测试设置代码:

var slnPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "MySolution.sln"));
var workspace = MSBuildWorkspace.Create();
_solution = workspace.OpenSolutionAsync(slnPath).Result;
_project = _solution.Projects.First(p => p.Name == "MyProject");

foreach (var documentId in _project.DocumentIds) {
    var document = _solution.GetDocument(documentId);
    if (document.SupportsSyntaxTree) {
        _documents.Add(document);
    }
}

主代码:

foreach (var document in _documents) {
    var methods = document.GetSyntaxRootAsync().Result.DescendantNodes().OfType<MemberAccessExpressionSyntax>();

    foreach (var m in methods.Where(x => x.Name is GenericNameSyntax)) {
        var genSyntax = m.Name as GenericNameSyntax;
        if (genSyntax?.Identifier.Text == "CreateRequest") {
            var args = genSyntax.TypeArgumentList.Arguments;

            if (args.Count == 3) {
                var item1 = args[0] as IdentifierNameSyntax;
                var item2 = args[1] as IdentifierNameSyntax;

                if (item1 != null && item2 != null) {

                    var c1 = ReflectionTestHelper.GetClassesWithKeyword(item1.Identifier.Text).SingleOrDefault(x => x.Name == item1.Identifier.Text)
                        ?? ReflectionTestHelper.GetEnumsWithKeyword(item1.Identifier.Text).SingleOrDefault(x => x.Name == item1.Identifier.Text);

                    var c2 = ReflectionTestHelper.GetClassesWithKeyword(item2.Identifier.Text).SingleOrDefault(x => x.Name == item2.Identifier.Text)
                        ?? ReflectionTestHelper.GetEnumsWithKeyword(item2.Identifier.Text).SingleOrDefault(x => x.Name == item2.Identifier.Text);

                    if (c1 == null)
                        errors.Add("Unable to find Class for mapping :: " + item1.Identifier.Text);

                    if (c2 == null)
                        errors.Add("Unable to find Class for mapping :: " + item2.Identifier.Text);

                    if (c1 != null && c2 != null) {

                        var map = Mapper.Configuration.FindTypeMapFor(c1, c2);

                        if (map == null) {

                            var location = genSyntax.GetLocation().GetMappedLineSpan();
                            var line = location.Span.Start.Line + 1;

                            var errormessage = new StringBuilder();
                            errormessage.AppendLine("No AutoMapper map found for :: " + item1.Identifier.Text + " -> " + item2.Identifier.Text);
                            errormessage.AppendLine("\tLocation: " + document.FilePath + "[Line:" + line + "]");
                            errormessage.AppendLine("\tMethod: " + genSyntax.Parent);
                            errors.Add(errormessage.ToString());
                        }
                    }
                }
            }
        }
    }
}

就像我说的那样,不是很好,但确实可以。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    • 1970-01-01
    • 2014-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多