0

Say i have a base class, called BaseService, then i have another class called AuditService, which inherits from BaseService, and maybe i have another class called FooService that also inherits from BaseService.

So hierarchy looks like this

BaseService
|
---- AuditService
|
---- FooService

Now what if i need a new service that contains all the features of AuditService and FooService?

c# doesnt allow you to inherit from multiple objects, so how would i go about this?

I dont want to have to inherit from 1 service and re-code all the rest of the items again.

EDIT:

To include a few more details about my classes, some code below.

As you can see i override particular functions from base service in the foo service and auditable service. In future i will need a service that implements all functionality of baseservice, foo service and auditable service.

BASESERVICE

public abstract class BaseService<TEntity> 
    where TEntity : class, IBaseEntity
{
    protected DataContext _context;

    protected BaseService(DataContext context)
    {
        _context = context;
    }

    public virtual async Task<ICollection<TEntity>> GetAllAsync()
    {
        return await _context.Set<TEntity>().ToListAsync();
    }

    public virtual Task<TEntity> GetAsync(long id)
    {
        return _context.Set<TEntity>().FindAsync(id);
    }

    public virtual Task<int> AddAsync(TEntity t)
    {
        if (_context.Entry(t).State == EntityState.Detached)
        {
            _context.Set<TEntity>().Add(t);
        }

        _context.Entry(t).State = EntityState.Added;

        return _context.SaveChangesAsync();
    }

    public virtual Task<int> AddAllAsync(ICollection<TEntity> all)
    {
        foreach (var item in all)
        {
            if (_context.Entry(item).State == EntityState.Detached)
            {
                _context.Set<TEntity>().Add(item);
            }

            _context.Entry(item).State = EntityState.Added;
        }

        return _context.SaveChangesAsync();
    }

    public virtual Task<int> UpdateAsync(TEntity updated)
    {
        _context.Entry(updated).State = EntityState.Modified;

        return _context.SaveChangesAsync();
    }

    public virtual async Task<int> DeleteAsync(long key)
    {
        TEntity entity = await GetAsync(key);

        _context.Entry(entity).State = EntityState.Deleted;

        return await _context.SaveChangesAsync();
    }

    public virtual Task<int> DeleteAsync(TEntity t)
    {
        _context.Entry(t).State = EntityState.Deleted;

        return _context.SaveChangesAsync();
    }

}

AUDITABLESERVICE

public class AuditableService<TEntity> : BaseService<TEntity>
    where TEntity : class, IAuditableEntity
{

    public virtual override Task<int> DeleteAsync(long key)
    {
        return DeleteAsync(key, true);
    }

    public virtual async Task<int> DeleteAsync(long key, bool useFlag)
    {
        TEntity entity = await GetAsync(key);

        return await DeleteAsync(entity, useFlag);
    }

    public virtual override Task<int> DeleteAsync(TEntity t)
    {
        return DeleteAsync(t, true);
    }

    public virtual Task<int> DeleteAsync(TEntity t, bool useFlag)
    {
        if (useFlag)
        {
            // flag item as deleted
            t.IsDeleted = true;

            return UpdateAsync(t);
        }
        else
        {
            return base.DeleteAsync(t);
        }
    }
}

FOOSERVICE

public class FooService<TEntity> : BaseService<TEntity>
    where TEntity : class, IBaseEntity
{
    protected IValidator<TEntity> _validator;

    protected ValidatorService(DataContext context)
        : base(context)
    {

    }

    public override async Task<int> AddAsync(TEntity t)
    {
        var results = await _validator.ValidateAsync(t);

        if (results.IsValid)
        {
            return await base.AddAsync(t);
        }
        else
        {
            throw new ValidationException(results.Errors);
        }
    }

    public override async Task<int> UpdateAsync(TEntity updated)
    {
        var results = await _validator.ValidateAsync(updated);

        if (results.IsValid)
        {
            return await base.UpdateAsync(updated);
        }
        else
        {
            throw new ValidationException(results.Errors);
        }
    }

}
Gillardo
  • 9,518
  • 18
  • 73
  • 141

2 Answers2

2

