Runtime: Hacer interfaces como la API oficial del proveedor de ADO.NET en lugar de clases

Creado en 26 sept. 2015  ·  174Comentarios  ·  Fuente: dotnet/runtime

Por lo que puedo ver actualmente en la página corefx-progress para System.Data.Common , las interfaces (IDbCommand, IDbConnection, etc.) se eliminaron a favor del uso de clases abstractas.

Pero en la nueva API, la mayoría de los métodos principales no son virtuales ni abstractos. Solo en DbCommand podemos ver esto:

public DbConnection Connection { get; set; }
public DbParameterCollection Parameters { get; }
public DbTransaction Transaction { get; set; }
public DbParameter CreateParameter();
public Task<int> ExecuteNonQueryAsync();
public DbDataReader ExecuteReader();
public DbDataReader ExecuteReader(CommandBehavior behavior);
public Task<DbDataReader> ExecuteReaderAsync();
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior);
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken);
public Task<DbDataReader> ExecuteReaderAsync(CancellationToken cancellationToken);
public Task<object> ExecuteScalarAsync();

Si bien estos métodos ciertamente pueden hacerse virtuales o abstractos, sería mucho más útil recuperar las interfaces reales y hacer que cualquier API pública dependa de estas interfaces en lugar de las clases abstractas.

Esto es sobre todo útil cuando se desarrollan bibliotecas. Hoy en día es muy difícil burlarse de un lector de datos para que devuelva un valor específico con fines de prueba. Lo mismo para asegurarse de que se llamó a ExecuteReaderAsync, no a ExecuteReader, etc.

Propongo que la fábrica de proveedores se haga como una interfaz:

public interface IDbProviderFactory {
    IDbCommand CreateCommand();
    IDbConnection CreateConnection();
    IDbConnectionStringBuilder CreateConnectionStringBuilder();
    IDbParameter CreateParameter();
}

Y luego siga desde allí al resto del proveedor a cosas como IDbDataReader , IDbTransaction , etc.

Sabemos que las interfaces se desincronizaron por alguna razón en el pasado y las clases abstractas se convirtieron en la API oficial, pero este ya no es el caso en corefx.

Tenga en cuenta que esto no significa eliminar System.Data.Common de ninguna manera, sino que haga que las clases Common implementen estas interfaces, y no usaría System.Data.Common a menos que esté implementando el proveedor. Las aplicaciones dependerían solo de las interfaces.

Considere esto para que la API sea más comprobable en corefx 1.0.

Relacionado con las discusiones sobre dotnet / runtime # 14302 y dotnet / runtime # 15269.

area-System.Data

Comentario más útil

No podemos agregar miembros a las interfaces

Correcto, y esa es una característica _buena_ de las interfaces. La preferencia por las clases base abstractas es la forma más segura de ayudar a la entropía de la API, en lugar de luchar contra ella.

Si bien no _ tiene_ que seguir los principios de OOD , le sugiero que lo haga cuando cree API de OO. En resumen, el Principio de Segregación de Interfaces (ISP) establece que _ ningún cliente debe verse obligado a depender de métodos que no utiliza_.

Si agrega nuevos métodos a una abstracción existente, automáticamente infringe el ISP.

Puede decidir que 'no tiene que adherirse a SOLID' porque es Microsoft y está trabajando con BCL, por lo tanto, 'las reglas normales no se aplican' (no comillas reales; simplemente parafraseando lo normal contra argumentos).

Habiendo mantenido un par de proyectos de código abierto durante 6-7 años, en mi experiencia es mejor mantener las interfaces pequeñas. Si necesita agregar nuevas capacidades a una abstracción, introduzca una nueva interfaz.

Todos 174 comentarios

Habrían cambiado a clases base abstractas porque las interfaces no se pueden versionar.

Supongo que los métodos no virtuales llaman a otro método que es virtual. Los métodos virtuales perjudican el rendimiento, por lo que no desea convertir las cosas en virtuales innecesariamente.

@JamesNK Ya veo. Pero dado que .NET Core es una nueva API y ADO.NET API es bastante estable durante casi una década, ¿cree que esto sigue siendo una preocupación válida? Además, al hablar del acceso a la base de datos, supongo que el costo de los métodos virtuales es eclipsada por el costo del acceso a la base de datos.

@NickCraver , @roji , @FransBouma ya que ustedes parecen tener interés en la API de ADO.NET, ¿tienen algo que decir al respecto?

@YoungGah , ¿vale la pena perseguir esto?

Supongo que los métodos no virtuales llaman a otro método que es virtual. Los métodos virtuales perjudican el rendimiento, por lo que no desea convertir las cosas en virtuales innecesariamente.

En el proceso de ejecutar una consulta en una base de datos remota y procesar los resultados, los nanosegundos perdidos en una llamada virtual son insignificantes. Además, ADO.NET usa este sistema desde el principio (también lo hacen muchas otras API en .NET) y nadie se quejó de que su código de base de datos sea tan lento debido a las llamadas a métodos virtuales;)

Puedo ver métodos asíncronos en su lista, así que supongo que hace un par de años, MS no pudo agregar asíncronos a IDbCommand. Quién sabe lo que traerá el mañana que requerirá nuevos métodos o propiedades.

Las interfaces no versionan.

El rendimiento es solo una de las razones para no hacer algo virtual. ¿Reducir el área de superficie para las implementaciones, tal vez? Dejaré que alguien en MS diga por qué decidió no hacerlo, no sé mucho sobre ADO.NET, así que solo estaría especulando.

@JamesNK Creo que sus preocupaciones son válidas, pero hay 2 puntos importantes a considerar:

  1. ADO.NET se ha mantenido bastante estable desde .NET 2.0, que una década, aunque la API asíncrona se agregó más tarde, no cambió el comportamiento de la API, solo agregó contrapartes asíncronas; no veo grandes cambios en el paradigma del controlador de la base de datos pronto
  2. Se supone que CoreFx tiene una idea de control de versiones diferente, ya que puede mantener el CLR anterior para aplicaciones antiguas. Por lo tanto, los problemas de control de versiones de la interfaz no deberían tener tanto impacto aquí.

Considere también que incluso un servidor SQL en "localhost" gastará al menos unos pocos ms solo para conectarse y devolver una consulta vacía. En la práctica, la mayoría de las consultas _fast_ a bases de datos relacionales tardan ~ 20 ms.

Ser capaz de simular la API con herramientas estándar como NSubstitute o Moq es mucho más valioso para el desarrollador de hoy que ahorrar microsegundos en búsquedas de métodos virtuales.

Supongo que no tengo una opinión muy fuerte aquí, pero aquí hay algunos comentarios:

  • Esté de acuerdo con lo anterior en que eliminar lo virtual frente a lo no virtual es insignificante en una API para el acceso a la base de datos
  • Las clases base permiten que ADO.NET proporcione implementaciones, supongo que de eso se tratan la mayoría de los métodos no abstractos no virtuales: la sobrecarga de ExecuteReader que no acepta un CommandBehavior pasa CommandBehavior.Default a la sobrecarga Eso hace. Si cambia a interfaces, todos los proveedores tendrán que implementar ExecuteReader () exactamente con el mismo texto estándar ...
  • No estoy seguro de que esto sea válido en todos los principales frameworks de burla, pero al menos en Moq, ¿no es tan fácil burlarse de una clase base como de una interfaz?

Entonces, en general, la idea de eliminar las clases base o las interfaces parece buena (más simple). Dado que no veo ninguna ventaja en las interfaces (a menos que me equivoque sobre la facilidad de burlarse), y las clases base pueden proporcionar una funcionalidad común (es decir, los métodos no abstractos no virtuales), supongo que el enfoque de Microsoft de deshacerse de las interfaces me parece bien ...

Estoy de acuerdo con @roji en todos sus puntos.

@roji solo una nota, no propongo eliminar las clases base, propongo agregar las interfaces como la API predeterminada. Las clases base aún pueden implementar el comportamiento predeterminado.

En cuanto a las pruebas, tuve grandes problemas para probar si mi API llamaba a los métodos correctos. Para verificar si ExecuteDataReader recibió los parámetros correctos, por ejemplo, debe verificar otro método protegido que se llama internamente con diferentes parámetros. Esto está lejos de ser ideal.

Actualmente, a menos que me equivoque, el único marco que puede burlarse de la API de ADO.NET es el marco de MS Fakes que puede burlarse de absolutamente cualquier cosa interceptando llamadas. Moq y otros no pueden hacer eso.

Me interesa ver si otras personas han tenido problemas similares.

@roji solo una nota, no propongo eliminar las clases base, propongo agregar las interfaces como la API predeterminada. Las clases base aún pueden implementar el comportamiento predeterminado.

Lo siento, entendí mal eso. En ese caso, ¿su propuesta no mantiene más o menos las cosas como están en .NET (no es que haya nada de malo en eso)?

En cuanto a las pruebas, tuve grandes problemas para probar si mi API llamaba a los métodos correctos. Para verificar si ExecuteDataReader recibió los parámetros correctos, por ejemplo, debe verificar otro método protegido que se llama internamente con diferentes parámetros. Esto está lejos de ser ideal.

Si entiendo su escenario (no estoy seguro), CallBase Moq es útil para este tipo de escenario: las implementaciones predeterminadas se heredan de la clase base

@roji

¿No es su propuesta más o menos mantener las cosas como están en .NET (no es que haya algo malo en eso)?

No exactamente. La API de la interfaz se agregó en .NET 1.0 y en desuso en 2.0. Desde 2.0, las interfaces están ahí por compatibilidad, pero no hay una interfaz para ProviderFactory u otras clases en Data.Common. Tampoco hay nada para la API async o 2.0 o métodos más nuevos.

Moq solo puede burlarse de las cosas que son burlables. Debe haber algún método que sea virtual o abstracto que pueda anular, o un método protegido al que pueda llamar. La API actual proporciona un método para algunos casos, pero no para la mayoría de ellos. Hay muchas cosas que son internas, privadas y fuera de alcance a menos que uses la reflexión. Solo MS Fakes puede hacerlo porque reemplaza la referencia con una corrección, pero esto solo está disponible en VS Enterprise y es inútil para proyectos de código abierto.

Parece que tengo un caso muy específico, pero ciertamente cualquiera que haya intentado burlarse de esta API se enfrentó a este problema. Solo en Google, casi todas las soluciones terminan con "simular la API de interfaz heredada o crear un contenedor que pueda simular":

@nvivo OK, gracias por los detalles adicionales. Admito que no he ido muy lejos con la burla de ADO.NET.

Lo que no entiendo es por qué querría burlarse de los métodos internos, privados y fuera de alcance de una API. ¿No debería burlarse de los métodos públicos que están directamente disponibles para su propio código de aplicación (que es lo que está tratando de probar)? Veo el problema con los métodos no virtuales (por ejemplo, la sobrecarga ExecuteReader () del parámetro 0), pero dado que en ADO.NET estos siempre (?) Llaman a alguna sobrecarga virtual (por ejemplo, ExecuteReader (CommandBehavior)), ¿hay un problema real aquí?

Solo tratando de comprender el escenario de su problema, ¿puede dar un ejemplo simple?

@nvivo Actualmente no tenemos ningún plan para incorporar interfaces debido al problema de control de versiones que ya han señalado varias personas en este hilo. Un buen ejemplo de interfaces que se quedan atrás es cuando se agregaron métodos asíncronos y de transmisión en .NET Framework 4.5. Cuando agregamos esas nuevas características, analizamos cuidadosamente la ampliación de las interfaces. Las opciones que teníamos en ese momento eran proporcionar InterfaceFooV2 o interfaces separadas para asyn y streaming. No queríamos agregar InterfaceFooV2 ya que podemos prever que querríamos agregar más API en el futuro. Seguir agregando interfaces separadas para cada nueva funcionalidad sería confuso, ya que no están vinculadas a las interfaces existentes.

@roji Tuve casos en los que quiero asegurarme de que se llamó a una sobrecarga específica de ExecuteReader, y no a "ninguna de las sobrecargas". Es el tipo de cosas que solo tiene en las bibliotecas, no en el código de usuario.

@YoungGah gracias por la información. Estoy cerrando esto entonces.

¿Las personas responsables de este cambio tienen alguna idea de su efecto? Las interfaces principales de ADO.NET han existido durante más de una década y el acceso a los datos es el centro de la mayoría de las aplicaciones comerciales. Tengo problemas para concebir cómo no romper intencionalmente tantas bases de código existentes no es la máxima prioridad. Estas son algunas de las interfaces de alto nivel más críticas en .NET, eliminando estas interrupciones en todas las bibliotecas de acceso a datos de ADO .NET y, en consecuencia, en todos los proyectos que las utilizan. Eliminarlos crea una fragmentación artificial, lo que genera frustración y confusión que obstaculizarán la adopción de CoreCLR.

Aún puede versionar interfaces y hacerlas compatibles con la fuente simplemente agregando métodos de extensión para cualquier API nueva con descuento de IDbCommand , por ejemplo:

public interface IDbCommand
{
    //...
}

public class DbCommand : IDbCommand
{
    void NewApi();
}

public static class DbCommandExtensions
{
    public static void NewApi(this IDbCommand cmd)
    {
        ((DbCommand)cmd).NewApi();
    }
}

La interfaz principal IDbCommand nunca tiene que cambiar después del lanzamiento de DNX y puede continuar agregando funcionalidad con la estrategia anterior. También puede más tarde (en una versión importante), enrollar estas extensiones y fusionarlas en la interfaz principal. De cualquier manera, obtenemos las interfaces ADO.NET estables centrales que son críticas para la migración de bases de código existentes y, en consecuencia, para la adopción de CoreCLR.

@Davkean me ha pedido que proporcione ejemplos concretos del impacto que tendrá la eliminación de las interfaces principales de ADO .NET. No puedo imaginar que este cambio se haya considerado sin evaluar el impacto inconmensurable que tendría en el ecosistema .NET existente, pero también se ha hecho, por lo que existe la posibilidad de que no se haya considerado, lo que voy a asumir aquí. en él no lo era.

A pesar del papel de EF de ser el ORM predeterminado de .NET y su éxito sobresaliente en la captura de una gran mayoría de la cuota de mercado, todavía hay una gran población de desarrolladores de .NET que prefieren utilizar ORM alternativos por varias razones diferentes. Por ejemplo, una característica importante en lo que respecta a CoreCLR es que tienen un soporte de primera clase que se ejecuta en Mono / Linux / OSX, además de admitir múltiples RDBMS alternativos. Dado que CoreCLR se está inclinando mucho por el mercado de desarrolladores de Linux / OSX, cuanto más soporte haya para los RDBM alternativos, mejor. Otro rasgo importante de la población de desarrolladores que adoptan Micro ORM es que han evaluado fuera de los valores predeterminados del ecosistema de MS para elegir el ORM más apropiado para ellos. Por todo lo que he visto, hay una alta correlación entre los desarrolladores de .NET OSS (es decir, anti-Dark Matter) activos y los desarrolladores que adoptan Micro ORM, del mismo modo espero que esto tenga una alta correlación con los primeros usuarios de CoreCLR, cuya principal propuesta de valor es para desarrollar en OSX / Linux. Estas son algunas de las razones por las que sería beneficioso incluir el ecosistema .NET circundante en su toma de decisiones al tomar decisiones de diseño fundamentales como esta.

Descargas de ORM alternativas

Un vistazo rápido a las descargas de NuGet proporciona una indicación de cómo se ve la participación de mercado que no pertenece a EF:

NHibernate - 1 M +
Dapper - 1M +
OrmLite - 500k +
Datos simples - 300k +
PetaPoco - ~ 100k
NPoco - 30k +

Los números reales son mucho más que esto, ya que muchos Micro ORM como Dapper, Massive, PetaPoco, NPoco, etc. fueron diseñados para caber en un solo .cs directo, por lo que NuGet no informa su uso real. También hay ORM de código cerrado como LLBLGen Pro, que tienen una gran base de usuarios, pero NuGet no informa de su uso, del mismo modo estoy seguro de que me he perdido varios otros ORM que olvidé / desconozco.

Impacto en los ORM alternativos

Gracias a GitHub podemos hacer una búsqueda rápida para ver cuántos archivos fuente diferentes contienen el núcleo
Interfaces .NET IDbConnection , IDbCommand y IDataReader ADO afectadas por este cambio:

IDbConnectionIDbCommandIDataReader
NHibernate59181132
Apuesto172117
OrmLite1795426
Simple.Data29276
NPoco4103

Nota: estos resultados solo muestran archivos fuente, el número real de referencias rotas es mucho mayor.

Impacto en el código fuente del cliente

El impacto real de este cambio también se extiende a todas las dependencias de proyectos que utilizan estos ORM.
Desafortunadamente, el efecto no se limita a las implementaciones internas, ya que también rompe al cliente.
código fuente, ya que muchos Micro ORM son solo métodos de extensión sobre interfaces ADO.NET para que el cliente
el código se parece a:

IDbConnection db = ...

//Dapper
db.Query<Dog>("select Age = <strong i="49">@Age</strong>, Id = @Id", new { Age = (int?)null, Id = guid });

//OrmLite
db.Select<Author>(q => q.Name.StartsWith("A"));

Una característica de extensibilidad del uso de métodos de extensión es que estos ORM son "abiertos" y los Clientes pueden extender los ORM con sus propias API de primera clase agregando métodos de extensión en sus propios proyectos; estos también están rotos.

Obviamente, cualquier código fuente que pase IDbConnection ahora también tiene prohibido trabajar en CoreCLR.

Por ejemplo, las interfaces ADO.NET centrales se utilizan mucho en marcos de alto nivel como ServiceStack, adoptado porque es la dependencia mínima para permitir el acceso a datos de múltiples RDBMS. También se asumió que de todas las clases que era poco probable que cambiaran, serían las interfaces principales de ADO.NET.

Resumen

