Pregunta

Tengo una pregunta, y voy a etiquetar este subjetiva ya que eso es lo que creo que se desarrolla en más de una discusión. Estoy esperando para algunas buenas ideas o algunas de pensamiento provocadores. Me disculpo por la cuestión de largo aliento pero es necesario conocer el contexto.

La pregunta es básicamente:

  • ¿Cómo lidiar con tipos concretos en relación con los contenedores IoC? En concreto, que es responsable de la eliminación de ellos, en caso de requerir la eliminación, y cómo conseguir que el conocimiento propaga a cabo al código de llamada?

¿Los necesita ser IDisposable? Si no es así, es que el código a prueba de futuro, o es la regla que no se puede utilizar objetos desechables? Si exige IDisposable-requisitos de interfaces y tipos concretos de ser a prueba de futuro, cuya responsabilidad se inyectan objetos como parte de las llamadas de constructor?


Editar : acepté la respuesta por @ Chris Ballard ya que es el más cercano uno con el enfoque que terminamos con.

Básicamente, volvemos siempre un tipo que se parece a esto:

public interface IService<T> : IDisposable
    where T: class
{
    T Instance { get; }
    Boolean Success { get; }
    String FailureMessage { get; } // in case Success=false
}

A continuación, devolver un objeto de la implementación de esta interfaz de vuelta de ambas .Resolve y .TryResolve, de modo que lo que tenemos en el código de llamada es siempre el mismo tipo.

Ahora, el objeto que implementa esta interfaz, es IService<T> IDisposable, y debe siempre desecharse. No es hasta que el programador que resuelve un servicio para decidir si el objeto IService<T> se deben eliminar o no.

Sin embargo, y esta es la parte crucial, si la instancia de servicio se deben eliminar o no, que el conocimiento se cuece en el objeto de implementar IService<T>, por lo que si se trata de un servicio de ámbito de la fábrica (es decir. Cada llamada a resolver termina con una nueva instancia de servicio), entonces la instancia de servicio estará dispuesto cuando está dispuesto el objeto IService<T>.

Esto también hizo posible para apoyar a otros ámbitos especiales, como la puesta en común. Ahora podemos decir que queremos instancias mínimas 2 de servicio, máximo 15, y por lo general 5, lo que significa que cada llamada a .Resolve o bien recuperar una instancia de servicio de un grupo de objetos disponibles, o construir una nueva. Y luego, cuando el objeto IService<T> que mantiene el servicio combinado se elimina, la instancia de servicio se libera de nuevo en su piscina.

Por supuesto, esto hizo que toda la mirada código como el siguiente:

using (var service = ServiceContainer.Global.Resolve<ISomeService>())
{
    service.Instance.DoSomething();
}

pero es un enfoque limpio, y tiene la misma sintaxis, independientemente del tipo de servicio o un objeto concreto en uso, así que elegimos que como una solución aceptable.


pregunta original sigue, para la posteridad


cuestión de largo aliento viene aquí:

Tenemos un contenedor IoC que usamos, y recientemente hemos descubierto lo que equivale a un problema.

En el código no COI, cuando se quiere utilizar, por ejemplo, un archivo, se utilizó una clase como esta:

using (Stream stream = new FileStream(...))
{
    ...
}

No había ninguna duda en cuanto a si esta clase era algo que llevó a cabo un recurso limitado o no, ya que sabía que los archivos tuvieron que ser cerradas, y la propia clase práctica IDisposable. La regla es simplemente que cada clase se construye un objeto de, que implementa IDisposable, tiene que ser eliminados. No se hicieron preguntas. No es hasta que el usuario de esta clase para decidir si llama Desechar es opcional o no.

Ok, así sucesivamente hasta el primer paso hacia el contenedor IoC. Vamos a suponer que no queremos el código para hablar directamente al archivo, pero en lugar de ir a través de una capa de direccionamiento indirecto. Vamos a llamar a esta clase un BinaryDataProvider para este ejemplo. Internamente, la clase es usando una corriente, que sigue siendo un objeto desechable, por lo que el código anterior sería cambiado a:

using (BinaryDataProvider provider = new BinaryDataProvider(...))
{
    ...
}

