1

I have a unit test ensure my WebAPI controllers derive from the right base type:

[TestMethod]
public void AllWebApiControllersShouldDeriveFromApiController()
{
    var controllers = Assembly.GetAssembly(typeof(ApiControllerBase)).GetTypes()
        .Where(t => t.Namespace == "Xxx.Web.Controllers")
        .ToList();

    controllers.Should().NotBeEmpty();

    foreach (var controller in controllers)
    {
        if (controller == typeof(ApiControllerBase)) continue;

        controller.Should().BeDerivedFrom<ApiControllerBase>();
    }
}

it was working fine until recently, when all of a sudden in the list of controllers two "ghost" controllers started to appear. They look like some sort of generic (auto-generated?) types and they are failing my tests. These are:

Name = "<>c" FullName = "Xxx.Web.Controllers.ExistingControllerNameController+<>c"

They are failing my test above. What are they? Where did they come from? How can I get rid of them?

N.B. test is failing on the build server as well. Clean & Rebuild did not help.

controller code before:

 [HttpGet]
 [Route("api/things")]
 public IEnumerable<ThingDto> GetAll()
 {
     return service.GetAll();
 }

& after:

 [HttpGet]
 [Route("api/things")]
 public IEnumerable<ThingDto> GetAll()
 {
     return service.GetAll().OrderBy(x => x.Description);
 }
Ruslan
  • 9,927
  • 15
  • 55
  • 89
  • Check what change in source control. It's impossible to help without knowing what classes are involved – Panagiotis Kanavos Jun 23 '16 at 16:14
  • 2
    They are compiler-generated classes that are generated for a variety of reasons. Just [add a check for the `CompilerGenerated` attribute on the type](http://stackoverflow.com/questions/6418779). – D Stanley Jun 23 '16 at 16:18
  • @DStanley you're right, however, I'd like to understand why they suddenly started to appear now? – Ruslan Jun 23 '16 at 16:25
  • Most common reason for those classes - using lambda expressions. Variables captured by closures are implemented via nested classes with auto-generated names. – Alexei Levenkov Jun 23 '16 at 16:25
  • 2
    Some possible causes are: anonymous types, `yield` syntax, lambdas with closures. I doubt that's an exhaustive list. – D Stanley Jun 23 '16 at 16:28
  • @AlexeiLevenkov sport on mate! i added `.OrderBy(x => ...)`. this should be the answer! – Ruslan Jun 23 '16 at 16:29
  • @Tsar If you want that to be the answer then you need to add your "before" and "after" code to the question and ask why the type was created in that specific instance. That's just one possible reason for the compiler-generated type. I'd also note that it's an _implementation detail_ and subject to change (e.g. there's no guarantee they they'll have the `<>c` prefix in the future). – D Stanley Jun 23 '16 at 16:49

2 Answers2

2

<> is not legal in a name in C#, those are names for classes that have been generated by the compiler.

Make your Where clause stricter so as to exclude compiler generated types.

var controllers = Assembly.GetAssembly(typeof(ApiControllerBase)).GetTypes()
        .Where(t => t.Namespace == "Xxx.Web.Controllers")
        .Where(t => !t.Name.Contains("<>")
        .ToList();
Patrick Allwood
  • 1,822
  • 17
  • 21
  • You should note that the use of `<>` in the name is an _implementation detail_ that's subject to change. There's no guarantee that future compilers use that prefix. – D Stanley Jun 23 '16 at 16:52
  • Yes, you're correct. Better to check for the `CompilerGenerated` attribute as you suggested. – Patrick Allwood Jun 24 '16 at 17:10
0

As per suggestions in the comments, the Lambda in the OrderBy clause has caused these compiler generated classes to appear. I have included the CompilerGeneratedAttribute check to exclude these from my test:

[TestMethod]
public void AllWebApiControllersShouldDeriveFromApiController()
{
    var controllers = Assembly.GetAssembly(typeof(ApiControllerBase)).GetTypes()
        .Where(t => t.Namespace == "Xxx.Web.Controllers"
        && IsCompilerGenerated(t) == false).ToList();

    controllers.Should().NotBeEmpty();

    foreach (var controller in controllers)
    {
        if (controller == typeof(ApiControllerBase)) continue;

        controller.Should().BeDerivedFrom<ApiControllerBase>();
    }
}

private static bool IsCompilerGenerated(MemberInfo t)
{
    return Attribute.GetCustomAttribute(t, typeof(CompilerGeneratedAttribute)) != null;
}
Romano Zumbé
  • 7,893
  • 4
  • 33
  • 55
Ruslan
  • 9,927
  • 15
  • 55
  • 89