0

I'm trying to come up with a way to write a generic interface that can be implemented for multiple data stores, basically the generic type will specify the ID for the data in the data store (some of these data stores use strongly typed libraries, so you have to call something like long createNode(Node node))

For example the system we are using now, uses longs for the IDs, but if we made the transition (or use both at the same time) to something like SQL it would most likely be GUIDs. The entities a the business logic layer are pretty much set, think of it as basically a file system with a bunch of custom attributes on the nodes.

So far I tried something like this:

public interface DataThing<T>
{
   T CreateThing(CustomEntity node);
   CustomEntity GetThing(T ID);
}

public class LongDataThing : DataThing<long>
{
   long CreateThing(CustomEntity node)
   {
      //...implement
   }

   CustomEntity GetThing(long ID)
   {
      //...implement
   }
}

...do the same thing for GUID/Int/string...whatever

Then when it comes to instantiating the class to work with basically a factory design pattern is where I'm having problems. I thought I could do something like:

private DataThing myDataStore;

switch(config.dbtype)
{
   case "longDB":
      this.myDataStore = new LongDataThing();
   break;

   case: "stringDB":
      this.myDataStore = new StringDataThing();
   break;

   //...etc.
}

But I can't create private DataThing myDataStore; without specifying it as long, string...etc, which means the business logic already has to know which type is being implemented instead of just asking the factory for a datastore object.

So what am I missing here? Can you not abstract out the datatype of an interface like this and have it transparent to the calling 'business logic' and figure out which implementation is desired from a config file or some other outside logic?

Plinkey
  • 21
  • 3
  • *something like SQL it would most likely be GUIDs* You shouldn't do this unless you have a good reason. [Int's still win as ids on SQL](https://stackoverflow.com/questions/404040/how-do-you-like-your-primary-keys) – Liam Mar 07 '19 at 16:02
  • 1
    What your trying to do here sounds an awful lot like [gold plating](https://blog.codinghorror.com/gold-plating/). Your presuming a requirement that may (or probably may not) exist in the future. You have no idea about this. My advice, complete your current design goal not what you think it may be in 2 years time. i.e. just don't bother with this it's overly complicated and likely going to be detrimental to your design. – Liam Mar 07 '19 at 16:05
  • 2
    ORMs like Entity Framework, NHibernate, Dapper (a micro-ORM) already provide what you describe at a higher level of abstraction, with a lot more features. – Panagiotis Kanavos Mar 07 '19 at 16:05
  • It's a current requirement, the goal is to implement this in our current datastore which is $$$ and keep the same external interfaces for customers and in the event that we move to a custom datastore solution or something that is less $$$ there is no change to the API. I could be attacking the problem from the wrong end, I will admit that. – Plinkey Mar 07 '19 at 16:07
  • But you have no idea what this "datastore" is or it's requirements so there is a fair chance that you won't get this right, because your guessing, so why do it now? Do it when you know the requirements. Anyway, @PanagiotisKanavos is right, just use an ORM. I'd recommend [Dapper](https://github.com/StackExchange/Dapper) – Liam Mar 07 '19 at 16:10
  • 1
    The current datastore that we're writing against isn't a relational database. It's basically a document management systems that has a SOAP interface for C#/JAVA. So there is no SQL in the traditional sense. – Plinkey Mar 07 '19 at 16:12
  • To go back to your question what do you want `CreateThing` to return if you haven't specified `T`? If you want `CreateThing` to return different things then you need to tell the compiler what these things implement. Say you have some code that uses `CreateThing` and this returns a `long`, how can this code possibly work if `CreateThing` suddenty starts returning a `Guid`?! It can't. So, no you can't do what you want to do. Interfaces define common functionality, if the return types differ this isn't common. – Liam Mar 07 '19 at 16:26
  • @Plinkey in that case the "generic" interface you try to implement will have the wrong shape. Besides, EF Core does target NoSQL databases like CosmosDB. It's actually *easier* in this case, since it's easier to map an object graph to a document than multiple related tables – Panagiotis Kanavos Mar 07 '19 at 16:27
  • 1
    @Liam that's kinda what I thought, I had a work around that had all of the interfaces between layers as string and the interface implementation would be in charge of it's own string boxing/unboxing into it's API tyep but it was pretty ugly but may be the way to go in this case. – Plinkey Mar 07 '19 at 16:29
  • You need to be careful, boxing and unboxing is not free, in fact it's a very expensive process and should generally be avoided. Continually doing this will seriously determent the performance of your application. – Liam Mar 07 '19 at 16:31

0 Answers0