Esto no cambia mucho. El conocimiento de que la clase implementa IDisposable es todavía aquí, sin hacer preguntas, tenemos que llamar a Dispose.

Pero, vamos a suponer que tenemos clases que proporcionan datos que en este momento no utiliza ningún tipo de recursos limitados.

El código anterior podría entonces ser escrita como:

BinaryDataProvider provider = new BinaryDataProvider();
...

OK, hasta ahora tan bueno, pero aquí viene la carne de la cuestión. Vamos a suponer que queremos usar un contenedor IoC para inyectar ese proveedor en lugar de en función de un tipo de hormigón específico.

El código sería entonces:

IBinaryDataProvider provider =
    ServiceContainer.Global.Resolve<IBinaryDataProvider>();
...

Tenga en cuenta que asumir que hay una interfaz independiente disponible que podemos acceder a través del objeto.

Con el cambio anterior, lo que si más adelante queremos usar un objeto que realmente debe ser desechado? Ninguna parte del código existente que resuelve que la interfaz está escrito para disponer del objeto, así que lo que ahora?

La forma en que lo vemos, tenemos que elegir una solución:

  • Implementar la comprobación de tiempo de ejecución que comprueba que si un tipo de hormigón que se está registrando implementa IDisposable, requiere que la interfaz que se expone a través también implementa IDisposable. Esto no es una buena solución
  • enfore una restricción en las interfaces siendo utilizado, deben heredar siempre desde IDisposable, con el fin de ser a prueba de futuro
  • Enforce tiempo de ejecución que no hay tipos de hormigón pueden ser IDisposable, ya que este específicamente no es manejada por el código utilizando el contenedor IoC
  • Sólo tienes que dejar hasta el programador para comprobar si el objeto implementa IDisposable y "hacer lo correcto"?
  • ¿Hay otros?

Además, ¿qué pasa con la inyección de objetos en los constructores? Nuestro contenedor, y algunos de los otros recipientes que hemos visto en, es capaz de inyectar un objeto fresco en un parámetro a un constructor de un tipo concreto. Por ejemplo, si nuestro BinaryDataProvider necesita un objeto que implementa la interfaz ILogging, si hacemos cumplir IDispose- "capacidad" en estos objetos, quién es la responsabilidad de disponer del objeto de registro?

¿Qué opinas? Quiero opiniones, buenas y malas.

¿Fue útil?

Solución

(Negación:.. Estoy responder a esta basado en la materia java A pesar de que el programa C # No he proxy nada en C #, pero sé que es posible Lo sentimos acerca de la terminología java)

Usted puede dejar que el marco COI inspeccionar el objeto que está siendo construido para ver si es compatible IDisposable. Si no es así, se puede usar un proxy dinámico para envolver el objeto real que proporciona el marco COI para el código de cliente. Este proxy dinámico podría implementar IDisposable, por lo que siempre habías entrega un IDisposable al cliente. Mientras se trabaja con interfaces que debe ser bastante simple?

Entonces será apenas tiene el problema de la comunicación con el desarrollador cuando el objeto es un IDisposable. No estoy muy seguro de cómo this'd hacerse de una manera agradable.

Otros consejos

Una opción podría ser la de ir con un patrón de fábrica, por lo que los objetos creados directamente por el contenedor IoC nunca necesitan ser eliminados mismas, por ejemplo

IBinaryDataProviderFactory factory =
    ServiceContainer.Global.Resolve<IBinaryDataProviderFactory>();
using(IBinaryDataProvider provider = factory.CreateProvider())
{
    ...
}

Lo malo se añade complejidad, pero sí significa que el contenedor no crea todo lo que se supone que el desarrollador de disponer de -. Siempre es explícita código que hace esto

Si realmente quiere que sea obvio, el método de fábrica podría llamarse algo así como CreateDisposableProvider ().

En realidad se le ocurrió una solución muy sucia: su contrato IService viola el SRP , la cual es una gran no-no.

Lo que recomiendo es distinguir los llamados servicios "simples" de los servicios llamados "prototipo". Tiempo de vida de los "simples" es gestionado por el contenedor, que puede consultar en tiempo de ejecución si un caso particular implementa IDisposable e invocar Dispose() en el apagado de ser así.

Gestión de prototipos, por el contrario, es totalmente responsabilidad del código de llamada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top