Daryl
Daryl

Reputation: 18895

How To Ensure Correct Extension Method Resolution

I'm seeing some unexpected behavior in my extension method resolution, and I'm attempting to figure out why. I've read Eric Lippert's Closer is Better article and I'm still confused.

First the call site:

// Assembly: DLaB.Xrm.2016.Tests
using DLaB.Xrm.Entities;
using DLaB.Xrm.Test;
using Microsoft.Xrm.Sdk;

namespace DLaB.Xrm.Tests 

    public class TrialClass{
    {
        public void Delete(){
            IOrganziationService service = GetService();
            Id<Contact> id = GetId();
            service.Delete(id);  // Call Site in Question
        }
    } 
}

IOrganizationService Defines a delete method with this signature:

// Assembly: Microsoft.Xrm.Sdk
void Delete(string entityName, Guid id);

I have defined Extension method overloads on the IOrganziationService in two different namespaces, in two different assemblies:

//Assembly: DLaB.Xrm.2016
//Namespace DLaB.Xrm
public static void Delete(this IOrganizationService service, Entity entity) { ... }
public static void Delete(this IOrganizationService service, EntityReference entity) { ... }


//Assembly: DLaB.Xrm.Test.2016
//Namespace DLaB.Xrm.Test
public static void Delete(this IOrganizationService service, Id id) { ... }
public static void Delete<T>(this IOrganizationService service, Id<T> entity) where T : Entity { ... }

I have also defined implicit operators for the Id Class that convert it to an Entity or an EntityReference

The Call Site service.Delete(id) shows a compiler error: Error CS0121 The call is ambiguous between the following methods or properties: 'Extensions.Delete(IOrganizationService, Entity)' and 'Extensions.Delete(IOrganizationService, EntityReference)'

Which I understand from my implicit operators I've defined. What I'm not understanding, is why the DLaB.Xrm namespace is closer than the DLaB.Xrm.Test namespace, even though I do not have a using directive for it.

Upvotes: 1

Views: 228

Answers (1)

Daryl
Daryl

Reputation: 18895

There are 6 rules of closeness defined in Eric's Blog:

  1. A method first declared in a derived class is closer than a method first declared in a base class.
  2. A method in a nested class is closer than a method in a containing class.
  3. Any method of the receiving type is closer than any extension method.
  4. An extension method found in a class in a nested namespace is closer than an extension method found in a class in an outer namespace.
  5. An extension method found in a class in the current namespace is closer than an extension method found in a class in a namespace mentioned by a using directive.
  6. An extension method found in a class in a namespace mentioned in a using directive where the directive is in a nested namespace is closer than an extension method found in a class in a namespace mentioned in a using directive where the directive is in an outer namespace.

The first 3 do not apply since there is no applicable method for an interface. The 4th is the key. DLaB.Xrm is in a nested namespace, even though its in a different assembly. I had falsely believed that you had to list the using directive of the extension method in order to be able to access it from another class. I now know that extension methods in nested namespaces are automatically available.

Changing namespace DLaB.Xrm.Tests to namespace NotNested.DLaB.Xrm.Tests eliminates all the rules of closeness, and the ambiguous call is resolved by normal signature overload resolutions rules. The actual fix I used was just to make the Delete call generic, which eliminates the two implicit conversion overloads from contention, and allows the call to be resolved to my preferred method.

Upvotes: 2

Related Questions