DDiVita
DDiVita

Reputation: 4265

How to write an extension method to return the type being used?

I have an extension method that allows a user to turn off lazy loading.

public static IBaseEntityService<TEntity, TPrimaryKey> EnableLazyLoading<TEntity, TPrimaryKey>(
        this IBaseEntityService<TEntity, TPrimaryKey> baseService,
        bool enabled)  
        where TEntity :Entity<TPrimaryKey> where TPrimaryKey : struct
    {
        baseService.UnitOfWork.EnableLazyLoading(enabled);
        baseService.UnitOfWork.EnableProxyCreation(enabled);
        var type = baseService.GetType().;
        return (type)baseService;
    }

Here is a sample of the base interface:

 public interface IBaseEntityService<TEntity,TPrimaryKey> : 
    IBaseService<TEntity, TPrimaryKey> 
    where TEntity: Entity<TPrimaryKey> where TPrimaryKey : struct
{

    TEntity FindByTenantId(Guid tenantId);
   IBaseEntitySpecification<TEntity,TPrimaryKey> Specification { get; } 
}

Let's say I have this service that implements the IBaseEntityService<TEntity,TPrimaryKey> interface:

public IUserService: IBaseEntityService<User,int>
{
      User FindByUserName(string username);
}

Here is what I want to achieve:

var user = _userService.EnableLazyLoading(false).FindByUserName("someUsername");

As you can see in my example after the EnableLazyLoading(false) I can get to the FindByUserName method. Currently, since it only returns an IBaseEntityService I do not have that option.

I want to ensure the extension method knows to return the IUserService since it implements the IBaseEntityService. I know it would have to cast it at some point and I want to avoid having to write a specific implementation of that same EnableLazyLoading method for the IUserService

I thought of something like this, but it seems like I could do something without having to implicitly call a Cast method:

public static TEntityService Cast<TEntity, TPrimaryKey, TEntityService>(
            this IBaseEntityService<TEntity, TPrimaryKey> baseEntityService)
            where TEntity : Entity<TPrimaryKey>
            where TPrimaryKey : struct
            where TEntityService : IBaseEntityService<TEntity, TPrimaryKey>
        {
            return (TEntityService) baseEntityService;
        }

So it might work like this:

var user = _userService.EnableLazyLoading(false).Cast<IUserService>().FindByUserName("someUsername");

Upvotes: 2

Views: 156

Answers (2)

BartoszKP
BartoszKP

Reputation: 35891

The best solution, provided by Ben in his answer is:

public static T EnableLazyLoading<T, TEntity, TKey>(
    this T @this,
    bool enabled)
                       where T       : IBaseService<TEntity, TKey>
                       where TEntity : Entity<TKey>
                       where TKey    : struct
{
    @this.UnitOfWork.EnableLazyLoading(enabled);
    @this.UnitOfWork.EnableProxyCreation(enabled);
    return @this;
}

Another solution, cheaper but but not that clean is to use the dynamic keyword:

public static dynamic EnableLazyLoading<TEntity, TPrimaryKey>(
    this IBaseEntityService<TEntity, TPrimaryKey> @this,
    bool enabled)  
                        where TEntity     : Entity<TPrimaryKey> 
                        where TPrimaryKey : struct
{
    @this.UnitOfWork.EnableLazyLoading(enabled);
    @this.UnitOfWork.EnableProxyCreation(enabled);
    return @this;
}

The cost is that you loose the convenience of IntelliSense, and possibly some performance.

Upvotes: 1

Ben
Ben

Reputation: 6059

You could just make the receiver of EnableLazyLoading a generic type constrained to be an IBaseService. It's wordy, but ought to do the trick.

public static TService EnableLazyLoading<TService, TEntity, TKey>(this TService service)
    where TService : IBaseService<TEntity, TKey>
    where TEntity : Entity<TKey>
    where TKey : struct
{
    // do stuff
    return service;
}

That way, you get the actual service type as the return type. I haven't done C# in a little while, so I may be forgetting something important, but IIRC this works just fine.

Upvotes: 4

Related Questions