Personalmente, estoy asombrado de que alguna vez haya un futuro en .NET sin estas interfaces.
Las interfaces son específicas por diseño para permitir múltiples implementaciones y ADO.NET es una
de los "modelos de proveedores abiertos" más importantes en .NET. No tengo idea de qué prioridades causaron la eliminación de estas interfaces, pero tanto las enormes bases de código .NET existentes que dependen de estas interfaces como el ecosistema alternativo de EF .NET deben tener una prioridad mucho mayor. Esto está causando una interrupción significativa y es una barrera importante necesaria para admitir las plataformas .NET 4.xy CoreCLR existentes, lo que obliga a una cantidad no trivial de complejidad adicional que debe aplicarse a todas las bases de código existentes afectadas por esto.

La percepción actual es que ADO.NET/CoreCLR se está rediseñando para proporcionar soporte de primera clase para EF y SQL Server, sin tener en cuenta el resto del ecosistema; decisiones de ruptura no transparentes como esta solo sirven para reforzar este estereotipo .

Como miembro anterior del equipo .NET (ahora trabajo en Roslyn), estuve muy involucrado en el diseño original de los nuevos datos comunes, junto con los equipos de SQL y Entity Framework. No estoy involucrado en esto en este momento, pero puedo agregar algunos antecedentes para ayudar a corregir algunas de las declaraciones que estoy viendo en Twitter y arriba.

El diseño actual de System.Data.Common para .NET Core comenzó en diciembre de 2012, aproximadamente 2 años antes de que abriéramos el código.

Metas:

  • Diseñe un área de superficie moderna para .NET Core, que redujo la duplicación de conceptos ( IDbConnection vs DbConnection ), confusiones, errores y problemas de capas (dividir SqlClient de DataCommon, dividir DataSet de abstracciones centrales) de el diseño original de .NET 1.0. Uno que sería fácilmente recogido, tanto por los consumidores existentes como por los desarrolladores _nuevos_ de .NET Framework.
  • Permita que los proveedores y consumidores creen un único binario / fuente en .NET Core y luego ejecuten ese mismo binario en .NET Framework. Tenga en cuenta que lo contrario no era un objetivo; poder tomar .NET Framework binary / source y ejecutarlo sin algunos cambios en .NET Core.

Corrección de algunas cosas que se esparcen:

  • Las interfaces tal como están no son versionables. No podemos agregar miembros a las interfaces, y la propuesta anterior proporcionada por @mythz a través de métodos de extensión requiere que los proveedores deriven de las clases base abstractas de todos modos.
  • System.Data.Common _no_ se ha alejado del modelo de proveedor. Las interfaces se eliminaron porque eran un concepto heredado de .NET 1.0 que fue reemplazado / duplicado por las clases base abstractas introducidas en .NET 2.0. En el momento en que tomamos esta decisión, todos los proveedores que pudimos encontrar derivaban de las clases base.
  • Al igual que las interfaces, las clases base se pueden burlar.
  • Entendimos que habría algunos cambios necesarios para aquellos que usan las interfaces .NET 1.0, sin embargo, es un puerto muy simple para pasar a las clases base. Por ejemplo, vea estas pocas líneas de cambio para AutoMapper: (https://github.com/AutoMapper/AutoMapper.Data/blob/master/AutoMapper.Data/DataReaderMapper.cs#L14).

Algo que me cuesta entender:

No podemos agregar miembros a las interfaces

¿Cómo no está bien agregar miembros a las interfaces CoreCLR todavía, está bien eliminarlos por completo?

la propuesta anterior proporcionada por @mythz a través de métodos de extensión requiere que los proveedores deriven de las clases base abstractas de todos modos.

La parte importante es que las interfaces existen y permiten que se compile el código fuente que hace referencia a ellas.

Si no desea versionar las interfaces, móntelas en EOL, simplemente restaure las interfaces como estaban antes de que fueran arrancadas y mitigue la carga que ahora se impone a todas las demás bibliotecas que las usan. Me refiero a que estas interfaces principales nunca quedaron obsoletas sin que se proporcionara una ruta de migración o advertencia. Sin embargo, ¿estamos siendo castigados por adoptar una API estable, bien conocida y publicada como parte integral de nuestras bibliotecas?

es un puerto muy simple para pasar a las clases base.

Esto debe agregarse en cada archivo fuente que haga referencia a las interfaces ADO.NET y obliga a los clientes a ensuciar su código con símbolos de compilación personalizados.

No parece haber el mismo cuidado por la compatibilidad con versiones anteriores, pero romper intencionalmente los clientes existentes en una versión futura simplemente no es una opción (me sorprende que incluso se considere con la participación de mercado mucho mayor de ADO .NET). No podemos romper con los clientes 4.x existentes y, sin embargo, se nos pide que admitamos CoreCLR. Entonces, ¿dónde deja esto a las bibliotecas 4.x existentes que desean mantener la compatibilidad con versiones anteriores y también admitir CoreCLR? ¿Deberíamos duplicar también documentos / ejemplos?

¿Cómo no está bien agregar miembros a las interfaces CoreCLR todavía, está bien eliminarlos por completo?

El área de superficie en .NET Core debe ser compatible de forma binaria con .NET Framework para permitir que las primeras y las terceras partes compilen contra .NET Core y ejecuten la portabilidad sin cambios en .NET Framework. Agregar miembros a las interfaces viola eso, ya que los consumidores de esos miembros fallarían cuando se ejecutaran en .NET Framework.

No estoy abogando por la eliminación o adición de estas interfaces, solo quería agregar algunos antecedentes de por qué el diseño terminó donde está. Dejaré que los propietarios actuales, incluidos @ saurabh500, aborden eso.

Solo para resumir el hilo, la razón por la que cree que Microsoft debería portar estas interfaces es para permitir que el ecosistema se transfiera fácilmente a .NET Core, mientras mantiene sus implementaciones de .NET Framework.

es permitir que el ecosistema se traslade fácilmente a .NET Core, mientras se mantienen sus implementaciones de .NET Framework?

Si.

Solo para resumir el hilo, la razón por la que cree que Microsoft debería portar estas interfaces es para permitir que el ecosistema se transfiera fácilmente a .NET Core, mientras mantiene sus implementaciones de .NET Framework.

Si. Las API externas ahora se rompen si transfiero mi base de código (LLBLGen Pro) a corefx: luego tengo que exponer 2 apis o romper la base de código existente para todos mis usuarios.

Puede que esté bien para ustedes romper nuestras cosas, ya que no sienten el dolor, nosotros sí. No está bien para mí: tengo que vivir con una base de código destruida y mantener 2 API que hacen lo mismo, O romper el código de mis usuarios porque pensaste que estaba bien.

Tampoco entiendo por qué las interfaces no se versionan, es solo una interfaz, como si una clase también tuviera una interfaz. CoreFX puede agregar perfectamente los métodos asíncronos a las interfaces.

El área de superficie en .NET Core debe ser compatible de forma binaria con .NET Framework para permitir que las primeras y las terceras partes compilen contra .NET Core y ejecuten la portabilidad sin cambios en .NET Framework. Agregar miembros a las interfaces viola eso, ya que los consumidores de esos miembros fallarían cuando se ejecutaran en .NET Framework.

Solución sencilla: agregue las interfaces tal como están ahora. Y una vez que todos se den cuenta de que esta regla anterior es en realidad bastante estúpida, puede agregar los métodos que necesitaba agregar a las interfaces hace mucho tiempo a las interfaces y seguir adelante.

Trabajo con software de MS el tiempo suficiente para que las reglas como la anterior sean excelentes en el papel, pero en la práctica se rompen en el momento en que un equipo importante de MS necesita que se rompan. Si eres tan 'abierto' y 'diferente' como dices en el alboroto de marketing / publicidad de CoreFX, demuéstralo. Todo lo que veo con respecto a System.Data y CoreFX es 'lo que MS necesita está hecho, lo que todos los demás necesitan está en segundo plano o ignorado'.

Otra cosa que olvidé mencionar: Fowler mencionó ayer en Twitter que necesitas que todos porten sus cosas. Tengo que pagar por migrar mi base de código 500K LoC a CoreFX yo mismo, lleva tiempo, esfuerzo y me quitará tiempo para otras funciones. La fricción adicional que es totalmente artificial (¡es una nueva plataforma! ¿Cómo pueden haber reglas restrictivas?) Realmente no ayuda en absoluto: agrega costos de mantenimiento adicionales, toma tiempo adicional para portar el código y la prueba y brinda una carga adicional para nuestros usuarios.

Todo eso está fuera de su alcance y, al parecer, no es de su incumbencia. Pero olvidas una cosa: ¿qué _si_ no portamos nuestro código y conmigo más gente? Estoy dispuesto a invertir tiempo y, por lo tanto, mi propio dinero para trasladar mi gran base de código a su nuevo marco brillante, pero lamento decirlo, siempre que me encuentro con un problema me encuentro con restricciones, reglas extrañas y debates interminables que terminan en silencio. . Iow: Me siento muy solo mientras, al mismo tiempo, parece que quieres desesperadamente que nos guste tu nuevo y brillante marco.

Como dije hace mucho tiempo : Véndeme este framework, este nuevo CoreFX. Bueno, mantener la fricción e introducir mucho queso movido y quitado no crea un gran incentivo para invertir una gran cantidad de tiempo (y dinero) en esto.

Solo mis 2 centavos.

@FransBouma Intentemos mantener esta conversación profesional, productiva y centrada en los hechos.

No estoy discutiendo a favor o en contra de agregar las interfaces. Sin embargo, no es compatible agregar métodos a las interfaces. Repasemos esto:

1) Agregue IDbConnection.OpenAsync a .NET Core
2) Cualquiera que llame a este método, ahora no podrá ejecutarse en .NET Framework (rompiendo un principio / objetivo central que mencioné anteriormente). Esto también rompe el diseñador XAML y algunas otras características de VS que se basan en este mismo hecho.
3) Para actualizar .NET Framework, enviamos una nueva versión de .NET Framework "4.7" con IDbConnection.OpenAsync
4) Todos los tipos que implementaron IDbConnection antes de agregar este método ahora no se cargan en .NET Framework "4.7"

Es por eso que no podemos agregar métodos a las interfaces.

Si mantengo mi frustración por cómo van las cosas con respecto a la comunicación de problemas con la EM para mí, no todos lo sabrán y pensarán que todo son rosas y arcoíris. Si eso parece poco profesional, que así sea, no me importa si MS piensa que soy un profesional o no.

Dicho esto: no estoy casado con las interfaces, así que si se van, el hecho de que a partir de entonces haya clases y no haya interfaces con las que trabajar en teoría no me convertirá en un panda triste: lo que se debe hacer puede en teoría a través de las clases base también, hoy, como en la actualidad, todos los principales proveedores de ADO.NET juegan bien y derivan de las clases base (este no ha sido el caso en el pasado IIRC con ODP.NET implementando una interfaz pero no derivado de la clase base). Esta es también la razón por la que inicialmente no pensé que eliminarlos fuera un gran problema. Desde entonces tuve algo de tiempo para pensar en ello y creo que es un gran problema.

No vivimos en el vacío en Marte, y los middleware / frameworks en la parte inferior de la pila tienen un problema ahora: los usuarios de las versiones completas de .NET actuales de estos frameworks quieren seguir usándolos en CoreFX ya que conocen estos frameworks. Sin embargo, trasladarlos a CoreFX es un gran PITA, debido a una gran cantidad de razones, una de ellas es que las interfaces de uso frecuente expuestas en API públicas no están presentes en CoreFX (y la razón de este hilo).

Solo por esa razón me gustaría volver a ver las interfaces. Para mí, personalmente, no por razones técnicas (por ejemplo, async necesita clases base, ya es un desastre). Sé que carecen de ciertos métodos, pero ese es tu problema, no el mío. Quitarlos hace que mi problema y (parafraseando ahora) la respuesta de MS a eso es: levanta las manos con "¡no se puede hacer!". Pero no tengo ese lujo. Tú creaste este lío, lo resuelves. Quieres que transfiera mi código, que invierta mucho tiempo y dinero (que tengo que pagar por mí mismo) para respaldar tu nuevo marco, ¿por qué estás haciendo que _su_ problema sea _mi_ problema?

Mirando su escenario de 4 pasos: agregar métodos a las interfaces no es un problema SI ve CoreFX como un marco separado. ¿Y no es ese el caso de todos modos? Es lo mismo que con Compact Framework hace todos esos años (al que sí porté mi framework, y aprendí un par de lecciones difíciles que me dicen que portar a CoreFX no será simple, rápido y fácil y mantener dos bases de código tampoco lo hará): comenzamos con 1 API, luego alguien olvidó algo o algún equipo dentro de MS necesita algo, y viola un cambio radical con el que solo se encontrarán un puñado de desarrolladores de pila de bajo nivel y así sucesivamente y los dos caminos se dividirán.

(ejemplo: Compact Framework olvidó 'SerializableAttribute'. Agregaron eso con un atributo ficticio sin hacer nada en una versión posterior, pero ese código roto que anticipó que no estaba presente y que definió el suyo propio)

Sin embargo, dividir las carreteras es comprensible: tratar de mantener las cosas compatibles es demasiado restrictivo. Predigo aquí ahora que esta regla se romperá en el futuro.

Ver las cosas como 'compatibles' es importante no solo en el nivel de la firma de la API, sino también en el nivel de _comportamiento_ de la API. Confiar en que esos dos serán completamente iguales (CoreFX y .NET completo) en el comportamiento de la API es demasiado arriesgado: un desarrollador de framework tendrá que probar la misma funcionalidad en CoreFX y en .NET completo, no hay forma de que las pruebas solo en CoreFX sean suficiente para asumir que el código funciona 100% igual en .NET completo en el futuro: porque ¿cómo puede garantizar eso? Una pila de llamadas de 20 llamadas profundas en CoreFX ha tocado mucho más código además de .NET completo, un pequeño detalle aquí y allá y las cosas cambian.

El punto en todo esto es: es un marco separado: se puede esperar que el código compilado con CoreFX sea diferente del código compilado con .NET completo.

Hay un par de situaciones:

1) un marco tiene una base de código del cual se compila al 100% en CoreFX. Esto da una dll que se puede ejecutar en .NET completo
2) un marco tiene una base de código del cual el 70% se compila en CoreFX y el 100% en .NET completo. Esto da 2 dlls: uno para CoreFX y otro para .NET completo. Es una tontería ejecutar la versión CoreFX en .NET completo, ya que se pierde el 30% de la funcionalidad.

En el caso de 1) Entiendo su punto. En el caso de 2) (que es el caso de todos los marcos de orientación completos de .NET actuales, entre ellos _todos_ los ORM de terceros) su punto no tiene sentido, ya que tendrán que trabajar con 2 dlls de todos modos: efectivamente 2 bases de código que tienen que mantenerse por separado, probarse por separado y migrar a sus propias versiones nuevas por separado. Especialmente si CoreFX obtiene nuevas características que aún no forman parte de .NET full (que será el caso). (Por cierto: si agrega DbDataReader.GetSchemaTable () a CoreFX, que devuelve una estructura de datos diferente a un DataTable, porque MS se niega a transferir eso, el código que usa DbDataReader.GetSchemaTable en CoreFX también se romperá en .NET por completo. Si lo nombra de manera diferente se romperá y el método no está allí. Iow: el código se romperá si se utilizan cosas que no están en _ambos_ marcos. Eso no significa que las cosas no deberían estar presentes en CoreFX).

No tener interfaces en CoreFX hace que la situación del marco en la situación 2) sea persistente: no pueden moverse para convertirse en un marco que encaje en 1) porque, por ejemplo, su API expone las interfaces.

Microsoft reescribe sus propias cosas para que sus marcos se conviertan en marcos en la situación 1) es genial, sin embargo, no tenemos un presupuesto de un millón de dólares, más de 15 personas en el tiempo de ejecución de ORM y una gran máquina de relaciones públicas de nuestro lado que suavizará las arrugas de rompiendo todas las aplicaciones que existen. Así que estamos atrapados en 2) o necesitamos un poco de ayuda de MS para pasar a 1).

Eso es lo que está en juego aquí. Dijiste en twitter "dinos lo que necesitas". Lo hicimos. Repetidamente. Especialmente con respecto a System.Data no hay comunicación. Nada. No hay planes futuros, no hay discusión sobre qué hacer, solo callejones sin salida y, a veces, si una persona con EM interviene, es alguien que no tiene ningún interés real en el asunto. Aprecio su tiempo en esto, cuanto más antecedentes tengamos, mejor, pero al mismo tiempo, es como hablar con un compañero de trabajo sobre esto: no se resolverá porque la (s) persona (s) a cargo están ausentes y no participando en la discusión.

Si eso me hace parecer frustrado y Dios no lo quiera "poco profesional", que así sea.

Gracias por su atención. Por cierto, no me hago ilusiones con System. cambiará. No es tu culpa, @davkean , no es nada personal.

