Eoin Campbell
Eoin Campbell

Reputation: 44268

How do I use C# Generics for this implementation when the Generic Decision is based on a DB Value

We have a content delivery system that delivers many different content types to devices. All the content is stored in a database with a contentID & mediaTypeID.

For this example lets assume the MediaType can be one of these 2 but in reality theres many more of them.

Gif
MP3

Because the content is stored in different places based on mediatype and requires different headers to be sent, theres a big nasty piece of legacy code that essentially switches on each mediatype and sets the correct parameters. *I'd like to change this into a more generic implementation. So heres what I've got so far in my wireframe

public interface IContentTypeDownloader
{
    MemoryStream GetContentStream();
    Dictionary<string, string> GetHeaderInfo();
}

public class GifDownloader : IContentTypeDownloader
{
    public MemoryStream GetContentStream(int contentID)
    {
        //Retrieve Specific Content gif
    }

    public Dictionary<string, string> GetHeaderInfo()
    {
        //Retrieve Header Info Specific To gifs
    }
}

public class MP3Downloader : IContentTypeDownloader
{
    public MemoryStream GetContentStream(int contentID)
    {
          //Retrieve Specific Content mp3
    }

    public Dictionary<string, string> GetHeaderInfo()
    {
        //Retrieve Header Info Specific To mp3s
    }
}

Which all seems sensible... Until I get to the Manager Class.

public class ContentManager<T> where T : IContentTypeDownloader
{
    public int ContentID { get; set; }

    public MemoryStream GetContent()
    {
        IContentTypeDownloader ictd = default(T);
        return ictd.GetContentStream(this.ContentID);
    }
    ... etc
}

The problem is, I still need to initialise this type with the specific IContentTypeDownloader for that mediaTypeID.

And I'm back to square 1, with a situation like

if(mediaTypeID == 1)
    ContentManager<GifDownloader> cm = new ContentManager<GifDownloader>();
else if (mediaTypeID == 2)
    ContentManager<MP3Downloader> cm = new ContentManager<MP3Downloader>();

etc...

Anyone any idea on how to make this last decision generic based on the value of the mediaTyepID that comes out of the Database

Upvotes: 2

Views: 322

Answers (3)

James
James

Reputation: 82096

I duno if you can generify it any further than what you have already.

Perhaps creating a factory class which just returns you the correct Interface based on whatever media type would be a neater solution i.e.

public static class MediaInterfaceFactory
{
    public static IContentTypeDownloader Create(int mediaId)
    {
       switch (mediaId)
       {
            case 1:
                return new GifDownloader();
            case 2:
                return new Mp3Downloader();

    }
}

Upvotes: 2

Stan R.
Stan R.

Reputation: 16065

I think it is totally acceptable to have a simple Factory Design Pattern. Of course you can go really crazy and have a Dictionary of Enum representing you content type as key and the actual Type as value. And then in your factory class take in the Enum and use Activator.CreateInstance to return the right type.

public enum MediaTypes
        {
            GIF,
            MPEG
        }


Dictionary<Enum, Type> dictionary = new Dictionary<Enum, Type>() { { MediaTypes.GIF, typeof(GifDownloader) } };

public static IContentTypeDownloader Create(MediaTypes mediaType)
    {
      Type type= dictionary[mediaType];
      IContentTypeDownloader contentObject = Activator.CreateInstance(type);

    }

you can use this with mediaId's of course as well in the dictionary..

Upvotes: 1

flq
flq

Reputation: 22849

I think I may go about this like that:

  • Define an attribute you place on your specific content type downloaders:

    [MediaType(ID = 1)]

  • Upon startup scan your assembly and set up a dictionary of style contentID -> instance of the content type downloader associated with the id (you figured that out with the attribute)

  • Have your content type downloaders some method like IContentTypeDownloader Clone();

Then your code should be reduced to

downloader = dictionary[mediaType].Clone();

With a DI Container of your choice you may be able to replace the dictionary and scanning with e.g. a convention-based configuration.

Upvotes: 1

Related Questions