As @Christos said, there is no multiple interitance support in .net - you have to describe your intentions to get a solution that fits your needs.

Composition could be a solution e.g. create another class that derives from BaseService holding references to a AutidService and a FooService instance to delegate operations to them (that would be a solution if BaseService already has a good interface for your new service. A benefit could be, that you could controll the delegation like with a decorator pattern, probably that would be a clean soultion following the SRP (Single responsibility principle)).

Give us more information about HOW the new service should re-use the defined services...

Your added code fits perfect for the composition since BaseService defines all Methods that your concrete services provide:

public class NewService<T> : BaseService<T>
{
    private readonly FooService<T> _fooService;
    private readonly AuditableService<T> _auditableService;
    public NewService (AuditableService<T> auditableService, FooService<T> fooService)
    {
        if(auditableService  == null)
            throw new ArgumentNullException("auditableService ");

        if(fooService == null)
            throw new ArgumentNullException("fooService");

        _auditableService = auditableService;
        _fooService = fooService;
    }

    public override Task<int> AddAsync(T t)
    {
         return _fooService.UpdateAsync(t);
    }

    public override Task<int> DeleteAsync(T t)
    {
         return _auditableService.DeleteAsync(t);
    }
}

Edit UnitTesting You could mock the passed services (since they have a common abstract base class) and verify that they are called by your new service. Thats more interaction testing than unit testing but it will ensure that you call the correct methodes. If there is no pre-/post-processing in the new service you don't need any further tests for the new service.

MrWombat
  • 649
  • 9
  • 19
  • I have added some code to show you how the classes inherit – Gillardo Dec 03 '14 at 11:06
  • (nice name), this makes sense. Thank you. Another question, slightly off topic if you dont mind. Unit testing this newService, i am guessing that i would only need to test "new" functions in NewService, and unit tests would test BaseService functionality separately? I wouldnt have to retest each baseservice function from with newservice would i? – Gillardo Dec 03 '14 at 12:06
  • 1
    Would this change if i have multiple NewServices that should use both the validatorService and fooService? Or should i simply create a new ValidateFooService which uses the above pattern and each new service that needs both services, just inherits that? As i wouldnt want to recode each AddAsync to call fooService in each new service i create (could be 25 of them) – Gillardo Dec 03 '14 at 13:32
  • I would say thats up to you ;) To Add new behavior, its good to go for composition while different implementations are easily done with inheritance. If AddAsync is the only method that you will change, i would go for inheritance. For testing i would recommend that you test at least the differences in each implementation of AddAysnc. – MrWombat Dec 03 '14 at 13:47
1

Multiple inheritance isn't supported in .net using classes. You can inherit from only one class but you can implement as many interfaces as you want. Hence a mechanism that can be used to implement multiple inheritance is using implementing multiple interfaces.

The below is straightforward:

public class AuditService : BaseService
{ }

public class FooService : BaseService
{ }

Now if you want a class all teh services that AuditService and FooService have, you could declare an interface, e.g. IUnionInterface, in which you will include all that you want and then you could ask from your class implements the IUnionInterface

public class SuperService : IUnionInterface
{ }

or you could declare an interface that contains the non common features of AuditService and FooService, INonCommonFeatures and then make your class inherit from your BaseService and implement INonCommonFeatures.

public class SuperService : BaseService, INonCommonFeatures
{ }

Now you have only to implement the features of INonCommonFeatures.

Christos
  • 53,228
  • 8
  • 76
  • 108
  • Could please the downvoter explain me where I am wrong? Thank you in advance. – Christos Dec 03 '14 at 10:56
  • when you say implement an interface what do you mean (i know what an interface is) but you cannot add actual method code to the interface, so how would this solve the problem? I have now looked at other posts and they say about using extension methods on interfaces, is this what you mean? – Gillardo Dec 03 '14 at 11:02
  • @user2736022 I mean to use an interface an implement the corresponding methods. I know that you cannot add the implementation of a method in an interface. you can only define it's signature. But since the most code you want is implemented in `BaseService`, then you have to implement only the method that aren't common between `AutidService` and `FooService`. – Christos Dec 03 '14 at 12:04