Tengo que hacerme eco de las frustraciones anteriores sobre la falta de comunicación. También necesitamos inserciones masivas e información de esquema. No ha habido ningún avance o comunicación en más de un mes (consulte dotnet / runtime # 15269 y dotnet / runtime # 14302) de estas funciones centrales faltantes (en ambos sentidos). Sin embargo, Microsoft está etiquetando el código actual como "un candidato para su lanzamiento", que en sí mismo es un mensaje de "es suficientemente bueno". No es. Faltan cosas principales que deben agregarse y, si sigue estos hilos, debe estar en la primera versión por razones de control de versiones similares.

Mire la última actualización en dotnet / runtime # 14302 ("¿Por qué DataTable / View / Set Absent?"), Es de hace 22 días preguntando:

Entonces, ¿System.Data.Common ya está completa para la V1 de CoreCLR?

Sí, la frustración puede parecer poco profesional. El tono y el contexto del texto apesta y siempre lo ha hecho, pero eso es a lo que estamos restringidos aquí. Creo que todos están tratando de ser productivos aquí, pero estamos teniendo un poco de obstáculos por parte de CoreFX sobre el progreso real en el área System.Data y, para ser franco, enfurece a ambos como autor de la biblioteca. y un usuario de estos bits.

Necesitamos estas piezas funcionales centrales, interfaces o no; no estoy muy familiarizado con las interfaces y hemos portado Dapper sin ellas. Pero la falta de DataTable, información de esquema de resultado, inserción masiva y demás son inaceptables en un "candidato de lanzamiento". Microsoft es quien aumenta la frustración al etiquetar el código actual como RC cuando se acepta casi universalmente que no está listo para su lanzamiento. Sí, es solo una etiqueta, pero es una etiqueta incorrecta y una que aumenta drásticamente el nivel de urgencia porque se basa en un horario arbitrario (que debería haber cambiado para reflejar la realidad). No creo que nadie en este hilo sea responsable de ese horario, pero vale la pena señalarlo como un factor importante en el _nivel_ de frustración.

Volvamos a la raíz del problema. Necesitamos estas piezas, y muchos de nuestros millones de usuarios las necesitan. Así que vamos a arreglarlo.

No olvidemos NHibernate con más de 1 millón de descargas :

| IDbConnection | IDbCommand | IDataReader |
| --- | --- | --- |
| 59 | 181 | 132 |

La percepción actual es que ADO.NET/CoreCLR se está rediseñando para proporcionar soporte de primera clase para EF y SQL Server, sin tener en cuenta el resto del ecosistema; decisiones de ruptura no transparentes como esta solo sirven para reforzar este estereotipo .

Esa percepción se ve reforzada por cosas como esta: https://github.com/dotnet/corefx/issues/4646

Por lo que puedo decir, no hay forma de implementar esa API de ninguna manera útil fuera del ensamblaje SqlClient.

Actualmente estoy de acuerdo con las pruebas sin interfaces. Pero, sinceramente, no entiendo el razonamiento sobre el control de versiones y la compatibilidad de la interfaz.

¿No es que la idea de .NET Core es que es un nuevo marco sin la carga de la compatibilidad y que está incluido con su aplicación, por lo que no tiene que lidiar con problemas como ese? El proveedor ya es incompatible con los de .NET debido a la falta de elementos como los esquemas y las tablas de datos, entonces, ¿con qué rompería la compatibilidad? Si la interfaz cambia, compílela con la nueva versión y agréguela a su aplicación.

Parece que la mayoría de las excusas para el diseño son preocupaciones del antiguo marco que no son aplicables al nuevo. De todos modos, veamos cómo resulta en la práctica.

Para aquellos de uso que pretenden admitir múltiples marcos, y que históricamente se han dirigido a las interfaces ... solo quiero compartir un montón de cosas feas que usa Dapper; No estoy diciendo que esto sea _bueno_, pero es suficiente para compilarlo. Por supuesto, está duplicado en una enorme pila de archivos ... Estoy compartiendo esto principalmente para enfatizar otro de los impactos:

https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper/SqlMapper.cs#L6 -L16

No podemos agregar miembros a las interfaces

Correcto, y esa es una característica _buena_ de las interfaces. La preferencia por las clases base abstractas es la forma más segura de ayudar a la entropía de la API, en lugar de luchar contra ella.

Si bien no _ tiene_ que seguir los principios de OOD , le sugiero que lo haga cuando cree API de OO. En resumen, el Principio de Segregación de Interfaces (ISP) establece que _ ningún cliente debe verse obligado a depender de métodos que no utiliza_.

Si agrega nuevos métodos a una abstracción existente, automáticamente infringe el ISP.

Puede decidir que 'no tiene que adherirse a SOLID' porque es Microsoft y está trabajando con BCL, por lo tanto, 'las reglas normales no se aplican' (no comillas reales; simplemente parafraseando lo normal contra argumentos).

Habiendo mantenido un par de proyectos de código abierto durante 6-7 años, en mi experiencia es mejor mantener las interfaces pequeñas. Si necesita agregar nuevas capacidades a una abstracción, introduzca una nueva interfaz.

Si hubiera votos a favor aquí, habría votado a favor del comentario de

Comentario perspicaz de @ploeh como de costumbre.

@FransBouma ,
@NickCraver , SqlBulkCopy está en nuestro plan futuro y estamos trabajando en el esquema. Somos lentos en el progreso del esquema, ya que también necesitamos hacer un progreso razonable para hacer que nuestra pila funcione en x-platform.
@mythz , gracias por proporcionar ejemplos, números y evaluación sobre el impacto en el cliente. Los revisaremos.

@YoungGah Actualice los problemas relacionados con la información para que estos problemas se mantengan actualizados, por ejemplo, https://github.com/dotnet/corefx/issues/1039 , ya que, de lo contrario, la información escasa está dispersa por todas partes. Es bueno que agregue GetSchemaTable (y el equivalente de DbConnection, ¡no olvide ese!), Pero es muy difícil obtener información sobre lo que sucederá y _cuando_. ¿Hay algún plan de qué se agregará cuando? Todo lo que tenemos para continuar ahora es una pista de que en 'el futuro' se podría agregar algo. Para ser honesto, eso no es realmente bueno para planificar un puerto de una base de código.

@FransBouma , sí, actualizaré los otros hilos también. En cuanto a su solicitud de más información sobre qué y cuándo estará disponible, entiendo completamente por qué lo necesitan. Publicaré la lista que indicará si la característica / funcionalidad estará disponible en v1, si se eliminó intencionalmente, estará disponible después de v1 o si el diseño de la disponibilidad futura está pendiente. Intentaré publicarlo durante las próximas 2 semanas. En cuanto a la función get schema en DbDataReader y la función DbConnection, nuestro plan es hacerla disponible para la versión rc2. Si el plan cambia por razones imprevisibles, actualizaremos la comunidad.

Lo que pase aquí, y para futuras referencias @YoungGah; IDataReader tiene una dependencia de DataTable, que tiene una dependencia de DataSet (que consideramos una capa separada, porque tiene muchas políticas, a diferencia de estos tipos que no tienen políticas), por lo que hay un trabajo de diseño aquí para romper eso si estas interfaces alguna vez fueran traído de vuelta.

Colocaría otro voto aquí para el @ploeh ; tienen interfaces, pero mucho, mucho más detalladas que la mayoría de las interfaces actualmente en BCL. Se trata tanto de los comentarios de @davkean sobre el desacoplamiento como de abordar el control de versiones.

¿Por qué no puede simplemente tener una nueva interfaz heredando la anterior? Obsoleto el antiguo. Quite el viejo en el futuro. Al menos entonces puedes extenderlo y no romper los usos existentes.

O múltiples interfaces más pequeñas. Acabo de llegar al comentario de ploehs.

No entiendo esta necesidad de tener una compatibilidad perfecta con el .NET original. No hay nada que romper aquí, este es un nuevo marco y la oportunidad perfecta para romper los lazos con el código heredado y aplicar los cambios que son necesarios durante mucho tiempo pero que perjudicarían al marco original.

Cuando propuse las interfaces, no estaba pensando en traer las interfaces 1.1 originales, sino interfaces actualizadas con el nuevo diseño. Incluso podría haber más, como dijo @ploeh .

Interfaces sí, soporte heredado si es posible, pero no debería ser una prioridad en este momento.

Reapertura ya que hay mucho interés en este tema.

No hay nada que romper aquí, este es un nuevo marco y la oportunidad perfecta para romper los lazos con el código heredado y aplicar los cambios que son necesarios durante mucho tiempo pero que perjudicarían al marco original.

Entonces, ¿la oportunidad perfecta para deshacerse de las interfaces originales mal diseñadas y estandarizar en clases base como ADO.NET ya lo está haciendo? :Cara de burla:

Sin embargo, en serio, puede elegir entre una API limpia o compatibilidad con versiones anteriores. Elegir uno. No veo la manera de tener tu pastel y comérmelo también.

@JamesNK pero ese era exactamente el punto. No se requiere compatibilidad con versiones anteriores, punto.

Bromeas, pero la API mal diseñada con interfaces era mala porque estaban mal diseñadas, no porque fueran interfaces. =) No es que las interfaces no se usen absolutamente en todas partes en NET o esto es algo nuevo. Solo diseña la cosa correctamente esta vez y sigue adelante.

ADO.NET es una de las piezas de código más estables de todo .NET. Tuvo dos o tres cambios serios en 15 años. Es la API perfecta para estabilizar una interfaz y simplificarla para todos.

Como nota, este tema aquí también es uno de los más comentados, y tuvo la misma discusión larga sobre las interfaces frente a los métodos virtuales, la capacidad de prueba y demás.

@nvivo Debo admitir que estoy confundido. Una vez que establecimos que las clases base permitían la capacidad de prueba, este hilo se transformó para recuperar las interfaces para permitir la migración del código de .NET Framework a .NET Core. ¿Cómo ayuda eso rediseñar las interfaces e introducir algo nuevo?

¿Por qué no podemos tener interfaces originales para la compatibilidad con versiones anteriores y seguir adelante con lo que elija (ya sea clases abstractas o interfaces pequeñas)? Las interfaces originales podrían ubicarse en la parte superior de la nueva pila y proporcionar compatibilidad con versiones anteriores. Esta parte también podría ser opcional.
Eso facilitaría la migración y aún permitiría una nueva forma.

@davkean

No puedo responder por todos los que comentaron aquí. Propuse usar interfaces como API de ADO.NET, actualizado a la nueva API actual. No pedí traer las interfaces originales y todos los problemas que tenía. El propósito era tener una API definida más limpia y hacer que fuera más fácil simularlo y probar el código que depende de él, principalmente bibliotecas de abstracción de datos y no código de usuario.

Las interfaces son mejores para describir las API como dijo sabiamente @ploeh , y mucho más fáciles de burlar. El diseño actual es terrible para burlarse y requiere que implemente casi todo el proveedor como una biblioteca para hacer pruebas simples. Si eso no es importante para todos, puedo entenderlo. Pero definitivamente no estoy de acuerdo en que sea lo suficientemente comprobable hoy. Probar si se llamó a un método A con el parámetro X comprobando si el método B llamado C con los parámetros X, Y, Z no es ideal.

Ahora, solo para ver cómo las clases ya están creando un mal diseño:

  • ¿Por qué DbCommand tiene una propiedad DesignTimeVisible? ¿Es el soporte del tiempo de diseño un requisito para que una conexión se defina como una conexión?
  • ¿Por qué hay un evento para notificar cambios de estado pero no notificar otras cosas como comandos ejecutados o transacciones iniciadas? ¿La notificación es incluso un requisito para que existan conexiones o algo que facilite la creación de interfaces de usuario?
  • ¿Es un ConnectionStringBuilder un requisito para que exista un proveedor? ¿O algo más agradable para que los asistentes de VS funcionen de inmediato?
  • ¿Por qué DbDataReader define Get métodos para algunos tipos principales pero no agrega para otros como GetByteArray() y GetDateTimeOffset() ? ¿Y es incluso necesario recuperar un TextReader si esto se puede hacer afuera usando cadenas o secuencias? Si se tratara de una interfaz, ¿se agregarían métodos como estos a la API o se crearían como métodos de extensión o ayudantes en las clases concretas (como la familia GetSql* en SqlDataReader)?

Todas estas son preguntas retóricas. Estoy seguro de que todos tienen respuestas convincentes y se han considerado algunas cosas. El punto es que el diseño actual claramente no es algo que se haya pensado como una definición de API , algo que probablemente recibiría más atención con las interfaces.

Honestamente, desde el exterior, la discusión sobre el diseño parece que fue como:

  • mantengamos estos eventos aquí porque es más fácil para los asistentes de Visual Studio trabajar desde el primer momento.
  • eliminemos los métodos de recuperación de esquemas porque tenemos EF y eso es lo que todo el mundo debería usar, punto.
  • mantengamos estos métodos de conveniencia porque es compatible desde .NET 1.1 y ¡NUNCA podemos romper la compatibilidad!
  • ¡Eliminemos las tablas de datos y los conjuntos de datos y hagamos que todos los que vienen de .NET 1.1 cambien toda su base de código de todos modos!
  • construyamos un nuevo modelo de proveedor centrado en la computación en la nube, la comunidad, el código abierto y las aplicaciones del mañana ...
  • ... ¡y construyamos este modelo basándonos en las necesidades de ayer usando SqlClient como caso de prueba _sole_!
  • ¡Construyamos un marco completamente nuevo que se incluirá con cada aplicación, para que nadie tenga que preocuparse de que las actualizaciones rompan su aplicación nunca más!
  • .. ¡Entonces tomemos la decisión de no agregar interfaces porque cualquier cambio puede romper sus actualizaciones!

Sí, hay una pequeña perorata y esta discusión no va a ninguna parte =) ... Pero solo quería sacar esto de mi pecho. Es 2015, todo se rompe todo el tiempo y estamos acostumbrados. Habrá 20 actualizaciones de ASP.NET MVC en los próximos años que provocarán muchos más cambios importantes que los cambios de interfaz en ADO.NET.

Todavía amo .NET y lo que está haciendo con él en general, estoy seguro de que es una prisa sacar .NET Core v1 a tiempo y no todo será perfecto. Solo espero que la comunidad pueda ayudar a dirigir esto hacia otras direcciones a medida que pasa el tiempo y que no tengas miedo de romper las cosas a medida que avanzamos.

Para los mantenedores de ORM, ¿por qué no

`` c #

si COREFX

espacio de nombres System.Data {
interfaz pública IDbConnection {...}
}
''

y usa el patrón de adaptador para envolver el nuevo System.Data con sus propias implementaciones? De hecho, podría crear un paquete de código fuente abierto y compartirlo.

It's 2015, everything breaks all the time and we're used to it. There will be 20 updates to ASP.NET MVC in the next years that will cause a lot more breaking changes than interface changes in ADO.NET.

I still love .NET and what you're doing with it in general, I'm sure it's a rush to get .NET Core v1 out in time and not everything will be perfect. I just hope the community can help steer this to other directions as the time goes and you're not afraid to break things as we move.
- nvivo

Este es el problema; en lugar de un enfoque considerado, estamos obteniendo una reescritura apresurada para cumplir con una fecha límite arbitraria.
Preferiría que fuera tarde, Q4 / 16 si es necesario, que tener algo de mierda nueva y brillante. Es 2015 y todo se rompe es una terrible justificación.

@thefringeninja que agrega una dependencia completamente innecesaria y confusa que solo funciona con la mitad de los sistemas (en el caso compartido), o conduce a colisiones de nombres que requieren extern alias para desempaquetar (y mucha confusión por qué el método que toma a System.Data.IDbConnection no aceptará los System.Data.IDbConnection diferentes pero iguales que le está ofreciendo). Básicamente, eso empeoraría las cosas 10 veces.

¿Puede dar un ejemplo concreto @mgravell ? Puedo ver cómo se rompería esto si usara Type.GetType("System.Data.IDbConnection, System.Data") , o tal vez en escenarios PCL.

Si orm A define System.Data.IDbConnection, y orm B define
System.Data.IDbConnection, entonces ahora hay dos completamente diferentes y
interfaces incompatibles que tienen el mismo nombre / espacio de nombres, conflicto y
en realidad, no funciona con ninguno de los proveedores de bases de datos. No resuelve nada
básicamente. Peor aún: alquila API inutilizables donde alguien espera pasar
en SqlConnection o NgpSqlConnection, y no funciona.

De hecho, podría crear un paquete de código fuente abierto y compartirlo.

Si no es System.Data.Common, entonces eso significa que DbConnection no lo implementa y: es mejor que no te molestes.

Nunca obtendría un consenso para que todos los mantenedores de proveedores de ORM o ADO .NET asuman una dependencia externa de terceros (prácticamente garantiza que SQL Server no lo hará) y 2 bibliotecas que redefinen una interfaz BCL central no se pueden usar juntas. Peor aún para los marcos de alto nivel (como ServiceStack) que hacen referencia a las interfaces System.Data en las bibliotecas centrales (que ahora necesitaría definir) ya no podrán usar ningún ORM que no haga referencia a las mismas interfaces, lo cual no -uno lo haría o debería.

La única forma en que puede asegurarse de que cada biblioteca haga referencia a las mismas interfaces System.Data es si se restauraron con las clases base implementándolas, lo que todavía no tengo claro qué daño tiene esto.

@mgravell ah dependencia transitiva, no lo había considerado. : +1:

Sabes que no veo por qué esto es un problema a menos que estés acoplando estrechamente tu código con un código que no te pertenece. ¡Proteja su código de sus dependencias! Envuélvalo y abstraiga. Hay muchas formas de hacer esto y hacer que su código sea comprobable. Muchos se mencionan anteriormente. La integración prueba los bits que no le pertenecen. Así es como funciona. ¡No deberías burlarte de los objetos BCL! Si es así, su diseño no es bueno.

@nvivo Entiendo que este es su problema original, pero la dirección ahora se ha convertido en un hilo sobre la recuperación de las interfaces de la era v1 por razones de compatibilidad. Mantengámonos enfocados en eso: si desea discutir cómo hacer cambios en el área de superficie actual, presente nuevos problemas.

@mythz Hay dos problemas que tuvimos con las interfaces; 1) trajeron una dependencia (fuerte) de DataSet, que no pertenece a las abstracciones libres de políticas y 2) traen un conjunto paralelo de abstracciones a las clases base pero están bloqueadas con el área de superficie v1. Queríamos evitar esa confusión.

Estoy de acuerdo en que no es viable que un tercero proporcione estas interfaces; deben implementarse en las abstracciones centrales para que sean útiles.

Entiendo que este es su problema original, pero la dirección ahora se ha convertido en un hilo sobre la recuperación de las interfaces de la era v1 por razones de compatibilidad. Mantengámonos enfocados en eso: si desea discutir cómo hacer cambios en el área de superficie actual, presente nuevos problemas.

Esto no tiene ningún sentido.

@nvivo significa que no eres tú el que tiene el problema a pesar de haberlo archivado, evidente al haberlo cerrado. El problema es restaurar las interfaces System.Data para aliviar la carga de soportar todo el ecosistema que depende de ellas. Parece que estás de acuerdo con:

Es 2015, todo se rompe todo el tiempo y estamos acostumbrados.

Pero esta no es una estrategia satisfactoria para aquellos de nosotros que tenemos que admitir bases de código existentes y las bases de código de nuestro Cliente, y definitivamente no debería ser la estrategia predeterminada para los custodios de bibliotecas BCL que afectan a todo .NET.

Pero esta no es una estrategia satisfactoria para aquellos de nosotros que tenemos que admitir bases de código existentes

@mythz Esto está fuera de contexto. Eso no es lo que quise decir. Todo el mundo aquí tiene que admitir bases de código existentes, dudo que haya algún recién llegado a .NET en la discusión.

El problema en lo que se convirtió esta conversación es que no tiene mucho sentido. .NET Core es un nuevo marco, no una actualización. Gran parte de la API .NET completa existente no existe y no lo estará. La compatibilidad con versiones anteriores no funcionará así de todos modos.

@nvivo Este sentimiento exacto es el motivo por el que este problema no se aplica a usted. Si cree que la compatibilidad con versiones anteriores no es importante, nunca ha intentado admitir una base de código significativa dirigida a múltiples plataformas; tampoco está hablando en nombre del equipo de CoreCLR. Estas bibliotecas no se desarrollaron desde cero, si lee arriba, encontrará que es un objetivo principal que las bibliotecas CoreCLR se ejecuten en el .NET Framework completo. CoreCLR es otro objetivo de la plataforma para el cual la portabilidad de las bibliotecas existentes es vital para su éxito, algo que el equipo de .NET está fomentando activamente y que estas interfaces faltantes están obstaculizando actualmente.

Con toda esta charla sobre las interfaces que no son compatibles con la versión, me hace pensar en cómo el lenguaje de programación Go evita este problema con las interfaces implícitas.

Se me pidió que ampliara lo que quería decir con abstracciones _sin políticas_.

Básicamente, por libre de políticas me refiero a que las abstracciones System.Data.Common contienen casi cero reglas comerciales; todo lo que hacen es proporcionar una forma de API que un proveedor determinado debe implementar. Esto contrasta con DataSet, que tiene muchas reglas y códigos comerciales. Los tipos sin políticas, debido a su propia construcción, tienden a realizar versiones con menos frecuencia que los tipos que contienen políticas, ya que hay menos código y, por lo tanto, menos errores y cambios de diseño. Queremos abstracciones y tipos que se _intercambian_ [1] entre bibliotecas de terceros a versiones con poca frecuencia [2] para reducir la cantidad de problemas que se encuentran al coordinar dependencias en un gráfico de paquete grande. No puede incrustar o fusionar tipos de intercambio como parte de su aplicación, mientras que puede hacerlo con los tipos que no son de intercambio. Por lo tanto, por qué queremos que se dividan.

[1] Por _exchanged_, me refiero a tipos que tienden a aparecer en API públicas. Por ejemplo, consideramos Collection<T> un tipo de intercambio, pero no List<T> .
[2] El uso de versiones semánticas para estos ocasionaría rupturas casi idénticas a las anteriores con las interfaces eliminadas, porque "bifurca" el ecosistema; las bibliotecas deben tomar una decisión si apuntar a la biblioteca antes o después de la pausa, o bifurcarse para manejar la división.

@mythz Tengo que admitir orms / clientes como parte de una aplicación comercial y un código existente ... No estoy diciendo que esté de acuerdo con ninguna de las partes, pero el hecho es que dnx es un framework / runtime completamente nuevo. Si no tiene algunas interfaces, trátelo ... con una directiva de compilador.

Si no tiene algunas interfaces, trátelo ... con una directiva de compilador.

¿Oh? ¿Qué tal un método que devuelve IDataReader o un método que acepta IDbConnection? ¿O IDbTransaction? ¿Cómo vas a #ifdev alrededor de eso, si tu código _customers_ usa esa API?

'Lidiar con eso' ... ¿qué tipo de arrogancia es esa?

Simple, actualice el paquete subyacente (su organización) para devolver su propio tipo o el tipo base que han admitido desde 2.0. Si está apuntando a versiones anteriores de .net, puede usar una directiva para devolver la interfaz y marcarla como obsoleta.

Sí, apesta, pero estoy seguro de que ellos (personas realmente inteligentes que piensan en esto todo el tiempo) lo hicieron por una buena razón en ese momento (en .net 2.0). ¿Puede ocurrir una charla y tal vez esto se cambie, seguro? Pero el hecho es que las personas que se actualicen al nuevo tiempo de ejecución tendrán que trabajar un poco. No es hacer clic en un botón en una interfaz de usuario elegante y tener que trabajar mucho para ellos. Pero al mismo tiempo, estoy de acuerdo con usted en que actualizar al nuevo marco debería ser más fácil que reemplazar un montón de tipos.

@FransBouma es probablemente alguien que también defiende los nombres fuertes.

@FransBouma @ phillip-haydon puede llevar tu troleo a otros lugares, no puedes esperar actuar así y que la gente te tome en serio. Si tiene alguna duda, eche un vistazo a mis contribuciones / proyectos de código abierto involucrados ... Tendré que lidiar con esto ... de cualquier manera ...

Para que conste, estoy en contra de los nombres fuertes.

tratar con él...

¿Y dices que estoy trolling?

" @FransBouma es probablemente alguien que también defiende los nombres fuertes". está fuera de tema y no es muy útil para esta charla. Sí, se siente como si estuvieras trolling ...

Un hecho importante a considerar en esta discusión es que las interfaces IDb * fueron obsoletas como API para ADO.NET en .NET 2.0 hace una década cuando se introdujeron las clases base. No se marcaron en desuso, pero cualquier cosa construida desde ese momento depende de las clases base. Me vienen a la mente los proveedores de App.config y el soporte de cadenas de conexión.

Si tiene código que depende de esas interfaces, está codificando con una API muy obsoleta sin soporte para cosas como métodos asíncronos, lo que significa que deberá actualizarlo de todos modos si desea que la gente siga usándolo.

Simple, actualice el paquete subyacente (su organización) para devolver su propio tipo o el tipo base que han admitido desde 2.0. Si está apuntando a versiones anteriores de .net, puede usar una directiva para devolver la interfaz y marcarla como obsoleta.

Es una API pública, utilizada por muchos miles de aplicaciones y la API ha sido pública y en uso desde .net 1.0. No es 'simple', al contrario. No podemos simplemente cambiar la API porque Microsoft cree que eso es lo que tienen que hacer para mejorar nuestras vidas: será una gran carga para nuestros usuarios y, por lo tanto, para nosotros.

Sí, apesta, pero estoy seguro de que ellos (personas realmente inteligentes que piensan en esto todo el tiempo) lo hicieron por una buena razón en ese momento (en .net 2.0). ¿Puede ocurrir una charla y tal vez esto se cambie, seguro? Pero el hecho es que las personas que se actualicen al nuevo tiempo de ejecución tendrán que trabajar un poco. No es hacer clic en un botón en una interfaz de usuario elegante y tener que trabajar mucho para ellos. Pero al mismo tiempo, estoy de acuerdo con usted en que actualizar al nuevo marco debería ser más fácil que reemplazar un montón de tipos.

esa es la cuestión: no se ve como un 'nuevo tiempo de ejecución'. Si ese fuera el caso, como ya mencioné, no sería un problema. Sin embargo, Microsoft tiene la idea de que las DLL de destino de CoreFX también deben funcionar en .NET por completo, por lo que no hay un nuevo marco. Para los mantenedores de bibliotecas que apuntan a .NET completo también, con funcionalidad que no está en CoreFX (por lo que muchas de las bibliotecas que existen hoy en día) se encontrarán con mucha "diversión", ya que tendrán que mantener 2 versiones. No 2 o 3 #ifdevs y el problema está resuelto, pero muchos, muchos de ellos. Quizás sea diferente para ti, ya que quizás puedas facturar a tu cliente las horas que tienes que dedicar a cambiar las apis. Es diferente cuando crea un sistema de propósito general que es utilizado por muchos.

Si la compatibilidad con un marco compacto es una guía, admitir un ORM en .net completo y CoreCLR será una pérdida de tiempo terrible, mucha frustración y, en realidad, no se ganará mucho: no obtendrá nuevas funciones, solo trabajará en torno a la ausencia de ellas. .

(y antes de que alguien comience con: "pero se ejecuta en linux, obtendrá eso": nuestras cosas se ejecutan en Mono desde hace muchos años. Así que no, no es realmente una característica nueva para ganar, ya estaba allí).

SellMeThisFramework. Oh, ¿por qué me estoy molestando?

pero todo lo construido desde ese momento depende de las clases base en su lugar

_cough_ Linq-to-SQL DataContext _cough_

Como ocurre con muchos ORM que no son de MS, de ahí el problema. Para dapper, solo mordimos
la viñeta y migró a DbConnection. Si tuviéramos el tiempo otra vez, yo
sugeriría encarecidamente que MS use [Obsolete] cuando obsoleto algo. Pero:
no podemos cambiar el pasado.

Sin embargo, es un problema muy doloroso de resolver, especialmente porque la mayoría
los autores de la biblioteca deben continuar con ambas API (compilando de una manera para
net40 etc, y otra forma para DNX). Publiqué previamente el lío espantoso
que dapper usa para hacer esto: no es bonito, y no es tan simple como

si.

El 27 de noviembre de 2015 a las 20:07, "Natan Vivo" [email protected] escribió:

Un hecho importante a considerar en esta discusión es que el IDb *
las interfaces quedaron obsoletas como API para ADO.NET en .NET 2.0 hace una década
cuando se introdujeron las clases base. No se marcaron como obsoletos, pero
todo lo construido desde ese momento depende de las clases base.
Me vienen a la mente los proveedores de App.config y el soporte de cadenas de conexión.

Si tiene un código que depende de esas interfaces, está codificando contra un
API muy obsoleta sin soporte para cosas como métodos asíncronos, lo que significa
Deberá actualizarlo de todos modos si desea que las personas sigan usándolo.

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -160198453.

No veo el punto con que los métodos asíncronos no se pudieron agregar como resultado del uso de interfaces. Podría haber creado nuevas interfaces para los métodos asíncronos. IAsyncDbCommand , IAsyncDataReader etc. Entonces podría hacer que las clases base implementen ambos tipos de interfaces.

Los usuarios de ADO.NET están usando la versión asíncrona o las versiones síncronas, no ambas. Así que habría funcionado muy bien.

Para los desarrolladores de bibliotecas, realmente no importa si la funcionalidad crece y las interfaces siguen siendo las mismas. ¿No es ese el propósito? Introducir nuevas interfaces para nuevas funcionalidades. Trabajar con clases base es una molestia.

¿Puedo resumir el hilo aquí?

Múltiples expertos independientes de la comunidad reconocidos en herramientas de datos .NET,
incluyendo varios autores y mantenedores de ORM le están diciendo - bastante
claramente, que esto representa un conjunto significativo de problemas. No creo
Cualquiera de nosotros ignora las sutilezas o es ingenuo en la programación.
principios, y la mayoría, si no todos, conocemos bien toda la historia de fondo,
porque estábamos allí en ese momento.

La respuesta oficial parece ser "nos parece bien, y EF está contento".
Sí, lo sabemos, porque la decisión se tomó en primer lugar.

Bueno, todos hemos expresado nuestras opiniones, aunque no haya sido fructífero.
El 27 de noviembre de 2015 a las 20:41, "Jonas Gauffin" [email protected] escribió:

No veo el punto con que los métodos asíncronos no se pueden agregar como un
resultado del uso de interfaces. Podrías haber creado _nuevas_ interfaces para
los métodos asincrónicos. IAsyncDbCommand, IAsyncDataReader, etc. Entonces podría
hacer que las clases base implementen ambos tipos de interfaces.

Los usuarios de ADO.NET están usando la versión asíncrona o la síncrona.
versiones, no ambas. Así que habría funcionado muy bien.

Para los desarrolladores de bibliotecas, realmente no importa si la funcionalidad crece y
las interfaces siguen siendo las mismas. ¿No es ese el propósito? Introducir nuevo
interfaces para nuevas funcionalidades. Trabajar con clases base es una molestia.

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -160201361.

Amigos ... actualiza tu código y mejora tu versión principal. Hecho.

Sí, pero no hay ninguna razón por la que no pueda rellenar esa interfaz con una directiva de compilador al apuntar al núcleo. Lo he hecho con algunos de nuestros paquetes pcl.

Creo que Microsoft necesita reiterar que el núcleo no está lleno, pero eso aún no ayuda. Creo que Microsoft necesita limpiar algunas interfaces para ser honesto. Hubo una publicación de blog recientemente donde hay interfaces muy inconsistentes y nunca se sabe cuál elegir ... Creo que definir una segunda interfaz asíncrona apesta. Sería bueno si todo fuera asincrónico.

Sería bueno si se revisara el marco completo para asegurarse de que las cosas que deben marcarse como obsoletas sean ... y se publiquen como 4.6.2

@mgravell +100. Bien dicho, 100% de acuerdo.

¿Cuánto se ve realmente afectado? ¿Estamos hablando de coreclr aquí? El escritorio .NET estará vivo durante muchos años hasta que coreclr pueda ponerse al día. Exactamente para los que se quejan, ¿qué cobertura pierdes aquí? Mucha gente básicamente dice que es el fin del mundo.

@leppie De hecho, existirá durante muchos años. Y también tendremos que mantener estos trucos y soluciones en nuestro código durante los próximos años. El punto de discusión aquí es la eliminación del puente común entre los dos. Esa carga de trabajo se ha transferido a todos los desarrolladores de bibliotecas en lugar de a BCL. Entiendo los pros y los contras de ambos lados de la interfaz, pero no entiendo la actitud de "es menor, sigue adelante" de algunos aquí.

Seamos francos aquí: si todos los consumidores de bibliotecas tienen que hacer lo mismo, debería estar en el BCL . El debate es "¿qué forma toma eso?"

En el lado positivo de eliminar las interfaces, hay un mecanismo de control de versiones _additional_ con el nuevo modelo de empaquetado ahora en juego: qué clases están disponibles en X, Y y Z ahora están mucho mejor respaldadas por herramientas. Por ejemplo, dotnet5.2 vs 5.4 actualmente. Pero también hay algunos inconvenientes. Por ejemplo, SqlClient aún no está al punto de implementar las interfaces tal como están hoy (vea dotnet / runtime # 14302 y dotnet / runtime # 15269), y dado lo que dijo @YoungGah (a menos que esté malinterpretando) estamos esperando en 5.4 o 5.5 para el mismo nivel de soporte para esquema, inserciones masivas, etc.

Entonces, ¿qué pasa con 5.6 (1.5)? Si se agregan más miembros a las clases abstractas, ¿se espera que cada proveedor de datos mantenga el ritmo del objetivo en movimiento que puede cambiar con cada persona en movimiento? ¿Cada consumidor necesita hacer una determinación de la versión de las características disponibles en cada uno? ¿Necesitamos compilar una versión del ensamblado para cada versión de la plataforma que avance para que coincida con la base abstracta de la clase que se está pasando? No está 100% claro cómo funciona todo esto en el futuro con las adiciones. Una interfaz que no cambia es mucho más clara. Además de esto, está la documentación: qué características, métodos, etc. están disponibles en qué versiones _y plataformas_ serán un gran problema para todos los autores de bibliotecas en el futuro. Esa preocupación está solo semi-relacionada aquí, pero está en juego.

Por ahora, estoy esperando ansiosamente la actualización en las próximas 2 semanas. Mi temor es que efectivamente lo será, ya que el mensaje ha sido todo el tiempo: "Estamos haciendo X porque funciona para SQL y Entity Framework, y eso es lo suficientemente bueno", sin usar ninguna de esas palabras. La frustración del lado del autor de la biblioteca, para mí, ha sido la falta de progreso (tanto en el código como en la discusión) en estos frentes durante meses.

100% de acuerdo.

Cuando diseñé la versión 2 (y superior) de SqlFu (mi Micro ORM), tuve que decidir dónde adjuntar los métodos de extensión: a DbConnection / DbCommand oa las interfaces. Encontré esto y decidí ir a las clases abstractas.

Por lo tanto, no estoy TAN afectado, aunque sí estoy afectado por la eliminación de IDataReader , porque MS, en su sabiduría, decidió no dejar muy claro qué interfaces deberían tratarse como obsoletas y cuáles no. Pero en mi caso, no es difícil reemplazar la interfaz.

Sin embargo, veo el valor de tener interfaces dedicadas y no creo que sea TAN difícil para MS mantenerlas / agregarlas nuevamente. Si deciden que las interfaces antiguas no están tan bien diseñadas, ¡está bien! Diseñe los nuevos para que sean más específicos. Al menos en el futuro no tendremos que lidiar con el mismo problema.

(Esta es la semana de Acción de Gracias en los EE. UU., Por lo que las respuestas de Microsoft serán bastante limitadas hasta que regresen a la oficina la próxima semana)

Solo quiero reiterar, para que no pierda ninguna buena sugerencia / error en este hilo, si tiene problemas con las clases base / área de superficie actuales, y / o la forma en que se publican en el futuro, presente un nuevo problema, dejemos esta discusión únicamente sobre las interfaces v1.

@NickCraver Similar a la compatibilidad de versión a versión de .NET Framework, no habrá cambios importantes entre las versiones del área de superficie de .NET Core. Por ejemplo, la adición de miembros abstractos a las clases base sería un ejemplo de un cambio que _no se hará_.

@davkean, ¿

Para el registro, hay problemas separados archivados en el área de superficie, consulte dotnet / runtime # 14302 y dotnet / runtime # 15269 con las últimas actualizaciones de Microsoft el 25 de septiembre y el 2 de octubre respectivamente, a pesar de solicitar actualizaciones y actividad varias veces después de eso. Son 2 meses y 2 lanzamientos que se han ido, con silencio. Esto es a pesar de que dotnet / runtime # 14302 es el problema más activo en este repositorio (y este se acaba de convertir en el segundo). ¿Puedes entender nuestra frustración?

Estoy absolutamente seguro de que no haremos cambios importantes, el equipo de Data Common está buscando hacerlos sin presentar miembros abstractos.

@NickCraver Lo sentimos, lo estamos haciendo mal aquí; estos problemas no se han olvidado, @YoungGah proporcionó una actualización sobre ellos, me aseguraré de que actualice los problemas con el progreso. Muchos MS todavía se están acostumbrando a este _trabajar en el espacio abierto_, mejorará con el tiempo, gracias por llamarnos al respecto.

@niemyjski

dnx es un marco / tiempo de ejecución completamente nuevo

Si cree que las bibliotecas dnx runtime y corefx se manifestaron de la nada, está subestimando seriamente el tiempo que tomaría desarrollarlas desde cero. El hecho de que las bibliotecas CoreFx se ejecuten en el .NET Framework completo debería darle una pista de que no, no es completamente nuevo.

Si no tiene algunas interfaces, trátelo ... con una directiva de compilador.

Sí, pero no hay ninguna razón por la que no pueda rellenar esa interfaz con una directiva de compilador al apuntar al núcleo.

Si se ha molestado en leer los comentarios antes de volar a este hilo, sabrá que a) hay razones yb) esta es una estrategia defectuosa que no funciona para las interfaces BCL centrales.

