I am working on a DI problem that I think I understand the cause of, but I need some suggestions to work around.
I have built a stand alone assembly that talks to Sql (call this assembly a), and another assembly that contains business logic (call this assembly b). I created an interface for the db class in the b assembly. Since the interface isn't part of the db assembly, I don't need any references to the db project, and I can load a reference to the db assembly or a stub if I want to run unit tests at run time and neither assembly needs to know about the other.
I can write code in the business logic library that compiles that looks like this: (pretend that a and b are namespaces in their respective assemblies)
a.IDatabaseClass db_class = (a.IDatabase)new b.Database();
When I try to run this however, I get an invalid cast exception. I think it compiles because the interface matches the class perfectly, but fails at run time because object signature doesn't see IDatabase in the inheritance chain of the Database class.
In c++ you can get away with casting anything however you want, but c# is a little bit stricter about casting object pointers. Even though the class has all the correct function signatures, it is blowing up because the objects don't match.
Now I could put the db object interface in the assembly with the db object, but then the business logic needs a reference to the db assembly. Also, this just creates complications down the road, because if I write a stub db object in a unit test, I need a reference to the db assembly just for the interface that I am going to use in my test stub object. This doesn't seem to be disentangling couplings by doing this...
I could put all the interfaces in a third assembly that is a parent to the db assembly, the business logic, and the unit tests. This is how you can solve circular dependency issues. However, this ties the db assembly to the parent assembly, and makes it a lot less modular to be used with other projects.
I am open to suggestions about how I can set up each assembly so that they function independently and can be used for DI. I suppose I could keep the test stub objects in the same assembly as the real code, but that seems weird.
Solution: One of the replies below makes the comment that what I was shooting for is basically duck-typing for interfaces. C# doesn't support duck typing currently, but I was thinking it might be possible since interface implementation acts in similar way to what you might call a partial class pointer (or probably more accurately, a collection of function pointers). My experiment was showing me otherwise, and that is why.
so until Redmond puts 'more mallard' into c#, looks like we cannot reach that final elegant level of decoupling assemblies entirely.
Create a reference library containing your common interfaces. This way you'll have a common source with all of your implementation agnostic logic.
I don't have the experience to feel confident making this a categorical statement, but I strongly suspect that coupling is primarily an issue when you're talking about individual types referencing specific interfaces and how they behave. Assemblies don't have the same sort of specificity that would make coupling a problem.
Let me amend and extend. One assembly must reference the other, or both must reference a common assembly. C# doesn't have duck-interfacing, if that's a thing.
Now I think the best practice is to isolate the business logic from external interfaces, so if you're going to do anything you should keep your business logic isolated from your DB implementation. So DB/Application assembly references business logic, but not the other way around.
Here's more information about the dependency orientation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With