Hello.

I have a class that depends on SqlConnection .

In my DI Config written:

 container.RegisterType<IDbConnection, SqlConnection>(); 

I need a property: con.ServerVersion , which can be accessed only if we do not use the interface when creating an object, that is, as follows:

 SqlConnection con = new SqlConnection() 

which creates a strong dependency.

How can you avoid strict dependence in this situation?

  • By what I understood, I would solve in 2 ways 1). would work with IDbConnection and add another third-party DbConPropertyResolver who could ask "what do you know about this IDbConnection", and he himself can be in any other assembly and communicate through the IDbConPropertyResolver interface 2) expand through extensions IDbConnection, and you can directly define the extension in another assembly - vitidev
  • 3) Expand the IDbConnection interface through the successor and make a wrapper on a specific SqlConnection and work with this inherited interface where you need to know what is not in IDbConnection - vitidev
  • If we consider the third option, as I understand it, we create a strong dependency in the interface itself which inherits and extends IDbConnection ?? - aperkot
  • Not. We simply add the GetVersion method or the ServVersion {get;} property to the interface. Those who do not need it, continue to work with IDbConnection, and for those who need it, they will require a more specific interface heir. But already in the implementation of the wrapper that implements this interface there will be a dependency (and the implementation can be in any assembly and the container will be the place of comparison). - vitidev
  • Sorry that I ask again for the hundredth time) I just want to be sure that I understood exactly: 1. Inherit the IDbConnection interface, with one property ServerVersion; 2. We implement this interface, the field of this class is SqlConnection conection, and in the constructor, for example, we create it, passing the connection string, well, we implement all the necessary properties and methods by simply calling them from SqlConnection conection ?? - aperkot

2 answers 2

If you need ServerVersion , then your code basically cannot work with anything other than SqlConnection - it is also inoperative on other IDbConnection implementations.

The fact that in this case you will see the absence of a dependency in code, replacing it with an IDbConnection caste somewhere in the code will in fact not eliminate the dependency. Just disguise well, and make it checkable at runtime, not at compile time (which in this particular case is bad rather than good).

Want a good solution - use Interface segregation - split the SqlConnection public interface into two

  • IDbConnection , which will be responsible for the execution of requests (if at all)
  • IDbVersionProvider - which will respond by providing a version.

Implement IDbVersionProvider by creating a SqlConnection (directly) and extracting a version from it.

  • That is, IDbVersionProvider will be an extension of IDbConnection with the server version and will be used exclusively for this? - aperkot
  • @aperkot is not an extension of IDbConnection. Just an interface with a single Version method / property. Which is implemented by a class that internally creates a SqlConnection and returns a version - PashaPash

We have IDbConnection and its implementation with something that is not in IDbConnection, that is, dependence on implementation. And also some ioC (DI). Well, the knowledge that the implementation can be in any assembly is the main thing to register in the container, and this can be done in any assembly. A typical variant is that the container is prokibyvaetsya by class-recorders in assemblies (modules), where each module registers what it has.

Solution options:

1 Third party service.

This is a head-on decision. If IDbConnection does not provide the necessary data, then we simply create a service that will give us the data for the connection. That is, we create an interface like

 interface IMetaDataProvider{ int GetServerVersion(IDbConnection conn); } 

and we do the implementation of this in any place where there is an obvious dependency, that is, in another assembly.

 interface MetaDataProvider : IMetaDataProvider{ int GetServerVersion(IDbConnection conn){ var tmp=conn as SqlConnection; ... достаем что нам надо } } 

and register this service in IoC and where you just need to prokidyvaem it and the client code learns from him about the connection that he needs.

2 Extending IDbConnection through inheritance.

We make an IDbConnection successor with the properties we need.

 interface IMyDbConnection : IDbConnection { int ServerVersion {get;} } 

since SqlConnection does not know about our IMyDbConnection, you will have to write a wrapper.

 class SqlConnWrapper : IMyDbConnection { private SqlConnection _conn; ...тут реализация IDbConnection с простым прокидываем к conn int ServerVersion => _conn.ServerVersion; } 

I remind you that the wrapper can be defined in another assembly and the IoC will bind the interface and implementation. We register the SqlConnWrapper for IMyDbConnection and IDbConnection (the details of the registration depend on the container)

And those who need knowledge of ServerVersion will request IMyDbConnection for themselves, where they will obviously read the property, while others will ask for IDbConnection

3 IDbConnection Extension

just write the extension method for IDbConnection

 public static class IDbConnectionEx { public int GetVersion(IDbConnection conn) { var tmp = conn as SqlConnection; ...получаем версию } } 

here we must have a direct dependency on the assembly where the extension was defined, but from the extension extension we can do anything again ... for example, refer to the global DI (if there is one) where at least something like IMetaDataProvider lies and delegate the request to it , and MetaDataProvider can be in any assembly.

  • Thanks for such a bold answer and time allotted! - aperkot