Question

I have build a generic data converter for my service layer and I am now trying to build interfaces so that I can use dependency injection (Autofac) to do the binding. However I cannot get the final interface needed for injection to compile. I have build a simple example which I have listed below:

public interface IMyData
{
    int I { get; set; }
}

public class MyData : IMyData
{
    public int I { get; set; }
}

public interface IMyDto
{
    int J { get; set; }
}

public class MyDto : IMyDto
{
    public int J { get; set; }
}

public class MyService<TData, TDto> : IMyService<TData, TDto>
    where TData : class, new()
    where TDto : GenericDto<TData, TDto>, new()
{
    public TDto DoSomething(TData data)
    {
        return new TDto();
    }
}

public interface IMyService<in TData, out TDto>
    where TData : class, new()
    where TDto : GenericDto<TData, TDto>, new()
{
    TDto DoSomething(TData data);
}

public class TryToUse
{

    private MyService<MyData, MyDto> actualServiceActualClasses;         //OK

    private IMyService<MyData, MyDto> interfaceServiceActualClasses;     //OK

    //fails with two messages, both the same for IMyData and IMyDto
    //'TryGenericInterfaces.IMyDto' must be a non-abstract type with a public
    //parameterless constructor in order to use it as parameter 'TDto' in the 
    //generic type or method 'TryGenericInterfaces.IMyService<TData,TDto>'

    private IMyService<IMyData, IMyDto> interfaceServiceInterfaceClasses; //fails
}

You will see I have embedded the compile time error above the offending declaration. Needless to say its the final declaration that I need for dependency injection to work (and also because its how I want to declare it anyway).

It is a limitation of interfaces or have I simply missed something?

UPDATE

Thanks @D Stanley and others for pointing out my misunderstanding. I think you can see what I am trying to do, which is build a generalised data to DTO and DTO to data converter. The code works fine, with the help of AutoMapper. (I have updated the code to include the GenericDto which each DTO inherits).

The question was not well thought out and I am sorry for that. I have provided an answer which agrees with you guys and also explains what I was ultimately trying to achieve (which is actually working now).

Was it helpful?

Solution 2

Sorry, my stupid mistake. I don't know what I was smoking yesterday but after a good nights sleep I am clearer now.

@rhughes pointed out (see note below) it was the new() constraint that was stopping the Generic definition from taking interfaces, which was a good suggestion. I did try it but Entity Framework (EF) won't work with interfaces (known design point) so I stayed with real classes in my Generic definition, i.e.

private IMyService<MyData, MyDto>

To use my generic services with EF I will have to relax my design goal of only passing interfaces between layers. However, on balance, the gain of the generic services and the fact that the classes are normally just data means I am OK about relaxing my rule of only passing interfaces between layers in these cases.

Also a few people have asked why I even need a generic in the example I put up. I should explain that as it was a interface issue I removed some code to make the question simpler (The code was rather long). In fact the signature of the service included a generic class as a constraint for the TDto part. The full definition is:

public class MyService<TData, TDto> : IMyService<TData, TDto>
    where TData : class, new()
    where TDto : GenericDto<TData, TDto>, new()

The GenericDto is an abstract class which contains a number of methods for transforming data for TData->TDto and TDto->TData. These default methods use AutoMapper and below is an example of building a list query to take a EF class and transform it to an IQueryable TDto:

internal virtual IQueryable<TDto> BuildListQueryUntracked(TemplateWebAppDb context)
{
    Mapper.CreateMap<TData, TDto>();
    return context.Set<TData>().AsNoTracking().Project().To<TDto>();
}

These default transformations can be overridden by the actual Dto class when the transform is too complicated for AutoMapper. However I should say I have found AutoMapper pretty good, especially now that it can project to IQueryable.

Thanks for everybody's help and sorry about the brain freeze on my part.

OTHER TIPS

The error seems pretty clear - you have specified that TData and TDto must be classes with parameterless constructors, but you are trying to use interfaces (which neither are classes nor have constructors) as the generic parameters. I don't see around that unless you can remove the class, new() restrictions

Would this work instead?

public interface IMyService<in TData, out TDto>
    where TData : IMyData
    where TDto : IMyDto
{
    TDto DoSomething(TData data);
}

and

public class MyService<TData, TDto> : IMyService<TData, TDto>
    where TData : class, IMyData, new()
    where TDto : class, IMyDto, new()
{
    public TDto DoSomething(TData data)
    {
        return new TDto();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top