@nvivo

Un hecho importante a considerar en esta discusión es que las interfaces IDb * fueron obsoletas como API para ADO.NET en .NET 2.0 hace una década cuando se introdujeron las clases base.

Si ese fuera el caso, entonces no habría abierto un problema diciéndoles que vuelvan a usar interfaces que a sabiendas sabía que las clases base desaprobaban hace una década. La forma en que se comunica una API está en desuso es utilizar el atributo [Obsolete] que es su único propósito para existir. Si no se comunica ampliamente, no está en desuso, independientemente de cuáles sean sus pensamientos ahora. El hecho de que la mayoría de los ORM que no son de MS .NET dependan de ellos debería dar una indicación de que su desaprobación fue mal comunicada, en todo caso.

Si tiene código que depende de esas interfaces, está codificando con una API muy obsoleta sin soporte para cosas como métodos asíncronos, lo que significa que deberá actualizarlo de todos modos si desea que la gente siga usándolo.

Un falso muñeco de paja: uno no implica al otro. Ya hemos agregado soporte para las API asíncronas, y no, agregarlas no rompió las bases de código del Cliente existentes ni tampoco la necesidad de cambiar ninguna de las API existentes.

De acuerdo, todo esto de hacer un comienzo limpio es genial, pero puedo hacer una pregunta: ¿Qué compromisos se han hecho para respaldar sus propios marcos? ¿Qué horrores del pasado se han migrado porque son necesarios para que, digamos, Entity Framework se ejecute?

Sería una lástima hacer desaparecer los MicroORM, hacen que el código .Net sea algo eficaz (los EF son una bestia inutilizable para aplicaciones en las que 500ms para cargar unas pocas filas no es aceptable).

En cuanto a las interfaces frente a las clases base: las clases base son excelentes siempre que todo lo que pueda reutilizarse sea virtual. Por ejemplo, una de las decisiones de diseño más irritantes en WCF es el uso copioso de clases selladas que contienen mucha funcionalidad. Supongamos que tiene que hacer un pequeño ajuste en la forma en que se manejan los mensajes XML (porque: interoperabilidad). En lugar de heredar y anular una función pequeña, debe volver a implementarla. En el ejemplo de WCF, se omitió la S en SOLID, por lo que generalmente se deja implementando una interfaz grande sin ninguna de las pruebas que se requieren para garantizar que sea de calidad de producción.

Entonces: las clases base que podemos adaptar son algo bueno.

Estoy absolutamente seguro de que no haremos cambios importantes, el equipo de Data Common está buscando hacerlos sin presentar miembros abstractos.

@davkean Eso es imposible de garantizar, y lo sabes. ADO.NET es un subsistema para comunicarse con software de terceros con una amplia variedad de características, que se exponen a través de una API común con algunos puntos de extensión. Las cosas cambian, incluso en el terreno de las bases de datos, y estos cambios se transmiten a la API utilizada para comunicarse y consumir estos servicios de bases de datos externas. Además: los cambios en el comportamiento _es_ también un cambio radical. Y también los hemos visto en ADO.NET (por ejemplo, sobre el manejo de errores) en los últimos años.

Toda la API de ADO.NET está repleta de los efectos secundarios de esos cambios, a menudo impulsados ​​por SQL Server; Difícilmente ha ocurrido que las cosas se diseñaron en general y luego se trasladaron al Cliente SQL, pero al revés (por ejemplo, no hay muchas características, si es que las hay, en las clases base que SqlClient ignora). Además, las cosas que necesitaban los terceros nunca llegaron a la API.

Entonces, en resumen, es una API que, al principio con .NET 1.0 tenía un diseño general (que demostró ser seriamente defectuoso en muchas áreas) y que desde entonces ha sido parcheado con características a la izquierda y a la derecha para hacer frente a los cambios en el paisaje. _La mayoría_ de ellos todavía están en la API (si no todos). Y ahora Microsoft eliminará una: las interfaces.

No se gana absolutamente nada al eliminar una parte aleatoria de la API: no se agrega ninguna función a través de esto (podría dedicar tiempo a eso en lugar de retroceder aquí, por ejemplo), pero el código que aprovecha esa parte de la API no funcionará. Si el punto detrás de todo este movimiento de queso es 'comenzar de nuevo', entonces hágalo, pero hágalo rediseñando la API para convertirla en una verdadera API de propósito general, deshacerse de _todo_ el cruft que se ha acumulado a lo largo de los años .

Pero eso no está hecho. Nadie tiene que preguntarse por qué, todos sabemos por qué. También sabemos que si un equipo dentro de MS se hubiera visto gravemente afectado por la eliminación de las interfaces, nunca se habrían eliminado en primer lugar.

Si Microsoft puede agregar nueva funcionalidad a través de clases base para que los proveedores de terceros implementen automáticamente 'una forma de' la función (como con Async, donde el método async recurre al método de sincronización si el proveedor no lo implementa), genial: eso significa que nosotros, los desarrolladores de ORM de terceros (y seamos sinceros: muchos desarrolladores acceden a _su_ API a través del código de (micro) ORM de terceros) pueden simplemente apuntar a una clase y eso es todo.

Pero no está garantizado que lo hagas. Por ejemplo, nadie en Microsoft se ha molestado nunca en agregar características específicas para Oracle o PostgreSql a ADO.NET. Por ejemplo, el uso de UDT, árboles de documentos, múltiples conjuntos de resultados a través de cursores, cada proveedor de ADO.NET tiene que encontrar su propia manera de manejar estas características. ¿Qué pasa si (¿cuándo?) SQL Server obtiene una función de árbol de documentos, por ejemplo, con documentos JSON, ¿ADO.NET se actualizará y luego con una nueva API para eso? ¿Como lo ha hecho en el pasado con los informes de errores de los proveedores de ADO.NET?

No puede ofrecer garantías como esa, y no es prudente afirmar aquí que la EM no romperá nada en el futuro. Siempre lo han hecho ya veces incluso tuvieron que hacerlo: después de todo, cada API tiene fallas, y ADO.NET está lleno de ellas; de una forma u otra, algún día alguien los 'arreglará'.

Entonces, para recapitular: las interfaces son parte de ADO.NET como también las partes estropeadas en otras partes de la API son parte de ADO.NET. Esos no se eliminan para arreglar la API, ni se refactoriza la API para convertirla en una API de propósito más general: se dejó como está, con algunos elementos eliminados como los elementos dependientes de DataSet / Table porque estos no se han portado (y no hay otros temas que se debaten con un progreso similar), excepto que ... se eliminan las interfaces.

Solo desde ese punto de vista, ya no tiene ningún sentido.

@mythz

Si ese fuera el caso, entonces no habría abierto un problema diciéndoles que vuelvan a usar interfaces que sabían que estaban en desuso.

No es posible que lea el OP y lo comprenda. Estas discusiones se están volviendo demasiado religiosas y estás asumiendo cosas que nadie dijo.

Abrí este problema porque creo que las interfaces son mejores para describir una API y ayudan con las pruebas. Si se hace, no creo que deban ser compatibles con una API de 15 años que tuviera sus problemas. La compatibilidad con versiones anteriores nunca fue un punto en el tema hasta que ustedes pasaron la discusión a eso.

No es que crea que las cosas deberían romperse por el simple hecho de hacerlo. Pero el control de versiones de la interfaz es un problema del pasado. Si corefx cambia algo entre las versiones principales, se espera que sus versiones principales tengan cambios importantes. Si rompen una interfaz entre versiones menores, eso es solo descuido.

Ya hemos agregado soporte para API asíncronas

No puede agregar una API asíncrona sobre una API de sincronización. Si hizo eso usando IDbConnection o IDbCommand, lo hizo mal. Si no está utilizando estas interfaces, entonces no tiene ningún sentido defender ninguna compatibilidad con ellas.

Ya hemos agregado soporte para API asíncronas

No puede agregar una API asíncrona sobre una API de sincronización. Si hizo eso usando IDbConnection o IDbCommand, lo hizo mal. Si no está utilizando estas interfaces, entonces no tiene ningún sentido defender ninguna compatibilidad con ellas.

En ADO.NET eso es lo que hicieron: los métodos Async por defecto recurren a las variantes de sincronización. De esta manera, todos los proveedores de ADO.NET que no son compatibles con Async (léase: todos excepto SqlServer en este momento) no tienen que implementar cosas que no son compatibles: el código de terceros en los ORM que ofrecen una api Async puede programar contra los métodos Async en ADO.NET y si el proveedor de ado.net no admite async, nadie lo sabrá. Bueno ... lo sabrás porque es más lento, pero aparte de eso.

Ahora también es una buena ilustración de la ausencia de cualquier 'diseño' o arquitectura general dentro de ADO.NET: no hay _no_ forma de crear un punto de guardado transaccional en la API general. Aunque casi todas las bases de datos lo admiten con un método 'Guardar (cadena)' en su clase derivada de DbTransaction. Todos excepto OleDbTransaction (ya que MS Access no lo admite, al menos esa es mi sospecha).

No es fácil, pero nadie dijo que fuera fácil. Este problema no es nuevo, OleDB y ODBC lo han abordado durante muchos años, JDBC ha encontrado una manera de resolverlo, Microsoft no tiene que reinventar la rueda para superar cosas como esta. Tampoco es exclusivo del ámbito DB: por ejemplo, cada tarjeta de video que existe admite un subconjunto diferente de características a través de su API, expuestas al desarrollador a través de Direct3D / X. Es realmente interesante cómo van los diseños en estos otros mundos: la API está diseñada y las partes que necesitan admitirla (controladores JDBC, escritores de controladores OleDB, etc.) tienen que implementarlos. ¿Su controlador no es compatible con X? Su controlador no es compatible con X. "Oracle no es compatible con ADO.NET v10". Nadie dentro de Oracle quiere leer eso. En cambio, SqlClient es el líder, y lo que se cae del vagón se agrega a ADO.NET y eso es todo.

En ADO.NET eso es lo que hicieron: los métodos Async por defecto recurren a las variantes de sincronización.

No, no lo es. La API expone métodos asíncronos que recurren a los métodos de sincronización de forma predeterminada, pero los proveedores anulan las operaciones asíncronas reales. Lo que @mythz está afirmando es que está usando IDbCommand e IDbConnection y lo está haciendo.

Esto no es posible, punto. Si lo hace, o no lo está haciendo bien o no está utilizando la interfaz. No puede inventar async si la api subyacente no es async.

No, no lo es. La API expone métodos asíncronos que recurren a los métodos de sincronización de forma predeterminada, pero los proveedores anulan las operaciones asíncronas reales. Lo que @mythz está afirmando es que está usando IDbCommand e IDbConnection y lo está haciendo.

Ningún proveedor oficial lo hace, excepto SqlClient, todos los demás, por ejemplo, ODP.NET, no implementan ninguna forma de código asíncrono y, por lo tanto, el código de llamada recurre a las variantes de sincronización (los métodos asíncronos en DbDataReader / DbCommand, etc.que realmente ejecutan el código de sincronización ). por lo que el código de usuario llama a una variante asíncrona, que está bajo el capó realizando operaciones de sincronización. Lo que da como resultado que no se realice ninguna asincronización en la práctica (ya que todo el código simplemente se sincroniza en la práctica). Quizás los proveedores de devart implementan una API asíncrona en su propia implementación, no estoy seguro.

De todos modos, no se trata de hacerlo bien, se trata de versionar las API.

@nvivo

Abrí este problema porque creo que las interfaces son mejores para describir una API y ayudan con las pruebas. Si se hace, no creo que deban ser compatibles con una API de 15 años que tuviera sus problemas. La compatibilidad con versiones anteriores nunca fue un punto en el tema hasta que ustedes pasaron la discusión a eso.

Ok, entonces sabía que las interfaces centrales de ADO.NET estaban en desuso hace 10 años, con todo movido a las clases base, sin embargo, pensó que deberían simplemente abandonar eso ahora y regresar a las interfaces, casualmente usando los mismos nombres que las interfaces existentes, pero el las interfaces existentes ya no deberían existir, porque no se requiere compatibilidad con versiones anteriores, ¿verdad? seguro, suena legítimo.

Si desea hacer avanzar una plataforma, evoluciona las API con el tiempo y las respalda en paralelo, lo que brinda a todos la capacidad de admitir API paralelas y les permite planificar su camino y sus Clientes fuera de ellas. Arrancarlos sin previo aviso rompe innecesariamente el ecosistema que depende de ellos y empuja la complejidad hacia todas las dependencias posteriores.

No puede agregar una API asíncrona sobre una API de sincronización. Si hizo eso usando IDbConnection o IDbCommand, lo hizo mal. Si no está utilizando estas interfaces, entonces no tiene ningún sentido defender ninguna compatibilidad con ellas.

Me gustaría que dejaras de contaminar este hilo con comentarios sobre cosas de las que claramente no tienes conocimiento. Lea el código fuente si desea saber cómo se implementan las API Async y deje de difundir falsedades a ciegas. Es imposible que una biblioteca de terceros amplíe las interfaces System.Data. Proporcionamos una API independiente de la implementación que, para admitir todos los principales RDBMS, expone la dependencia mínima que cada proveedor de ADO.NET implementa en su API externa, es decir, las interfaces centrales System.Data. Las API asíncronas son métodos de extensión de IDbConnection que, en segundo plano, aprovechan las API asíncronas en los proveedores ADO.NET concretos que las admiten. Internamente, ya existen dependencias concretas en cada proveedor ADO.NET compatible, independientemente del soporte asincrónico. Su sugerencia de que "en realidad no tiene ningún sentido defender ninguna compatibilidad retroactiva con ellos" es inexperta y completamente infundada.

Permítanme preguntarle esto al lado de Microsoft de la cerca (cc @davkean @YoungGah): digamos que es un mundo perfecto y nunca surgió nada. ¿Cuándo _ quieres romper cosas? Versiones principales como 6.0? ¿En otro momento? El argumento en contra de las interfaces es que no versionan. Bueno, sí, eso es válido, pero _si tampoco cambiamos las clases abstractas_, también es un punto discutible. Entonces ... ¿podemos aclarar algo?

Seguimientos:
Si la respuesta es sí (en algún momento posterior a la RTM habrá cambios), ¿qué tipo de interrupciones veríamos? ¿Adiciones, nuevos métodos? Si heredo la clase base de mi proveedor, ¿qué sucede cuando agrega un método conflictivo que la gente está usando, etc.?

Si la respuesta es no (nunca): ¿por qué no volver a agregar las interfaces?

Este hilo está un poco pendiente de discutir _ ahora mismo_, lo cual es principalmente algo bueno porque esto debe arreglarse lo antes posible. Todos los autores de bibliotecas aquí saben lo difícil que es conseguir algo después de un lanzamiento, y es por eso que nos esforzamos tanto. Desafortunadamente, la falta de un plan claro para _futuras_ adiciones y cambios, si los hay, hace que los argumentos estén menos informados.

¿Cuales son tus planes para el futuro?

No deberíamos forzar la implementación a través de clases abstractas en este caso. OMI

Microsoft no realizará cambios que se rompan para .NET 4.5. Es parte de Windows. La compatibilidad es el rey.

Microsoft podría realizar cambios importantes en .NET Core que no afectan a 4.5. Dudo que publiquen 1.0 RTM en algo de bajo nivel como ADO.NET, pero la barra es más baja. No es parte de Windows y las versiones de .NET Core se pueden implementar una al lado de la otra.

Las clases abstractas se pueden cambiar, eso no se rompe. Simplemente agregue un método que sea virtual con una implementación predeterminada. Ya se ha hecho con los métodos asíncronos de ADO.NET. Con la introducción de .NET Core, creo que los cambios en las clases compartidas deberían realizarse junto con las versiones de .NET 4.5 para mantener la compatibilidad. Alguien me corrija si eso está mal y solo se aplica a las interfaces: grin:

@FransBouma

Ningún proveedor oficial hace eso, excepto SqlClient, todos los demás, por ejemplo, ODP.NET, no implementan ninguna forma de código asíncrono y, por lo tanto, el código de llamada recurre a las variantes de sincronización.

Tiene razón, pero eso no es un problema con la API, y más pereza o falta de comprensión por parte de los implementadores. El conector MySql, por ejemplo, volvió a implementar todos sus métodos asíncronos creando un TaskCompletionSource y completándolos con métodos de sincronización, lo cual es ridículo. Podrían simplemente eliminar la mitad de su base de código y permanecer con el mismo comportamiento.

No decir que las interfaces resolverían eso, pero no tener un comportamiento predeterminado para async al menos haría que algunos de ellos pensaran en esto. El hecho de que el 90% de la gente muy técnica no entienda las operaciones asincrónicas tampoco ayuda.

Las clases abstractas se pueden cambiar, eso no se rompe. Simplemente agregue un método que sea virtual con una implementación predeterminada. Ya se ha hecho con los métodos asíncronos de ADO.NET.

Esto se está rompiendo. Está rompiendo para todas las bibliotecas que subclasificaron esta implementación sin saber que se agregó y la gente luego consume este pensamiento. Oh, esta implementación ahora es compatible con postgresql BAM ERROR wtf pasó ...

La implementación forzada para una abstracción de base de datos es incorrecta.

No importa si son interfaces o una clase base. Habrá cambios importantes. Pero la implementación predefinida forzada es incorrecta.

El polimorfismo no funciona de esa manera. No puede anular un método sin saberlo. Si su referencia es una DbConnection y llama a QueryAsync, solo llamará a ese método o lo que sea que haya sido anulado. No se llamará a un método llamado QueryAsync que ya existe en una subclase.

Estás confundiendo anular un método frente a ocultarlo con el mismo nombre.

@JamesNK

Si los métodos se definen como abstractos, no existe implementación en la clase base. Esto rompe el contrato para los terceros, ya que requiere que agreguen implementación en la subclase.

Si hace que el método sea virtual para que se pueda anular, entonces existe una implementación en la clase base que podría no tener sentido para la subclase. Esto todavía no funciona porque existe una implementación que no fue implementada por el autor de la biblioteca. Seguro que tu aplicación puede compilarse, y todo está bien, pero alguien llama a ese método y no es válido para la subclase. Eso está mal. Esa es una implementación forzada que no pertenece a la subclase.

Entonces, clase abstracta donde puede existir implementación que no pertenece a la subclase. O interfaces donde no existe una implementación predeterminada para terceros.

@ phillip-haydon es por eso que se implementa como un método virtual, no como un método abstracto.

Puede agregar cosas, solo romperá las subclases que ya tienen un miembro con la misma firma (nombre / argumentos). Si los argumentos son diferentes, podría introducir errores sutiles si los desarrolladores confunden las sobrecargas.

Esa es una implementación forzada que no pertenece a la subclase.

Entonces no lo pongas ahí.

@jamesnk

No lo pongas ahí. Por eso estamos discutiendo la eliminación de interfaces.

Hacerlo virtual no resuelve el problema. No debería haber una implementación predefinida. Fin de la historia

@JamesNK En este caso no lo pusimos ahí, _Microsoft_ lo puso ahí incluyéndolo en abstracto. La adición de métodos que se supone que funcionan en _todos_ los proveedores que alguna vez heredan, no veo que vaya sin problemas o de manera eficiente, incluso si no se está rompiendo técnicamente (admitiré que "debería usar nuevas" advertencias de compilación que _ técnicamente_ no se están rompiendo). Simplemente, no habrá una implementación compartida o compartida inmediatamente en la mayoría de los casos. Entonces, ¿cuál es la alternativa? throw new NotImplementedException() dentro de ese virtual? En primer lugar, eso no es un argumento para que exista, está plagado de más problemas (de tiempo de ejecución).

Veamos hoy: preferiría ver un IDbAsyncConnection agregado cuando un proveedor lo admite en lugar de un montón de métodos que son sincrónicos bajo las sábanas que conducen a confusión e ineficiencia, que es lo que tenemos hoy en estos resúmenes.

Preferiría ver un IDbAsyncConnection agregado cuando un proveedor lo admite en lugar de un montón de métodos que son sincrónicos bajo las sábanas que conducen a confusión e ineficiencia.

@NickCraver +1000 a eso. Como este error aquí donde el equipo de Oracle simplemente no entiende lo que significa async.

Podrías hacer eso con interfaces. El problema con ellos es que no puede aceptar argumentos que exijan múltiples interfaces, por ejemplo, necesito un tipo que sea IDbAsyncConnection e IDbConnection. Pierde una escritura fuerte y tiene que comenzar a buscar interfaces de estilo COM, que no creo que sea muy fácil de usar. Es una herramienta en el diseño de API que tiene su lugar pero no sé si iría a ella por defecto.

Si la implementación predeterminada arrojaba NotImplementedException, entonces atornillarla a la clase base es lo incorrecto. Como dije, no lo pongas ahí entonces. Si ve que alguien lo hace, plantee un problema.

De cualquier manera, ya sean interfaces o clases base abstractas, mi experiencia es agregar nuevas características a una biblioteca que no fue diseñada originalmente para ellos sin romper el mundo es muy difícil.

@JamesNK presumiblemente IDbAsyncConnection heredaría IDbConnection aquí, pero ese no tiene por qué ser necesariamente el caso - podrían compartir miembros comunes o heredar de una base común. Por ejemplo, en Dapper probablemente implementaríamos de la siguiente manera:

`` C #
IEnumerableConsulta(este IDbConnection cnn, CommandDefinition cmd)

``` C#
Task<IEnumerable<T>> QueryAsync<T>(this IDbAsyncConnection cnn, CommandDefinition cmd)

Imagino que la mayoría de las bibliotecas con métodos sync / async tendrían usos e implementaciones similares.

_Editar: _ después de escribir que me doy cuenta de cuánto mejor sería Async al final del nombre para todos estos ...

ADO.NET tuvo los siguientes cambios importantes a lo largo de los años:

  1. En 2003 (1.1) hicieron un cambio radical desde 1.0 y lo rediseñaron
  2. En 2005 (2.0) se trasladaron al modelo de proveedor con las clases base que existen en la actualidad
  3. En 2012 (4.5) agregaron soporte asincrónico, que en realidad no cambió nada más que agregar nuevos métodos que hicieron las mismas cosas de forma asincrónica.

Volver a la forma en que se definió la api de 2003 es un cambio que romperá la compatibilidad (que la gente no quiere) o eliminará las características agregadas en la última década. Pero agregar una nueva interfaz a .NET Core con el diseño actual es _compatible con la fuente_ con cualquier versión de .NET. La recompilación es todo lo que necesita para mantener la mayor parte del código escrito en los últimos 15 años en funcionamiento. Y deberá volver a compilar de todos modos para apuntar a corefx.

Esta API se ha mantenido estable durante mucho tiempo. Podría rediseñarse como interfaces si la gente quisiera. Como de costumbre, aquí no hay problemas técnicos, se reduce a cicatrices, preferencias y ego.

¿Por qué no recuperar las interfaces y marcarlas como obsoletas?

Aunque se descartó esta idea, me pregunto si las inferfaces neutrales de ensamblaje podrían haber ayudado en este tipo de situación, consulte este http://davidfowl.com/assembly-neutral-interfaces/ y luego su implementación
http://davidfowl.com/assembly-neutral-interfaces-implementation/

Creo que las interfaces neutrales de montaje son una pista falsa aquí; si algo es
que suceda, esto es algo totalmente "común", ya que "común" existe. Más eso
es discutible ya que la función se evaporó.
El 28 de noviembre de 2015 a las 17:38, "Shahid Khan" [email protected] escribió:

Aunque esta idea se descartó, me pregunto si el ensamblaje es neutral
inferfaces podrían haber ayudado en este tipo de situación a ver esto
http://davidfowl.com/assembly-neutral-interfaces/ y luego su
implementación
http://davidfowl.com/assembly-neutral-interfaces-implementation/

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -160323344.

Lo que observo:

  • Las personas detrás de la idea de CoreClr (nuevo marco brillante) tienen una mentalidad completamente diferente de la gente detrás de su implementación (la compatibilidad con versiones anteriores con código base de 15 años es el rey).

Lo que pienso:

  • Al eliminar las interfaces, empeora las cosas.
  • Nada es obsoleto hasta que se decora con [Obsolete]. Y no importa quién piense qué en cualquier momento. Este es el contrato y si simplemente no lo está cumpliendo, entonces no lo está haciendo.

Lo que quiero:

  • Utilice la (s) interfaz (s) como superficie base de la API en lugar de la (s) clase (s) base (s).

Lo que yo siento:

  • Estamos a finales de 2015 y es frustrante y triste ver a la gente discutiendo sobre esto.

las interfaces no se pueden versionar.

¿Seguramente las interfaces se pueden versionar fácilmente con la herencia de interfaces? Y si está haciendo algo totalmente diferente; bueno, es una interfaz diferente.

interface IOldInterface {}
interface INewInterface : IOldInterface {}
interface IDifferentInterface {}

class SomeClass : IOldInterface, INewInterface, IDifferentInterface
{
}

Simplemente no lo llames IInterfaceV2 , IInterfaceV3 , necesita explicar lo que agrega.

Para poner en contexto [Obsolete] la interfaz anterior, y usar algunas interfaces nuevas y llamarlas como realmente son; en lugar de los métodos no asíncronos que parecen normales en la actualidad.

public interface IDbUtilityProviderFactory 
{
    IDbConnectionStringBuilder CreateConnectionStringBuilder();
    IDbParameter CreateParameter();
}

public interface IDbBlockingProviderFactory : IDbUtilityProviderFactory 
{
    IDbBlockingCommand CreateBlockingCommand();
    IDbBlockingConnection CreateBlockingConnection();
}

public interface IDbAyncProviderFactory : IDbUtilityProviderFactory 
{
    IDbCommandAsync CreateAsyncCommand();
    IDbConnectionAsync CreateAsyncConnection();
}

@abatishchev

Las personas detrás de la idea de CoreClr (nuevo marco brillante) tienen una mentalidad completamente diferente de la gente detrás de su implementación (la compatibilidad con versiones anteriores con código base de 15 años es el rey).

Gracias por dejar esto en claro, parece que hay que señalarlo. En general, creo que los equipos de MS se preocupan profundamente por la compatibilidad con versiones anteriores, que es vital para la evolución de ambos lenguajes y sus plataformas. Es solo que las decisiones en torno a System.Data no se toman teniendo en cuenta el ecosistema más amplio, al que estas abstracciones centrales deberían servir por igual.

  • Al eliminar las interfaces, empeora las cosas.
  • Nada es obsoleto hasta que se decora con [Obsolete]. Y no importa quién piense qué en cualquier momento. Este es el contrato y si simplemente no lo está cumpliendo, entonces no lo está haciendo.

Precisamente.

Respecto al versionado de interfaces. Corrígeme si me equivoco, pero pensé que el objetivo de CoreClr es un control de versiones independiente más detallado. ¿Rompiendo el cambio? ¡Auge! Lanzamiento de una nueva versión principal.
Parece que después de 15 años de experiencia en diseño, va a repetir los mismos errores, pero ahora tendrá paquetes nuget versionados y lanzados de forma independiente. Los argumentos anteriores son idénticos al buen marco monolítico antiguo, a pesar de que ya no lo es.
Si la compatibilidad con versiones anteriores es imprescindible, simplemente no puede eliminar esas interfaces. Si no es así, dejemos de hablar de ello y diseñemos la API desde cero, esta vez escuchando a los principales autores de ORM y otros desarrolladores experimentados de ADO.NET.
Gracias.

@abatishchev muy buenos puntos. Yo también me pregunté: ¿cuál es realmente el punto del argumento de compatibilidad con versiones anteriores? Si se agrega una nueva función a CoreClr, todo lo que lo use no se ejecutará en .net completo, por lo que, para estar seguro, solo se puede usar el comportamiento común. (que nunca funcionó bien).

Perdón por el largo silencio de mi parte.

Portar a .NET Core

En primer lugar, hablemos del elefante en la sala, que es que .NET Core no tiene tantas API disponibles como muchas personas, incluyéndonos a nosotros, esperarían.

Estoy trabajando con mi equipo para armar un conjunto de documentos sobre cómo vamos a abordar el área de portar activos existentes a .NET Core.

Estamos planeando migrar más de la funcionalidad que actualmente solo existe en .NET Framework / Mono a .NET Core. Este documento indicará cómo vamos a hacer eso, cómo priorizamos y cuáles serán las mecánicas. No solo me gustaría que ese trabajo ocurriera al aire libre, también me gustaría permitir que la comunidad nos ayude a portar más funcionalidades.

Cambios importantes

Hay un área que causa mucha confusión cuando la gente habla de .NET Core. Déjame aclarar una cosa:

No es un cambio importante si .NET Core tiene menos API que .NET Framework.

La razón es que .NET Core es una plataforma nueva y técnicamente puede tener un conjunto arbitrario de API. Sin embargo, por supuesto, no queremos un conjunto arbitrario , eso es lo que hicimos en el pasado. El objetivo de .NET Core es tener una historia en la que la gente pueda crear bibliotecas (y con aplicaciones de consola hasta cierto punto, incluso aplicaciones) que se ejecuten en .NET Framework y .NET Core. Esto requiere que haya un subconjunto de ambas plataformas que sea 100% compatible. En ese contexto, nos escuchará hablar sobre cambios importantes.

Además de eso, nuestra intención es tener una barra de compatibilidad alta dentro de .NET Core. En otras palabras, no planeamos realizar cambios importantes en la API entre una versión de una API de .NET Core y otra.

Interfaces

Agregar miembros a las interfaces es, por definición, un cambio radical. Algunas personas argumentan que el impacto es bajo y que hay formas de modelar eso, pero tal como está hoy en día, es un cambio binario y rompedor de fuentes.

WinRT, que se basa en COM y, por lo tanto, depende en gran medida de las interfaces, resuelve este problema creando más interfaces, como IFoo , IFoo2 , IFoo3 . Es factible, pero ciertamente es complicado sin una función de tiempo de ejecución o de idioma que lo haga soportable. Hasta ahora, tal característica no existe. Sin embargo, como miembro del equipo de diseño de idiomas, estoy bastante interesado en escuchar propuestas. Otros lenguajes y plataformas tienen ideas relacionadas en ese espacio, y también estoy buscando activamente opciones (como combinaciones / rasgos, extensión de Swift-todo, o miembros predeterminados para interfaces).

Dado que todavía nos preocupamos por la compatibilidad con versiones anteriores, generalmente preferimos los tipos base abstractos sobre las interfaces.

Interfaces ADO.NET

Dicho todo esto, hablemos de la pregunta original en este hilo: exponer las interfaces del proveedor de ADO.NET.

Como explicó David: los consideramos obsoletos cuando se introdujeron los tipos base abstractos, que estaba en .NET 2 / Visual Studio 2005. Parece que hay una fuerte creencia de que tener estas interfaces es fundamental para trasladar los marcos ORM a .NET Core. Para mí, esto proporciona suficiente evidencia de que deberíamos portar las interfaces a .NET Core.

Sin embargo, como ocurre con todas las API nuevas, debemos tener en cuenta uno de los objetivos principales de .NET Core, que es tener una pila de componentes. La interfaz IDataReader depende de DataTable , que depende de DataSet . Como se describe en dotnet / runtime # 14302, no nos oponemos a agregar soporte para DataTable pero consideramos DataSet heredado. Sin embargo, podríamos agregarlo como un paquete separado, pero de cualquier manera esto requeriría romper la cadena de dependencia de las interfaces -> DataTable -> DataSet . Trabajaré con @YoungGah para ver qué podemos hacer allí.

¿Este enfoque abordaría las preocupaciones?

A menos que me equivoque, la única dependencia de DataTable en IDataReader es la
Método GetSchemaTable (), ya discutido en profundidad en el DataTable
cadena. Reconozco fácilmente, sin embargo, que el hecho de que exista una
Espero agregar _alguna_ mente de funcionalidad similar en una fecha posterior (ya sea
a través de DataTable o no) hace que sea muy incómodo exponer esto en el
interfaz, ya que extender la interfaz más tarde es problemático. No seria
tan simple como "quitar el método por ahora, agregar algo más después"
El 5 de diciembre de 2015 a las 12:17 a. M., "Immo Landwerth" [email protected] escribió:

Perdón por el largo silencio de mi parte.
Portar a .NET Core

En primer lugar, hablemos del elefante en la habitación, que es .NET.
Core no tiene tantas API disponibles como mucha gente, incluida
nosotros - esperaría.

Estoy trabajando con mi equipo para preparar un conjunto de documentos sobre cómo vamos
para abordar el área de portar activos existentes a .NET Core.

Estamos planeando portar más de la funcionalidad que actualmente solo
existe en .NET Framework / Mono a .NET Core. Este documento llamará
cómo vamos a hacer eso, cómo priorizamos y qué mecánicas
ser. No solo me gustaría que ese trabajo ocurriera al aire libre, también me gustaría
Permitir que la comunidad nos ayude a portar más funcionalidades.
Cambios importantes

Hay un área que causa mucha confusión cuando la gente habla de
.NET Core. Déjame aclarar una cosa:

No es un cambio importante si .NET Core tiene menos API que .NET
Estructura.

La razón es que .NET Core es una nueva plataforma y técnicamente puede
tener un conjunto arbitrario de API. Sin embargo, por supuesto, no queremos
conjunto arbitrario
http://blogs.msdn.com/b/dotnet/archive/2014/12/04/introducing-net-core.aspx
- eso es lo que hicimos en el pasado. El objetivo de .NET Core es tener una
historia donde la gente puede crear bibliotecas (y con aplicaciones de consola para un cierto
extender incluso las aplicaciones) que se ejecutarán en .NET Framework y .NET Core. Esta
requiere que haya un subconjunto de ambas plataformas que sea 100%
compatible. En ese contexto, nos escuchará hablar sobre cambios importantes.

Además de eso, nuestra intención es tener una barra de compatibilidad alta dentro de .NET Core.
En otras palabras, no planeamos realizar cambios importantes en la API entre
una versión de una API de .NET Core y otra.
Interfaces

Agregar miembros a las interfaces es, por definición, un cambio radical.
Algunas personas argumentan que el impacto es bajo y que hay formas de modelar
eso, pero tal como está hoy en día, es un cambio binario y que rompe la fuente.

WinRT, que se basa en COM y, por lo tanto, depende en gran medida de las interfaces,
resuelve este problema creando más interfaces, como IFoo, IFoo2,
IFoo3. Es factible, pero ciertamente es complicado sin un tiempo de ejecución o
característica del idioma para que sea soportable. Hasta ahora, tal característica no existe.
Sin embargo, como miembro del equipo de diseño de idiomas, estoy bastante interesado en
escuchar propuestas. Otros lenguajes y plataformas tienen ideas relacionadas en ese
espacio, y también estoy buscando activamente opciones (como
mix-ins / rasgos, extensión de Swift-todo, o miembros predeterminados para
interfaces).

Dado que todavía nos preocupamos por la compatibilidad con versiones anteriores, generalmente favorecemos
tipos de base abstractos sobre interfaces.
Interfaces ADO.NET

Dicho todo esto, hablemos de la pregunta original en este hilo:
exponiendo las interfaces del proveedor ADO.NET.

Como explicó David: los consideramos obsoletos cuando la base abstracta
se introdujeron tipos, que estaba en .NET 2 / Visual Studio 2005. Parece
que hay una fuerte creencia de que tener estas interfaces es fundamental para
portar marcos ORM a .NET Core. Para mí, esto proporciona suficiente evidencia
que deberíamos portar las interfaces a .NET Core.

Sin embargo, como ocurre con todas las API nuevas, debemos tener en cuenta una de las
objetivos principales de .NET Core, que es tener una pila en componentes. los
interfaz IDataReader tiene una dependencia en DataTable, que tiene una
dependencia de DataSet. Como se describe en dotnet / runtime # 14302
https://github.com/dotnet/corefx/issues/1039 , no nos oponemos a agregar
soporte para DataTable, pero realmente no queremos portar DataSet. Entonces
agregar las interfaces requerirá romper esa dependencia. Trabajaré con
con @YoungGah https://github.com/YoungGah para ver qué podemos hacer allí.

¿Este enfoque abordaría las preocupaciones?

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -162115855.

Sí, IDataReader tiene una dependencia de DataTable que tiene una dependencia de DataSet.

Como se mencionó anteriormente, no podemos eliminar miembros de las interfaces, por lo que necesitaríamos romper esa dependencia de alguna otra manera; ya sea al no portar la interfaz o al romper la dependencia de DataTable -> DataSet.

¿Este enfoque abordaría las preocupaciones?

Sí, restaurar las interfaces ADO.NET incluso sin el método GetSchemaTable() resolvería las grandes dependencias de los problemas de la interfaz ADO.NET a los que me enfrento actualmente.

Supongo que romper la dependencia de DataTable -> DataSet si significara que GetSchemaTable() también se incluiría, sería el enfoque preferido para aquellos que dependen de GetSchemaTable() (por lo que ese sería mi voto) - pero Daría más peso a los desarrolladores que esto afecta.

@terrajobst gracias por su resumen y compartir sus consideraciones.

@terrajobst gracias por el resumen y las explicaciones. Mantengo Npgsql, así que escribo desde la perspectiva de un proveedor ADO.NET, no un ORM.

En .NET Core, Microsoft parece haber tenido el deseo de eliminar algunas características heredadas de ADO.NET, a saber, DataTable / DataSet y las interfaces. Esto crea una API mejor y más limpia en el futuro, pero crea un trabajo sustancial para los usuarios que ya dependen de las interfaces y la API DataTable / DataSet.

Personalmente, creo que matar DataTable / DataSet es algo bueno, y que debería introducirse una API de metadatos nueva y mejor (creo que esto es cierto incluso sin matar DataTable / DataSet). No minimizo
el dolor causado por esta rotura a los consumidores de ORM (y otros), pero una cosa para recordar es que si alguna vez habrá una oportunidad para limpiar e introducir roturas, .NET Core es esa oportunidad.

Un factor frustrante adicional aquí es que mantener las interfaces ADO.NET también significa mantener DataTable / DataSet también. En otras palabras, aunque personalmente no estoy muy convencido del problema de las interfaces, mantenerlas significa mantener DataTable / DataSet y eso parece más problemático.

Como no sé cuántos ORM existen que todavía dependen de las interfaces, y tampoco sé cuánto esfuerzo les supondría hacer la transición a las clases base, la elección no es muy grande. claro aquí. Pero mi instinto es aprovechar esta oportunidad y limpiar las cosas incluso si eso significa que algunas personas sufren ...

PD: Hagas lo que hagas, no sigas el camino de la explosión de la interfaz, es decir, IFoo2, IFoo3. Eso es solo una no solución.
PPS Si decide crear una nueva API de metadatos que no sea DataTable en .NET Core y desea que las bibliotecas de .NET Core se ejecuten en .NET Framework, esto significa que tendrá que lanzar un nuevo .NET Framework con la nueva API junto con .NET Core.

@terrajobst Gracias por romper el silencio;). Creo que el problema principal es que no hay ningún diseño presente para ADO.NET. No ha habido durante bastante tiempo y se muestra en varias partes de la API, que es una mezcolanza de funciones acolchadas sin pensar las cosas durante más de un par de minutos (parece). Estoy de acuerdo con @roji en esto: .NET core es una oportunidad única en la vida para hacer algo al respecto, por lo que .NET core no debería verse retenido por la regla (en mi opinión, tonta) de que debería ser compatible con versiones anteriores .NET completo.

Dicho esto, si las cosas no van a mejorar para ADO.NET (es decir, obtiene un diseño real en el que se diseña una API general que luego se especializa en SqlClient y no al revés. SQL Server, pero en otras bases de datos se agregan a la API general y no se dejan en manos del escritor del proveedor de ADO.NET), entonces la siguiente mejor opción es portar tanto como sea posible, incluidas las interfaces y nosotros los desarrolladores de terceros #ifdef nosotros mismos alrededor de los baches que quedarán.

Sin embargo, la dependencia de DataTable puede ser un problema, ya que la interfaz de IDataReader dificultará que las cosas sean compatibles con versiones anteriores de .NET full _if_ datatable en cualquier forma que no se transfiera. Pero creo que es una causa perdida de todos modos. MS dijo varias veces que .NET full no recibirá tantas / tan frecuentes actualizaciones como .NET core, por lo que si se agrega algo _nuevo_ al .NET core, nadie puede usarlo, ya que usarlo hace que la biblioteca Inmediatamente incompatible con .NET completo. Es probable que me pierda algo aquí, así que si ese es el caso, corríjame :)

Eso solo lo convierte en una situación extraña: tiene que haber compatibilidad con versiones anteriores, pero en la práctica esto parece difícil de lograr y de todos modos una pista falsa. En mi humilde opinión, si eso se aborda primero, el resultado de eso se puede usar para tomar decisiones adecuadas sobre qué hacer con las API de .NET core, es decir, mejorarlas en lugar de arrastrar el viejo cruft mal diseñado de .NET por completo. Eso no quiere decir que todas las API estén mal diseñadas, sin embargo, con ADO.NET (como describí en una publicación anterior), las cosas no se han hecho correctamente durante muchos años. Es preferible hacer una ruptura clara y, en su lugar, implementar un sistema que pueda ser más robusto (JDBC todavía está funcionando bien, ODBC todavía funciona como lo hacía hace 25 años) y que se adapta al cambio es preferible.

Pensando un poco más en esto, el comentario de @FransBouma sobre el requisito de compatibilidad con versiones anteriores tiene mucho sentido. Una de las promesas de .NET Core son las iteraciones más rápidas gracias al empaquetado de Nuget en componentes; en lugar de las actualizaciones de .NET Framework que vienen una o dos veces al año, las actualizaciones de .NET Core se pueden lanzar cuando sea necesario. ¿Parece que el valor de esto es muy limitado si las actualizaciones nunca pueden romper la compatibilidad con versiones anteriores de .NET Framework ...?

Entiendo que la compatibilidad con versiones anteriores es un requisito que va mucho más allá de esta discusión específica de ADO.NET, me pregunto.

Es al revés con @roji : las iteraciones cortas se habilitan al no romper las API.

Si eres un desarrollador de .Net y tu compilación se rompe cada semana porque la API del marco subyacente cambia constantemente, no pasará mucho tiempo antes de que comiences a considerar otra plataforma.

Por lo tanto, es una iteración rápida impulsada por cambios inquebrantables.

(editado)
Solo puede iterar sin romper los cambios hasta que tenga que, por ejemplo, agregar algo a la interfaz de una clase, un cambio de comportamiento (!) O una adición de comportamiento, que no es un cambio de ruptura en el sentido literal (el cambio de comportamiento lo es), pero usándolo en .net core hará que su código no sea compatible con .net full, por lo que, en ese sentido, hará que .net core ya no sea compatible con .net full _para ese fragmento de código_. Lo que en mi humilde opinión se reduce a .NET core siempre tendrá las mismas interfaces (de clase) y comportamiento que en .NET completo, hasta el último byte (que en mi humilde opinión es insostenible) o obtendrá nuevas características que se actualizarán más tarde (que es literalmente lo que MS dijo por cierto) a .NET completo, lo que efectivamente lo hace no compatible con versiones anteriores. Depende de qué lado de la valla se encuentre, por supuesto: si dice: 'hay un conjunto común de interfaces (de clase) X con comportamiento definido B, y tanto .NET core como .NET implementan completamente estos y X & B ganaron' t cambiar en el futuro ', todavía significará que hay un nuevo marco fuera de X & B que obtendrá cosas nuevas y que es precisamente donde las cosas pueden cambiar y también donde está el futuro.

Se podría ir muy lejos con esto, por ejemplo, que las interfaces / clases utilizadas en X & B en .net core son en realidad envoltorios alrededor de las nuevas clases / interfaces en .NET core. Luego, depende del desarrollador que los use usar X & B o los nuevos con un mejor diseño y sin una API en común con .NET completo.

Como ya tenemos que definir nuestro camino para evitar las cosas que faltan en X y B, al admitir ambos marcos, en mi humilde opinión, es preferible simplemente poder apuntar a nuevas interfaces / clases en el núcleo de .NET a medida que tarde o temprano cambie el comportamiento (tal vez incluso muy sutil , como una excepción diferente en una situación dada) surgirá allí de todos modos, por lo que 'portar' el código al núcleo .NET con su _new_ API (no X&B) sería mejor. Tenemos que virar hacia el lado de babor de todos modos, al menos así es como yo lo veo.

@ryanbnl , estoy de acuerdo con @FransBouma. El punto no es que queramos poder romper las API. Es que restringir .NET Core para que se pueda ejecutar dentro de .NET Framework significa que nunca podrá agregar nada a ninguna interfaz de .NET Core, o agregar un miembro abstracto a cualquier clase base abstracta. Estos dos cambios realmente no rompen la compatibilidad con versiones anteriores en ningún sentido real para alguien que usa .NET Core, sino que rompen la compatibilidad con .NET Framework.

@roji a menos que sea un nuevo paquete externo de framework (por ejemplo, no GAC) cuando puede ser compatible tanto con Framework como con el núcleo y ejecutarse en su propia cadencia; pero eso podría ser incluso más cambios para los proveedores de ORM y el nombre System.Data.IDbConnection ya está tomado ...

@benaadams, ese es un comentario válido: solo tenía en mente las API de marco que no son nuget, como ADO.NET. Sin embargo, estos parecen cubrir suficiente espacio de API como para ser una preocupación: .NET Core no podrá evolucionar ninguno de estos (lea: agregar métodos de interfaz o métodos abstractos) sin volverse imposible de ejecutar

Sin embargo, no estoy seguro de a qué te refieres con IDbConnection ...

@roji solo se refería a un nuevo paquete que proporcionaba estos tipos, por ejemplo System.Data.Database ; para seguir siendo compatible tanto con el núcleo como con el completo, no podría redefinir ningún tipo que sería GAC ​​en el marco completo o entraría en conflicto.

En el nuget point; hay alguna razón por la que esto no podría vivir en nuget tanto para el pleno como para el núcleo; y desaprobar la publicación actual System.Data api 4.6.1+?

Causaría más dolor ahora; pero ya se está rompiendo algo de compatibilidad, donde se eliminan las interfaces, o solo se eliminan DataSet por lo que los proveedores de ORM ya deben realizar algunas modificaciones para coreclr.

Una API completamente nueva que vivía en nuget y fuera del marco de GAC podría ser compatible con versiones anteriores para su uso con netstandard1.2, por ejemplo, 4.5.2+, coreclr, UWP, mono / Xamarin, etc. probablemente sea un mejor momento que tarde.

Dado que las nuevas API están en juego para el achema y demás, lo que indica que DataSet no vendrá (o DataTable ), ¿debería cerrarse? Parece que las clases base son el camino a seguir en función de los comentarios en dotnet / corefx # 5609 y el reenvío de tipos, lo que significa que las interfaces no tienen uso dado GetSchemaTable() y algunas otras no están allí para traerlas para compatibilizarlas. ... ¿es justo decirlo?

¿Qué debería cerrarse? Si no podemos tener GetSchemaTable() debido a la dependencia de DataTable / DataSet, eso es una cosa, pero las interfaces aún deben restaurarse (sans GetSchema si es necesario) y facilitar la migración de bases de código existentes con profundas dependencias en ellas. Las interfaces que faltan son un bloqueador, todavía estoy esperando un lanzamiento con ellas antes de que podamos comenzar el trabajo para admitir dnx / core.

Estoy de acuerdo con @mythz , las interfaces son otro tema y muy importante. Quizás no para la mayoría de los usuarios, pero estos mismos usuarios usan código escrito por un grupo pequeño y ese código _es_ se basa en estas interfaces (así como en otras características importantes de ADO.NET que faltan, como DbProviderFactory).

Para ser honesto, con el impulso extremo hacia una etiqueta 'RTM', tengo pocas esperanzas de que obtengamos una API sólida con 1.0. Será como .NET 2.0 nuevamente: todos los errores cometidos por la primera versión serán corregidos.

@FransBouma

Agregar las interfaces a .NET Core sería un cambio aditivo. Entonces, incluso si no pasa el corte para V1, podría agregarse en cualquier versión siguiente, incluso 1.1.

Todos los errores cometidos por la primera versión serán corregidos.

No te ofendas, pero así es como funciona el software.

@terrajobst

Entonces, incluso si no pasa el corte para V1, podría agregarse en cualquier versión siguiente, incluso 1.1.

sí, no es que técnicamente no sea posible, es que los resultados de hacerlo (o la falta de hacerlo) tienen consecuencias (de gran alcance), de las cuales Microsoft no es el que sufre, somos nosotros. Hasta ahora he visto poca apatía por eso. Es genial y emocionante trabajar en nuevos marcos, pero no se desarrolla en una sala limpia para una nueva audiencia. Tampoco es el caso, no hay historia de la que aprender, al contrario.

No te ofendas, pero así es como funciona el software.

Mira, esto es exactamente lo que quiero decir arriba: no eres tú quien tiene que lidiar con las consecuencias de tus decisiones, tengo que hacerlo. Y ya me he ocupado de las consecuencias de una decisión similar de sus predecesores, así que le avisé para que no cometa el mismo error.

Sé cómo funciona el software, he sido un desarrollador de software profesional durante más de 21 años. Di mi consejo honesto, no como un novato, sino como un experto curtido en la batalla en esta área en particular. Puedes hacer lo que quieras con él, es solo que espero que te lo pienses dos veces antes de tomar una decisión a la ligera aquí, ya que las consecuencias son de largo alcance, y como dije: tenemos que lidiar con estos, no tú.

Incluso un error se puede corregir más tarde, pero aún no se ha cometido, es una buena razón para no hacerlo en primer lugar, ¿no es así?

Ustedes están exagerando. Dudo que los efectos de esto tengan las mismas consecuencias que tenía el antiguo .NET.

Con coreclr incluido en cada aplicación, el legado tiene un significado muy diferente. Al igual que a casi nadie le importa si las características de asp.net mvc 5 no se exportan a mvc 4 o 3. Legacy aquí tendrá un significado diferente, y hay antecedentes en otros proyectos para demostrarlo.

Menos mal que

@nvivo Por favor, no trates de minimizar las consecuencias con las que tengo que lidiar yo, ya que tú no tienes que lidiar con ellas, pero yo sí.

Gracias @FransBouma por ponerme en mi lugar. Fue mi error pensar que podía comentar sobre el tema. Ciertamente estás más calificado que yo para saber qué tipo de cosas afectan mi trabajo.

De hecho, aunque abrí el número, no tiene ningún efecto en mi trabajo o en las cosas que me importan. Estaba pensando en los desarrolladores pobres como tú que hacen todo el trabajo duro del planeta.

Estoy muy contento de que gente como usted esté aquí para solucionar los problemas difíciles. Por favor, no dude en decirnos una y otra vez y otra vez (y otra vez) cuántas cuestiones más importantes tiene que afrontar.

Gracias @FransBouma.

_sigh_ ¿Dónde digo todo eso? Todo lo que digo es que por favor no reste importancia a las cosas, ya que lo hace con 'estás exagerando', no creo que esté exagerando. Con 'no tienes que lidiar con ellos' quiero decir: las consecuencias para mí. Porque sé cuáles son, reacciono como lo hice. Aparentemente eso es 'exagerar'.

Pero lo que sea.

Tenemos respuestas para este problema aquí y aquí .

La respuesta oficial es: Las interfaces no estarán en .NET Core 1.0 y, aunque es poco probable, pueden considerarse para versiones futuras en una forma diferente a la que existía en .NET.

Estoy cerrando este problema ya que se abordó la pregunta original.

@nvivo Gracias, pero es mejor dejar las respuestas oficiales a las personas realmente responsables del proyecto que también son capaces de cerrar los problemas por sí mismos una vez que han decidido que se ha resuelto.

@terrajobst ¿Existe una respuesta oficial / cronología actualizada para las interfaces? y ¿cuál es la mejor manera de realizar un seguimiento de este elemento de trabajo en el futuro? ¿Deberíamos abrir una nueva edición o continuará proporcionando actualizaciones aquí?

Dejemos esto abierto por ahora. A mi modo de ver, la respuesta no fue "no expongamos las interfaces". La respuesta fue "busquemos una manera de exponerlos, pero pensemos en lo que significa para la dependencia de DataTable".

Perdón por volver con ustedes tan tarde. Después de discutir varias opciones dentro de nuestro equipo, decidimos traer de vuelta las interfaces con una clase vacía de DataTable. No es una solución ideal, pero dado el marco de tiempo de RTM, este enfoque garantizará que podamos buscar opciones viables en torno a DataTable / DataSet en el futuro. Intentaremos traer las interfaces para System.Data.Common por v1 RTM; SqlClient no implementará las interfaces por v1. Gracias por tus comentarios y tu paciencia. Sus comentarios son una parte clave para hacer que la pila de datos sea un producto viable.

@YoungGah gracias por la actualización, si las clases de DataTable van a ser marcadores de posición vacíos, ¿qué requiere tanto tiempo / esfuerzo (es decir, reteniéndolos) para que SqlClient v1 los implemente?

@mythz El costo está en implementar las interfaces en el tipo base / reenviarlas a los métodos existentes. El costo debería ser mínimo, pero por lo general aparecen las cosas: sonríe:

Hemos agregado las siguientes interfaces a .Net CoreFX en System.Data.Common

IDataParameter
IDataParameterCollection
IDataReader
IDataRecord
IDbCommand
IDbConnection
IDbDataParameter
IDbTransaction

Esto se hizo en el PR https://github.com/dotnet/corefx/pull/6359

@ saurabh500 ¡Buenas cosas, gracias!

: +1:

: +1:

Impresionante; ¿Hay un hito para que esto llegue a nuget? rc3?

El 25 de febrero de 2016 a las 02:54, Saurabh Singh [email protected]
escribió:

Hemos agregado las siguientes interfaces a .Net CoreFX en System.Data.Common

IDataParameter
IDataParameterCollection
IDataReader
IDataRecord
IDbCommand
IDbConnection
IDbDataParameter
IDbTransaction

Esto se hizo en PR dotnet / corefx # 6359 https://github.com/dotnet/corefx/pull/6359

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -188577701.

Saludos,

Bagazo

Como se comentó en el PR, necesitamos entender por qué no hay métodos asíncronos en estas interfaces. Como se propuso, esta es básicamente una versión reducida de las interfaces ADO.NET 1.1, y no creo que la idea deba ser solo la compatibilidad con el código antiguo.

Las interfaces deben enfocarse en el estado actual de ado.net, ya que los métodos asíncronos deben ser la forma predeterminada de acceder a cualquier base de datos en la actualidad. Sin soporte real para los métodos asíncronos, estas interfaces son inútiles para el desarrollo moderno.
desarrollo.

E incluso incluyendo los métodos asíncronos de .NET 4.5, también se deberían agregar algunos métodos adicionales, como DbTrabsaction.CommitAsync.

El proveedor de postgres agregó algunos métodos adicionales como CommitAsync a su api que son bastante útiles y necesarios.

Las interfaces actuales están bien como están. La implicación de cambiarlos es demasiado grande.

El modelo asíncrono es bastante diferente del síncrono y, como sabrá, si opta por el modelo asíncrono, debe hacerlo hasta el final. Por lo tanto, realmente no hay ninguna razón para tener las mismas interfaces para ambas API. Cree nuevos para la API asíncrona.

Si el equipo de .NET desea proporcionar una API más moderna, ¿por qué no crear una nueva API que no se llame ADO.NET? Sin legado que obstaculizar y sin quejas de la comunidad. ¿Eso también encaja bien con la forma en que se distribuirá dnx? es decir, paquetes independientes.

: +1: en las interfaces, buen compromiso.

No creo que la idea deba ser solo compatibilidad con código antiguo.

Esa es la idea completa aquí. De lo contrario, las clases base estarían bien. Eso es mucho dolor de portar que queremos evitar.

Sin soporte real para métodos asíncronos, estas interfaces son inútiles para el desarrollo moderno.

No estoy de acuerdo con esto, pero no estoy en desacuerdo con una versión asíncrona de las interfaces necesariamente (que nadie implementa hoy). Esta sería una característica nueva. No podemos agregar miembros retroactivamente a las interfaces existentes, eso simplemente rompe demasiadas cosas. Tener un IDbReaderAsync o algo no es una locura en mi opinión, pero es una discusión diferente.

Creo firmemente que los métodos async _no_ deberían estar en las clases base, si la implementación predeterminada es un contenedor de sincronización, eso es activamente malo y un desperdicio. Si hay otra propuesta, que así sea, pero de nuevo: ese debería ser un tema diferente de todos modos.

Ok, tal vez me expresé de la manera incorrecta aquí o fui demasiado fuerte con mis palabras.

Estoy a favor de una interfaz adicional para async si es necesario. Con lo que no estoy de acuerdo es con tener algo que defina un contrato oficial para ADO.NET (eso es lo que son las interfaces) pero no tiene métodos asíncronos en ninguna parte.

Pero entonces, tener interfaces alternativas para métodos asíncronos probablemente causaría otros problemas ...

Creo firmemente que los métodos asíncronos no deberían estar en las clases base, si la implementación predeterminada es un contenedor de sincronización, eso es activamente malo y un desperdicio.

Estoy de acuerdo, esta es la razón principal por la que la mayoría de los proveedores no se molestan en implementar una API asíncrona real. Pero cambiar eso rompería mucho más código y probablemente causaría mucho más ruido que eliminar interfaces, ya que las clases base han sido la API real para los proveedores desde 2.0.

Actualizar una biblioteca para no usar ninguna de las interfaces 1.1 causaría un impacto casi nulo en comparación con eliminar todo el código asincrónico escrito en los últimos años, eso sería desastroso. El compromiso es tener ambos. Cualquier código escrito hoy debería usar apis asíncronas, por lo que dejarlo fuera no tiene ningún sentido.

Cualquier código escrito hoy debería usar API asíncronas.

No quiero herir demasiado, pero ese mundo ideal está muy lejos de la realidad. Async es muy generalizado e infeccioso. Simplemente no puede confiar solo en las API asíncronas en las bibliotecas y esperar que las aplicaciones completas sean consumidores asíncronos (cambiando una _ton_ de su código para que también sea asíncrono) por capricho. Sync -> Async en todas partes también es muy malo por muchas razones de interbloqueo y eficiencia. Habrá código sincrónico escrito durante muchos años.

Existe una gran necesidad de ambas API. El punto es: no eliminemos los actuales ni demoremos su presencia para un nuevo conjunto hipotético y aún no diseñado. Podemos preocuparnos por el segundo / nuevo conjunto de forma independiente.

Actualizar una biblioteca para no usar ninguna de las interfaces 1.1 causaría un impacto casi nulo en comparación con la eliminación de todo el código asíncrono escrito en los últimos años

¿A qué te refieres? No ha habido API asíncronas para que exista tal código. Si confía en dichas API, no se encuentran en una clase base o una interfaz, sino directamente en un proveedor. Eso no se verá afectado por esto.

Cualquier código escrito hoy debería usar apis asíncronas, por lo que dejarlo fuera no tiene ningún sentido.

Dejar de lado muchas cosas no tiene sentido ... excepto por el hecho de que todos estamos limitados por los recursos (especialmente el tiempo). No creo que nadie se haya olvidado nada de forma permanente. Nada está fuera de la mesa. Simplemente no se ha logrado todavía. Abriría otro problema específicamente para comenzar una especificación en interfaces asíncronas para una generación futura.

¿A qué te refieres? No ha habido API asíncronas para que exista tal código. Si confía en dichas API, no se encuentran en una clase base o una interfaz, sino directamente en un proveedor. Eso no se verá afectado por esto.

.NET 4.5 introdujo métodos asíncronos en las clases base del proveedor. Esto fue en 2012, hace casi 4 años, por lo que ha sido parte de la API del proveedor de ADO.NET durante un tiempo. Entity Framework 6 (lanzado en 2013) depende de estas API asíncronas para todos los proveedores.

Los métodos asíncronos ya forman parte de ADO.NET durante el tiempo suficiente para que mucha gente grite si no se incluye en .NET Core. Tengo _código heredado_ que usa métodos asíncronos en ADO.NET.

Estoy defendiendo que, dado que ya son parte de ADO.NET, esto también debería estar presente en la nueva API de interfaz.

Si las personas quieren (y deberían) utilizar las API asíncronas, ya pueden hacerlo
que _antes de este cambio_ utilizando los tipos base. En última instancia, la solicitud
para soportar las interfaces se hizo por razones de compatibilidad con versiones anteriores;
agregar métodos a una interfaz _ saca completamente esto del agua_.
Dicho esto, en realidad es casi posible como _métodos de extensión_ y
comprobación de tipos contra los tipos base abstractos, pero ... bastante feo y no
vale la pena el dolor en mi opinión.

Entonces; versión corta: personalmente no puedo respaldar la adición de async al
interfaces, ya que eso destruye lo único que queríamos en la primera
lugar. Si desea async: necesita codificar contra las clases base, o usar
herramientas que pasan por alto estos detalles por usted.

Estoy defendiendo que, dado que ya forman parte de ADO.NET, esto también debería estar presente en la nueva API de interfaz.

Está malinterpretando por completo el propósito de estas interfaces ADO.NET, que es mantener la compatibilidad con el código existente. Estas no son interfaces _nuevas_, son interfaces _existentes_. Si desea acceder a las API más nuevas, consulte los tipos de base concretos.

@nvivo Disculpas, simplemente no te estoy siguiendo, estaba hablando de las API de _interface_, esas nunca han existido. Los tipos base ya tienen los mismos métodos *Async ¿Falta algo específico? Creo que estás argumentando que deberían integrarse en interfaces ... sí, claro, pero ese es otro tema que te animo a que abras.

Preferiría que hayan sido una interfaz desde el principio, ya que las implementaciones básicas necesarias para que el enfoque actual funcione (asíncrono sobre sincronización) son compensaciones terribles para que todo el enfoque funcione. Pero tampoco podemos tener las dos cosas: o se mueven a las interfaces o están presentes (como es el caso actual) para minimizar las interrupciones.

Sí, creo que estamos dando vueltas aquí. Dije esto antes, no creo que las interfaces deban agregarse _solo_ para ayudar a portar el código. Desde el punto de vista de la compatibilidad, las clases base han sido la API oficial de ADO.NET desde 2005, y eso es lo que implementan los proveedores. Cualquier cosa que use un IDbCommand o IDbConnection podría ser portado fácilmente (y debería haber sido portado) para usar clases base con una búsqueda / reemplazo y no tener inconvenientes.

Sé que no eres fanático de los ifdefs, pero admitir esto para una nueva plataforma solo será parte de la migración de todos modos.

Estoy de acuerdo en que esto debería haber sido interfaces todo el tiempo, pero como no lo fueron, me gustaría que este problema no se repita. Si se están agregando interfaces, al menos deberían representar la API actual, y no lo que eran hace una década. Los métodos asincrónicos son una parte integral de la API actual y esa es la dirección en la que se está moviendo Microsoft desde hace bastante tiempo. Aún sería compatible con la fuente, solo que más completo.

@mgravell

Si las personas quieren (y deberían) usar las API asíncronas, ya pueden hacerlo _antes de este cambio_ utilizando los tipos base.

No se trata de poder hacer nada. Se trata de arquitectura. Las interfaces son contratos, .NET Core es un nuevo marco que agrega este contrato a una versión rediseñada de la API.

.NET core no debería agregar un contrato oficial a una nueva API solo para ayudar a migrar código realmente antiguo, mientras que la mayoría de las otras cosas faltarán de todos modos. Si eso es una preocupación, las personas simplemente no están buscando lo suficiente por las razones por las que necesitarán cambiar su código de todos modos.

Si eso es todo lo que está haciendo el equipo, entonces está bien. En mi opinión, es una mala elección.

Cualquier cosa que use un IDbCommand o IDbConnection podría ser portado fácilmente (y debería haber sido portado) para usar clases base con una búsqueda / reemplazo y no tener inconvenientes.

Falso. Los problemas se han discutido en numerosas ocasiones en este hilo de varios autores de bibliotecas con experiencia de primera mano afectada por esto.

Sé que no eres fanático de los ifdefs

Cualquier solución que requiera que los clientes finales usen ifdefs es una experiencia de desarrollo defectuosa y no iniciadora, es decir, nunca habrá un producto exitoso que requiera que los clientes llenen su código con #defs cuando existan alternativas.

Si se agregan interfaces, al menos deben representar la API actual

Estas no son interfaces nuevas, son interfaces restauradas. Las API actuales y futuras son las clases base, no estas interfaces. No debería haber ningún problema aquí, puede olvidar que estas interfaces existen y continuar usando los tipos base como antes de que se restauraran estas interfaces.

Ya no se agrega ningún valor nuevo a este hilo. Las interfaces ADO.NET existentes se han restaurado para que este hilo se pueda poner en reposo. Lo único que se necesita de este hilo son actualizaciones a DataTable y GetSchemaTable() ya que pertenecen a las interfaces existentes. Si desea proponer cambios en la arquitectura o abogar por nuevas interfaces, abra un nuevo problema, que evitará que todos los de esta lista reciban spam.

@mythz , aceptemos estar en desacuerdo.

Solo agregando mis 2 centavos como otro desarrollador de ORM, las clases abstractas siempre son un olor a código cuando no están respaldadas por una interfaz. Me encantaría ver nuevas interfaces proporcionadas para que coincidan con las clases abstractas y las firmas de métodos sobrecargadas con una API de interfaz de requisito mínimo.

Felicitaciones a la comunidad por hablar.

las clases abstractas son siempre un olor a código cuando no están respaldadas por una interfaz

@psibernetic ¿Puedes ayudarme a entender esa afirmación? ¿Qué pasa con esto es un olor a código?

@psibernetic

Las interfaces y las clases abstractas nos dan contrato, ambas nos dan una abstracción y una buena definición para la API. Las interfaces son más útiles cuando se implementan clases que podrían implementar más de una interfaz o son subclases de otra clase base (asumiendo que es una gran ventaja de esa clase base). En este caso en particular, las clases concretas para Connection, Command, etc. para proveedores específicos tienen una fuerte relación IS A con las definiciones abstractas de API. Realmente no puedo imaginar un escenario en el que algún desarrollador necesite agregar una implementación concreta para IDbConnection o IConnection a una subclase. El escenario casi único serán nuevas clases que solo derivan para la clase abstracta y "duplicar" la misma definición en una interfaz es más trabajo (innecesario) para el diseñador de API.

¿Ve una ventaja o escenario específico y concreto para tener dos abstracciones iguales? ¿Cuándo la interfaz proporciona un beneficio práctico y real sobre la clase abstracta en este diseño de API específico?

La única ventaja que se me ocurre para las interfaces es la compatibilidad con versiones anteriores que necesitamos con las antiguas para romper el código de ejecución menos real que depende de esas interfaces. Si no tuviéramos las interfaces antiguas, estoy bastante seguro de que las clases abstractas serán suficientes.

@eocampo Tienes razón en que las clases abstractas probablemente proporcionan abstracción y contratos "suficientemente buenos". Siempre trato de proporcionar interfaces muy estrechas que representan acciones que se pueden realizar, como IAsyncCommand y similares. Eso permite que mis marcos se conecten de maneras que pueden no haber sido consideradas en el momento del diseño del marco con menos posibilidades de terribles NotSupportedExceptions o NotImplementedExceptions.

@davkean El olor del código es que en la mayoría de los casos, aunque no en todos, está requiriendo que un implementador implemente o herede un conjunto básico completo de funcionalidades que pueden no ser relevantes. Recuerdo haber visto implementaciones de IDataReader que leen desde un caché o en la memoria. No estoy seguro de si la clase abstracta DbDataReader lo permitiría, pero el nombre implica que no.

El modelo de mejores prácticas seguido predominantemente en dot net ha sido exponer interfaces y heredar de las clases base, ¿no es así?

El modelo de mejores prácticas seguido predominantemente en dot net ha sido exponer interfaces y heredar de las clases base, ¿no es así?

@psibernetic Bueno, no siempre. Por ejemplo, esta recomendación en el sitio de MSDN tiene más de una década allí. Y esa pauta es muy común en .Net Framework 2.0 al menos.

Además, esta es una buena referencia de las pautas para el diseño de bibliotecas en .Net desde los primeros días:

http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613

De todos modos, creo que la discusión difícil aquí se trata de dos temas ahora:

a) Las interfaces son solo para compatibilidad con versiones anteriores o podemos "comenzar desde cero" (código de ruptura) para permitir una interfaz y un diseño de API más limpios.
b) Cuánto podemos apostar por un diseño moderno y limpio a costa de no ser compatible con el framework .Net completo. (Compatibilidad específicamente entre .Net Core y Full core en el acceso a datos [no es el nivel más bajo y compatibilidad obligatoria])

Desde mi perspectiva, si tenemos las clases base abstractas como contrato principal y preferido, entonces las _interfaces_ deben coincidir con las antiguas solo por compatibilidad. Entiendo que @nvivo ya ha declarado que después de .Net 2.0 el contrato oficial eran las clases base abstractas, por lo que _podríamos_ pensar que las interfaces no resolverán el problema de compatibilidad, pero @mythz y @mikeobrien también proporcionaron datos

Para dejar de enviar spam y discutir los temas aquí tendremos que volver a leer esta larga conversación y no sé si podemos ponernos de acuerdo en la LISTA de temas específicos que estamos abordando o si es una buena idea crear dos o tres nuevos. cuestiones para cada tema específico. Soy más de la primera sugerencia porque hay muchos puntos buenos aquí. No tengo una buena idea de cómo podemos resumir todo esto y eliminar algo de ruido (incluso el mío).

Hablando de interfaces, ¿hay planes para finalmente hacer genéricas partes de System.Data? Siempre me ha molestado que System.Data nunca haya actualizado su API más allá de .NET 1.1, lo que deja a las personas con la necesidad de usar hacks como el método de extensión .AsEnumerable () para obtener un IEnumerablede un DataTable. ¿Por qué no se han actualizado colecciones como DataRowCollection para implementar las interfaces genéricas cuando todo lo demás en el marco lo hizo cuando salió 2.0?

¿Habrá un código auxiliar System.Data con redirecciones de tipo en él? Necesito usar ODP.NET pero ahora no puedo.

Creado dotnet / corefx # 7874

@mgravell @ploeh "Rickasaurus" implicaba que las clases de tipos estaban en el horizonte (para F # al menos, no estoy seguro de C # o .NET en general https://news.ycombinator.com/threads?id=Rickasaurus). Si es el caso de que vienen para todo .NET, ¿resolvería el problema?

No soy un experto en Haskell, pero tengo entendido que le permitirían utilizar un simple IDbConnection , IDbConnectionAsync y cualquier interfaz compartida futura después del hecho sin romper la fuente o compatibilidad binaria y sin obligar a terceros a implementarlo todo. Esto, sin dejar de ser fácil de burlarse.

¿Es este un entendimiento correcto? Si es así, ¿hay alguna posibilidad de que esta característica llegue a .NET de verdad?

¿Fue útil esta página
0 / 5 - 0 calificaciones