Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for interfaces? Or better yet full Dipendency Inection and IoC? #690

Open
ravensorb opened this issue Feb 1, 2018 · 10 comments
Open

Comments

@ravensorb
Copy link

I was curious if there are anything to support DI and IoC? This is becoming more and more of a common pattern and i could see a lot of value of it being support in this library.

Consider the following

public interface IAnimal
{
     int TagId { get;set}
     int Age {get;set;}
     int Weight {get;set;}
}

public interface IDog : IAnimal
{   
    string Breed {get;set;}
}

If you think about it, everything that you need to create tables and insert data can be found in the interfaces at least when it comes to creating the tables and writing the data. The fun comes with reading and this could be handled via DI or and IoC pattern with a resolver.

Thoughts?

@ptsoccer
Copy link

ptsoccer commented Feb 5, 2018

What does this get you? What's the difference between a class that has a bunch of getters and setters (current design) and a class that implements an interface that has a bunch of getters and setters (proposed design, if I'm understanding correctly)?

@ravensorb
Copy link
Author

Those are the interface descriptions -- not the concreate classes. It was a simplistic use case is all. For my actually use case, I have a very large scale enterprise application that is 100% based on the Design by Contract pattern to allow for 100% separation of concerns. This allows for different teams to work on different part of the application without impacting each other during releases (different pieces have different release schedules). We have a set of "contract resolvers" using Autofac (right now although we may switch), that we use through out the application to get a specific instance of what ever contract we need.

Ex:
var instance = Resolver.Resolve<IAnnualContract>();

this allows someone to work create an annual contract without needing to know much about it (they really shouldn't even need to care if you think about it)

So we are looking for a way to do something like

db.Save<IAnnualContract>(instance.)
or
db.FetchAll<IAnnualContract>()

the first one is easy (several ORM frameworks handle that). The second one seems to be the difficult one. If you look at what json.net does it has a concept of a contract resolver that is used to create a concrete instance of the object based on the type (it uses the resolver pattern above as well) and then hydrates the properties. This allows for the developers loading the data to again not have to really care or worry about the concrete instance, they just know that it will have the properties defined on the contract loaded correctly from the rdbms.

Does that help?

@ptsoccer
Copy link

ptsoccer commented Feb 5, 2018

I see, I do understand what you are trying to do.

What happens if you have an implementation of an IAnnualContract that requires more information to be stored than the other implementations, do you just add the union of all the properties of all the implementations into the interface?

@ravensorb
Copy link
Author

ravensorb commented Feb 5, 2018

Fair question, however I'd ask that if the developer or architect is deciding to use the interface approach, wouldn't it be safe to say that those other properties are not needed in the persistence layer? If they were they would be promoted. The main reason being is - how would manage schema changes in that scenario? In this pattern is the interfaces that defines the schema not the concrete instance, right?

@ptsoccer
Copy link

ptsoccer commented Feb 5, 2018

I agree managing the schema would be extremely error prone, I was trying to figure out if I was missing something. I suppose if you are confident all of your contract's will always need the same groups of data this would would work.

I'm also curious how something like FetchAll would work. How would it know which class to instantiate?

@ravensorb
Copy link
Author

wouldn't it just return an IList of the Interface?

var single = db.Fetch<IAnnualContract>() --< IAnnualContract
and
var listResult = db.FetchAll<IAnnualContract>() <-- IList < IAnnualContract >

or am I missing something?

@ptsoccer
Copy link

ptsoccer commented Feb 5, 2018

Well it has to instantiate a concrete type which implements the interface, how would it know which to pick? There would also have to be some place in SQLite you register the list of types that implement this interface.

@ravensorb
Copy link
Author

ravensorb commented Feb 5, 2018

that is what the DI container would handle. Right now I believe you are using var obj - Activator.CreateInstance(type), based on that I think the change would be just a matter of converting it to this var obj = container.Resolve(type) (this is simplistic though). To be a bit more complete it might make sense to have something like this

Here is an example of a generic DI resolver (Allows for using an DI framework)

public interface IContractResolver
{
    Func<Type, bool> CanResolve {get;set;}
    Func<Type,Type> Resolve { get;set; }
}

public class DefaultContactResolver : IContractResolver
{
   public Func<Type,bool> CanReslove {get;set;}
   public Func<Type, Type> Resolve {get;set;}
}

Used like this

    var container = new Autofac.ContainerBuilder().Build(); // this could just as easily use Ninject or Castle or any other DI framework

    var contractResolver = new DefaultContractResolver() { 
         CanResolve = (type) => container.IsRegistered,
         Resolve = (type) => container.Resolve
    }

    var dbConnection = new SQLiteConnection() { Resolver = contractResolver }

then inside the ORM framework you'd have something like this

   var obj = Resolver.CanResolve(type) ?? Activator.CreateInstance(type);

not sure if SQLiteConnection is the ideal place -- it was just a first thought :)

@ptsoccer
Copy link

ptsoccer commented Feb 5, 2018

Thank you, I believe this provides enough context for someone to tackle this if they wanted to.

@ravensorb
Copy link
Author

ravensorb commented Feb 5, 2018

I have taken a shot at the initial implementation and submitted a PR. Interested in taking a look at letting me know what you think?

I updated the CreateTable Test cases to show it working. It should be in pretty good shape, maybe just needs a few more test cases added to verify everything is accounted for (like relationships)

Side question -- any thought to splitting out the source into smaller files? At 4k+ lines VS is getting a bit on the sluggish side :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants