Runtime: Faça interfaces como a API oficial do provedor ADO.NET em vez de classes

Criado em 26 set. 2015  ·  174Comentários  ·  Fonte: dotnet/runtime

Pelo que posso ver atualmente na página corefx-progress para System.Data.Common , as interfaces (IDbCommand, IDbConnection, etc) foram removidas em favor do uso de classes abstratas.

Mas na nova API, a maioria dos métodos principais não são virtuais ou abstratos. Somente no DbCommand podemos ver isso:

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();

Embora esses métodos possam certamente ser virtuais ou abstratos, seria muito mais útil ter as interfaces reais de volta e fazer qualquer API pública depender dessas interfaces em vez das classes abstratas.

Isso é muito útil ao desenvolver bibliotecas. Hoje é muito difícil simular um datareader para fazê-lo retornar um valor específico para fins de teste. O mesmo para garantir que ExecuteReaderAsync foi chamado, não ExecuteReader, etc.

Proponho que a fábrica do provedor, em vez disso, seja feita como uma interface:

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

E então siga daí para o resto do provedor para coisas como IDbDataReader , IDbTransaction , etc.

Sabemos que as interfaces ficaram fora de sincronia por algum motivo no passado e as classes abstratas tornaram-se a API oficial, mas isso não precisa mais ser o caso no corefx.

Observe que isso não significa remover o System.Data.Common de forma alguma, mas, em vez disso, fazer com que as classes Common implementem essas interfaces e você não usaria System.Data.Common a menos que esteja implementando o provedor. Os aplicativos dependeriam apenas das interfaces.

Considere isso para tornar a API mais testável no corefx 1.0.

Relacionado a discussões sobre dotnet / runtime # 14302 e dotnet / runtime # 15269.

area-System.Data

Comentários muito úteis

Não podemos adicionar membros a interfaces

Correto, e esse é um _bom_ recurso de interfaces. A preferência por classes básicas abstratas é a maneira mais segura de ajudar na entropia da API, em vez de combatê-la.

Embora você não tenha que seguir os princípios do OOD , sugiro que o faça ao criar APIs OO. Resumindo, o Princípio de Segregação de Interface (ISP) afirma que _nenhum cliente deve ser forçado a depender de métodos que não usa_.

Se você adicionar novos métodos a uma abstração existente, você violará automaticamente o ISP.

Você pode decidir que 'não precisa aderir ao SÓLIDO' porque você é a Microsoft e está trabalhando com o BCL, portanto, 'as regras normais não se aplicam' (não as citações reais; apenas parafraseando o normal contra-argumentos).

Tendo mantido alguns projetos de código aberto por 6 a 7 anos, na minha experiência é melhor manter as interfaces pequenas. Se você precisar adicionar novos recursos a uma abstração, introduza uma nova interface.

Todos 174 comentários

Eles teriam mudado para classes básicas abstratas porque as interfaces não podem ser versionadas.

Estou supondo que os métodos não virtuais chamam outro método que é virtual. Os métodos virtuais prejudicam o desempenho, então você não quer tornar as coisas virtuais desnecessariamente.

@JamesNK entendo. Mas com o .NET Core sendo uma nova API e a API ADO.NET bastante estável por quase uma década, você acha que essa ainda é uma preocupação válida? Além disso, falando sobre acesso ao banco de dados, meu palpite é que o custo dos métodos virtuais é menor que o custo do acesso ao banco de dados.

@NickCraver , @roji , @FransBouma já que vocês parecem ter interesse na API ADO.NET, tem algo a dizer sobre isso?

@YoungGah , vale a pena perseguir isso?

Estou supondo que os métodos não virtuais chamam outro método que é virtual. Os métodos virtuais prejudicam o desempenho, então você não quer tornar as coisas virtuais desnecessariamente.

No processo de execução de uma consulta em um banco de dados remoto e no processamento dos resultados, os nanossegundos perdidos em uma virtcall são insignificantes. Além disso, o ADO.NET usa este sistema desde o início (o mesmo acontece com muitas outras APIs em .NET) e ninguém reclamou que seu código de banco de dados é tão lento devido a chamadas de métodos virtuais;)

Posso ver métodos assíncronos em sua lista, então estou supondo que apenas alguns anos atrás o MS não conseguiu adicionar assíncronos ao IDbCommand. Quem sabe o que o amanhã trará que exigirá novos métodos ou propriedades.

As interfaces não são modificadas.

O desempenho é apenas um dos motivos para não tornar algo virtual. Reduzir a área de superfície para implantes, talvez? Vou deixar alguém da MS dizer por que decidiu não fazê-lo, não sei muito sobre ADO.NET, então estaria apenas especulando.

@JamesNK Acho que suas preocupações são válidas, mas há 2 pontos importantes a serem considerados:

  1. O ADO.NET tem estado bastante estável desde .NET 2.0, que uma década - embora a API assíncrona tenha sido adicionada mais tarde, ela não mudou o comportamento da API, apenas adicionou contrapartes assíncronas - não vejo grandes mudanças em o paradigma do driver de banco de dados em breve
  2. Supõe-se que o CoreFx tenha uma ideia de versão diferente, já que você pode apenas manter o CLR anterior para aplicativos antigos. Portanto, os problemas de versão da interface não devem ter tanto impacto aqui

Considere também que mesmo um servidor sql em "localhost" gastará pelo menos alguns ms apenas para se conectar e retornar uma consulta vazia. Na prática, a maioria das consultas _rápidas_ aos bancos de dados relacionais leva cerca de 20 ms.

Ser capaz de simular a API com ferramentas padrão como NSubstitute ou Moq é muito mais valioso para o desenvolvedor hoje do que economizar microssegundos em pesquisas de método virtual.

Acho que não tenho uma opinião muito forte aqui, mas aqui estão algumas observações:

  • Concordar com o acima que remover virtual vs. não virtual é insignificante em uma API para acesso ao banco de dados
  • As classes base permitem que o ADO.NET forneça implementações, estou supondo que é disso que trata a maioria dos métodos não abstratos não virtuais - a sobrecarga de ExecuteReader que não aceita um CommandBehavior passa CommandBehavior.Default para a sobrecarga isso faz. Se você mudar para interfaces, cada provedor terá que implementar ExecuteReader () exatamente com o mesmo clichê ...
  • Não tenho certeza se isso é válido em todas as principais estruturas de simulação, mas pelo menos no Moq não é tão fácil simular uma classe base quanto uma interface?

Portanto, no geral, a ideia de eliminar as classes base ou as interfaces parece boa (mais simples). Como não vejo nenhuma vantagem nas interfaces (a menos que esteja errado sobre a facilidade de simulação) e as classes básicas podem fornecer funcionalidade comum (ou seja, os métodos não virtuais não abstratos), acho que a abordagem da Microsoft de descartar as interfaces parece bom para mim ...

Eu concordo com @roji em todos os seus pontos.

@roji, apenas uma nota, não estou propondo descartar as classes básicas, estou propondo adicionar as interfaces como a API padrão. As classes básicas ainda podem implementar o comportamento padrão.

Quanto aos testes, tive grandes problemas para testar se minha API estava chamando os métodos corretos. Para verificar se ExecuteDataReader recebeu os parâmetros corretos, por exemplo, você deve verificar outro método protegido que é chamado internamente com parâmetros diferentes. Isso está longe de ser ideal.

Atualmente, a menos que eu esteja enganado, a única estrutura que pode simular a API ADO.NET é a estrutura MS Fakes, que pode simular absolutamente qualquer coisa ao interceptar chamadas. Moq e outros não podem fazer isso.

Estou interessado em ver se outras pessoas tiveram problemas semelhantes.

@roji, apenas uma nota, não estou propondo descartar as classes básicas, estou propondo adicionar as interfaces como a API padrão. As classes básicas ainda podem implementar o comportamento padrão.

Desculpe, eu não entendi isso. Nesse caso, sua proposta não é mais ou menos manter as coisas como estão no .NET (não que haja algo de errado nisso)?

Quanto aos testes, tive grandes problemas para testar se minha API estava chamando os métodos corretos. Para verificar se ExecuteDataReader recebeu os parâmetros corretos, por exemplo, você deve verificar outro método protegido que é chamado internamente com parâmetros diferentes. Isso está longe de ser ideal.

Se eu entendi seu cenário (não tenho certeza), CallBase do Moq é útil para este tipo de cenário - implementações padrão são herdadas da classe base

@roji

sua proposta não é mais ou menos manter as coisas como estão no .NET (não que haja algo de errado com isso)?

Não exatamente. A interface API foi adicionada ao .NET 1.0 e obsoleta no 2.0. Desde 2.0, as interfaces existem para compatibilidade, mas não há interface para ProviderFactory ou outras classes em Data.Common. Também não há nada para a API assíncrona ou métodos 2.0 ou mais recentes.

Moq só pode zombar de coisas que podem ser zombadas. Deve haver algum método que seja virtual ou abstrato que ele possa substituir ou um método protegido que ele possa chamar. A API atual fornece método para alguns casos, mas não para a maioria deles. Existem muitas coisas que são internas, privadas e fora de alcance, a menos que você use reflexão. Apenas o MS Fakes pode fazer isso porque substitui a referência por um shim, mas isso só está disponível no VS Enterprise e é inútil para projetos de código aberto.

Parece que tenho um caso muito específico, mas certamente qualquer pessoa que já tentou zombar dessa API enfrentou esse problema. No google, quase toda solução termina com "simule a API de interface legada ou crie um wrapper que você pode simular":

@nvivo OK, obrigado pelos detalhes extras - eu admito que não fui muito longe com a simulação do ADO.NET.

O que eu não entendo é por que você iria querer simular métodos internos, privados e fora de alcance de uma API. Você não deveria zombar de métodos públicos que estão diretamente disponíveis para o código do seu próprio aplicativo (que é o que você está tentando testar)? Eu vejo o problema com os métodos não virtuais (por exemplo, a sobrecarga ExecuteReader () do parâmetro 0), mas dado que no ADO.NET estes sempre (?) Chamam alguma sobrecarga virtual (por exemplo, ExecuteReader (CommandBehavior)), há um problema real aqui?

Apenas tentando entender o cenário do seu problema, você pode dar um exemplo simples?

@nvivo Atualmente, não temos planos de trazer interfaces devido ao problema de versão que já foi apontado por várias pessoas neste tópico. Um bom exemplo de interfaces ficando atrasadas é quando os métodos assíncronos e de streaming foram adicionados ao .NET Framework 4.5. Quando adicionamos esses novos recursos, examinamos cuidadosamente a extensão das interfaces. As opções que tínhamos na época eram fornecer InterfaceFooV2 ou interfaces separadas para asyn e streaming. Não queríamos adicionar InterfaceFooV2, pois podemos prever que desejaríamos adicionar mais APIs no futuro. Continuar adicionando interfaces separadas para cada nova funcionalidade seria confuso, pois elas não estão vinculadas às interfaces existentes.

@roji Tive casos em que desejo garantir que uma sobrecarga específica de ExecuteReader foi chamada, e não que "qualquer uma das sobrecargas". É o tipo de coisa que você tem apenas em bibliotecas, não no código do usuário.

@YoungGah obrigado pela informação. Estou fechando isso então.

As pessoas responsáveis ​​por esta mudança têm alguma ideia de seu efeito? As interfaces básicas do ADO.NET já existem há mais de uma década e o acesso a dados sendo o centro da maioria dos aplicativos de negócios, estou tendo problemas para imaginar como não quebrar propositalmente tantas bases de código existentes não é a maior prioridade? Essas são algumas das interfaces de alto nível mais críticas em .NET, removendo essas quebras de todas as bibliotecas de acesso a dados ADO .NET que existem e, por consequência, de todos os projetos que as usam. Removê-los cria uma fragmentação artificial, causando frustração e confusão que dificultará a adoção do CoreCLR.

Você ainda pode criar versões de interfaces e torná-las compatíveis com o código-fonte apenas adicionando métodos de extensão para qualquer nova API IDbCommand , por exemplo:

public interface IDbCommand
{
    //...
}

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

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

A interface principal IDbCommand nunca precisa ser alterada após o lançamento do DNX e você pode continuar adicionando funcionalidades com a estratégia acima. Você também pode posteriormente (em uma versão mais recente), agrupar essas extensões e mesclá-las na interface principal. De qualquer forma, obtemos as interfaces principais estáveis ​​do ADO.NET, essenciais para a migração das bases de código existentes e, consequentemente, para a adoção do CoreCLR.

@Davkean me pediu para fornecer exemplos concretos do impacto que a remoção das interfaces principais do ADO .NET terá. Não posso imaginar que essa mudança foi considerada sem avaliar o impacto incomensurável que teria no ecossistema .NET existente, mas, novamente, também foi feito, então há uma possibilidade de não ter sido considerada - o que vou assumir aqui- nele não era.

Apesar do papel da EF de ser o ORM padrão do .NET e de seu notável sucesso em capturar uma grande maioria de participação no mercado, ainda há uma grande população de desenvolvedores .NET que prefere usar ORMs alternativos por uma série de razões diferentes. Por exemplo, um recurso importante relacionado ao CoreCLR é que eles têm suporte de primeira classe rodando em Mono / Linux / OSX, bem como suportam vários RDBMSs alternativos. Como o CoreCLR está se voltando fortemente para o mercado de desenvolvedores Linux / OSX, quanto mais suporte houver para RDBMs alternativos, melhor. Outra característica importante da população de desenvolvedores que adotam Micro ORMs é que eles avaliaram fora dos padrões do ecossistema MS para escolher o ORM mais apropriado para eles. De tudo o que vi, há uma alta correlação entre devs .NET OSS (ou seja, anti-Dark Matter) ativos e devs que adotam Micro ORMs, da mesma forma, espero que isso tenha uma alta correlação com os primeiros a adotar o CoreCLR - cuja principal proposta de valor é para desenvolver em OSX / Linux. Essas são algumas das razões pelas quais seria benéfico incluir o ecossistema .NET circundante na sua tomada de decisão ao fazer escolhas fundamentais de design como esta.

Downloads ORM alternativos

Uma rápida olhada nos downloads do NuGet fornece uma indicação de como é a participação de mercado não EF:

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

Os números reais são muito mais do que isso, pois muitos Micro ORMs como Dapper, Massive, PetaPoco, NPoco, etc. foram projetados para caber em um único .cs drop-in, então o NuGet não está relatando seu uso real. Há também ORMs de código fechado como LLBLGen Pro, que têm uma grande base de usuários, mas seu uso não é relatado pelo NuGet, da mesma forma, tenho certeza de que perdi vários outros ORMs que esqueci / não sei.

Impacto para ORMs alternativos

Graças ao GitHub, podemos fazer uma pesquisa rápida para ver quantos arquivos de origem diferentes contêm o núcleo
Interfaces IDbConnection , IDbCommand e IDataReader ADO .NET afetadas por esta mudança:

IDbConnectionIDbCommandIDataReader
NHibernate59181132
Dapper172117
OrmLite1795426
Simple.Data29276
NPoco4103

Observação: esses resultados mostram apenas arquivos de origem, o número real de referências quebradas é muito maior.

Impacto no código-fonte do cliente

O impacto real dessa mudança também se estende a todas as dependências de projetos que usam esses ORMs.
Infelizmente, o efeito não se limita a implementações internas, pois também prejudica o cliente
código-fonte como muitos Micro ORMs são apenas métodos de extensão sobre interfaces ADO.NET para que o cliente
código se parece com:

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"));

Um recurso de extensibilidade do uso de métodos de extensão é que esses ORMs são "abertos" e os clientes podem estender ORMs com suas próprias APIs de primeira classe adicionando métodos de extensão em seus próprios projetos - eles também estão corrompidos.

Obviamente, qualquer código-fonte que passe IDbConnection agora também está proibido de funcionar no CoreCLR.

Por exemplo, as principais interfaces ADO.NET são amplamente utilizadas em estruturas de alto nível como ServiceStack, adotadas porque é a dependência mínima para permitir acesso a dados multi-RDBMS. Também foi assumido que, entre todas as classes que provavelmente não sofreriam alterações, seriam as interfaces básicas do ADO.NET.

Resumo

Pessoalmente, estou surpreso de que algum dia existisse um futuro no .NET sem essas interfaces.
As interfaces são específicas para o propósito do projeto para permitir várias implementações e ADO.NET é um
dos mais importantes "modelos de provedor aberto" em .NET. Não tenho ideia de quais prioridades fizeram com que essas interfaces fossem removidas, mas tanto as grandes bases de código .NET existentes que dependem dessas interfaces quanto o ecossistema EF .NET alternativo devem receber uma prioridade muito mais alta. Isso está causando uma interrupção significativa e é a principal barreira necessária para dar suporte às plataformas .NET 4.xe CoreCLR existentes, forçando uma quantidade não trivial de complexidade adicional que deve ser aplicada a todas as bases de código existentes afetadas por isso.

A percepção atual é que ADO.NET/CoreCLR está sendo redesenhado para fornecer suporte de primeira classe para EF e SQL Server com o resto do ecossistema sendo desconsiderado - decisões de quebra não transparentes como esta só servem para reforçar este estereótipo .

Como um membro anterior da equipe .NET (agora trabalho em Roslyn), estive fortemente envolvido no design original do novo comum de dados, junto com as equipes SQL e Entity Framework. Não estou envolvido nisso no momento, mas posso adicionar alguns antecedentes para ajudar a corrigir algumas das afirmações que estou vendo no Twitter e acima.

O design atual de System.Data.Common para .NET Core começou em dezembro de 2012, aproximadamente 2 anos antes de abrirmos o código.

Metas:

  • Projete uma área de superfície moderna para .NET Core, que reduza a duplicação de conceitos ( IDbConnection vs DbConnection ), confusões, erros e problemas de camadas (divida SqlClient de DataCommon, divida DataSet de abstrações de núcleo) de o design original do .NET 1.0. Um que seria facilmente escolhido, tanto por consumidores existentes quanto por _novos_ desenvolvedores do .NET Framework.
  • Permita que provedores e consumidores criem um único binário / fonte no .NET Core e, em seguida, execute esse mesmo binário no .NET Framework. Observe que o reverso não era uma meta; ser capaz de pegar o binário / fonte do .NET Framework e executá-lo sem algumas alterações no .NET Core.

Correção de algumas coisas espalhadas:

  • As interfaces como estão não são versionáveis. Não podemos adicionar membros às interfaces, e a proposta acima fornecida por @mythz por meio de métodos de extensão exige que os provedores derivem das classes base abstratas de qualquer maneira.
  • System.Data.Common _não_ mudou do modelo de provedor. As interfaces foram removidas porque eram um conceito legado do .NET 1.0 que foi substituído / duplicado pelas classes básicas abstratas introduzidas no .NET 2.0. No momento em que tomamos essa decisão, todos os provedores que encontramos derivavam das classes básicas.
  • Como as interfaces, as classes básicas podem ser simuladas.
  • Nós entendemos que seriam necessárias algumas mudanças para aqueles que usam as interfaces .NET 1.0, no entanto, é uma porta muito simples para mover para as classes básicas. Por exemplo, veja estas poucas linhas de mudança para AutoMapper: (https://github.com/AutoMapper/AutoMapper.Data/blob/master/AutoMapper.Data/DataReaderMapper.cs#L14).

Algo que estou tendo problemas para entender:

Não podemos adicionar membros a interfaces

Como ainda não é possível adicionar membros às interfaces CoreCLR, é possível retirá-los totalmente?

a proposta acima fornecida por @mythz via métodos de extensão requer que os provedores derivem das classes base abstratas de qualquer maneira.

O importante é que as interfaces existem e permitem a compilação do código-fonte que as referencia.

Se você não quiser criar versões das interfaces, multa o EOL delas, apenas restaure as interfaces como estavam antes de serem arrancadas e reduza a carga agora imposta a todas as outras bibliotecas que as usam. Quero dizer, essas interfaces centrais nunca ficaram obsoletas sem nenhum aviso ou caminho de migração fornecido. Ainda assim, estamos sendo punidos por adotar uma API publicada, conhecida e estável como parte integrante de nossas bibliotecas.

é uma porta muito simples para mover para as classes básicas.

Isso precisa ser adicionado a cada arquivo de origem que faz referência às interfaces do ADO.NET e força os clientes a sujar seu código com símbolos de construção personalizados.

Não parece haver o mesmo cuidado com a compatibilidade com versões anteriores aqui, mas interromper propositalmente os clientes existentes em uma versão futura simplesmente não é uma opção (estou surpreso que seja considerado com a participação de mercado muito maior do ADO .NET). Não podemos quebrar os clientes 4.x existentes e, no entanto, estamos sendo solicitados a oferecer suporte ao CoreCLR - então onde isso deixa as bibliotecas 4.x existentes que desejam manter a compatibilidade com versões anteriores existentes e também dar suporte ao CoreCLR? Devemos duplicar documentos / exemplos também?

Como ainda não é possível adicionar membros às interfaces CoreCLR, é possível retirá-los totalmente?

A área de superfície no .NET Core precisa ser binária compatível com o .NET Framework para permitir que os próprios e terceiros construam no .NET Core e executem a portabilidade sem alterações no .NET Framework. Adicionar membros a interfaces viola isso, pois os consumidores desses membros falhariam ao executar no .NET Framework.

Não estou defendendo a remoção ou adição dessas interfaces, eu só queria adicionar algumas informações sobre por que o design acabou onde está. Vou deixar os proprietários atuais, incluindo @YoungGah e @ saurabh500, resolverem isso.

Para resumir o thread, o motivo pelo qual você acredita que a Microsoft deveria portar essas interfaces é permitir que o ecossistema seja facilmente portado para o .NET Core, enquanto mantém suas implementações do .NET Framework?

é permitir que o ecossistema seja facilmente transportado para o .NET Core, enquanto mantém suas implementações do .NET Framework?

sim.

Para resumir o thread, o motivo pelo qual você acredita que a Microsoft deveria portar essas interfaces é permitir que o ecossistema seja facilmente portado para o .NET Core, enquanto mantém suas implementações do .NET Framework?

sim. APIs externas agora são quebradas se eu portar minha base de código (LLBLGen Pro) para corefx: eu então tenho que expor 2 apis ou quebrar a base de código existente para todos os meus usuários.

Pode ser bom para vocês quebrar nossas coisas, pois vocês não sentem a dor, nós sentimos. Não está tudo bem para mim: eu tenho que viver com uma base de código destruída e manter 2 APIs que fazem a mesma coisa, OU quebrar o código dos meus usuários porque você achou que estava tudo bem.

Eu também não entendo porque as interfaces não mudam, é apenas uma interface, como se uma classe também tivesse uma interface. O CoreFX pode perfeitamente adicionar métodos assíncronos às interfaces.

A área de superfície no .NET Core precisa ser binária compatível com o .NET Framework para permitir que os próprios e terceiros construam no .NET Core e executem a portabilidade sem alterações no .NET Framework. Adicionar membros a interfaces viola isso, pois os consumidores desses membros falhariam ao executar no .NET Framework.

Solução fácil: adicione as interfaces como estão agora. E quando todos vocês perceberem que essa regra acima é na verdade um tanto estúpida, você pode adicionar os métodos de que precisava para adicionar às interfaces há muito tempo às interfaces e seguir em frente.

Eu trabalho com software MS por tempo suficiente para que regras como a acima sejam ótimas no papel, mas na prática são quebradas assim que uma equipe importante de MS precisa ser quebrada. Se você é tão 'aberto' e 'diferente' como diz na publicidade / propaganda da CoreFX, mostre. Tudo o que vejo com respeito a System.Data e CoreFX é 'o que o MS precisa é feito, o que todo mundo precisa fica em segundo plano ou é ignorado'.

Outra coisa que esqueci de mencionar: Fowler mencionou ontem no Twitter que você precisa que todos portem suas coisas. Eu mesmo tenho que pagar para portar minha base de código LoC 500K para CoreFX, isso leva tempo, esforço e levará tempo para outros recursos. Atrito extra que é totalmente artificial (é uma nova plataforma! Como pode haver regras restritivas?) Realmente não ajuda em nada: adiciona custos extras de manutenção, leva mais tempo para portar o código e testar e dá uma carga extra para nossos usuários.

Tudo isso está fora do seu escopo e, ao que parece, não é da sua conta. Mas você se esquece de uma coisa: o que _se_ não portarmos nosso código e comigo mais pessoas? Estou disposto a investir tempo e, portanto, meu próprio dinheiro para portar minha grande base de código para seu novo framework brilhante, mas lamento dizê-lo, sempre que encontro um problema, encontro restrições, regras estranhas e debates intermináveis ​​que terminam em silêncio . Iow: Eu me sinto muito abandonado enquanto, ao mesmo tempo, você parece querer desesperadamente que gostemos de sua nova estrutura brilhante.

Como eu disse há muito tempo : Venda-me esse framework, esse novo CoreFX. Bem, manter o atrito e introduzir muitos queijos removidos e retirados não está criando um grande incentivo para investir uma grande quantidade de tempo (e dinheiro) nisso.

Apenas meus 2 centavos.

@FransBouma Vamos tentar manter essa conversa profissional, produtiva e focada em fatos.

Não estou argumentando a favor ou contra a adição de interfaces. No entanto, não é compatível adicionar métodos às interfaces. Vamos examinar isso:

1) Adicionar IDbConnection.OpenAsync ao .NET Core
2) Qualquer pessoa que chamar esse método, agora não conseguirá executar no .NET Framework (quebrando um princípio / objetivo básico que mencionei acima). Isso também quebra o designer XAML e alguns outros recursos do VS que dependem exatamente desse fato.
3) Para trazer o .NET Framework atualizado, enviamos uma nova versão do .NET Framework "4.7" com IDbConnection.OpenAsync
4) Cada tipo que implementou IDbConnection antes de adicionar este método agora falha ao carregar no .NET Framework "4.7"

É por isso que não podemos adicionar métodos às interfaces.

Se eu mantiver minha frustração com a forma como as coisas acontecem em relação à comunicação de problemas com a EM para mim mesmo, vocês não saberão sobre isso e pensarão que tudo são rosas e arco-íris. Se isso parece pouco profissional, que seja, estou além de me importar se MS pensa que sou um profissional ou não.

Dito isso: não sou casado com as interfaces, então, se elas se forem, o fato de que daí em diante não haverá aulas e nenhuma interface para trabalhar em teoria não me tornará um panda triste: o que deve ser feito pode ser feito em teoria por meio das classes base também, hoje, como hoje todos os principais provedores ADO.NET funcionam bem e derivam das classes base (este não era o caso no passado IIRC com ODP.NET implementando uma interface, mas não derivada da classe base). Esta também é a razão pela qual inicialmente acima, no início deste tópico, realmente não achei que removê-los fosse um grande negócio. Desde então, tive algum tempo para pensar sobre isso e acho que _é_ um grande negócio.

Não vivemos em um vácuo em Marte, e middleware / frameworks na parte inferior da pilha têm um problema agora: os usuários das versões completas .NET atuais desses frameworks querem continuar a usá-los no CoreFX como eles conhecem esses frameworks. Portá-los para o CoreFX é, no entanto, um grande PITA, por causa de uma miríade de razões, uma delas sendo interfaces frequentemente usadas expostas em APIs públicas que não estão presentes no CoreFX (e a razão para este tópico).

Só por esse motivo, gostaria de ver as interfaces de volta. Para mim, pessoalmente, não por razões técnicas (por exemplo, o async precisa de classes básicas, já é uma bagunça). Sei que faltam certos métodos, mas isso é problema seu , não meu. Removê-los torna isso meu problema e (parafraseando agora) a resposta do MS para isso é: jogue suas mãos para cima com "não pode ser feito!". Mas eu não tenho esse luxo. Você criou essa bagunça, você resolve. Você quer que eu faça a portabilidade do meu código, invista muito tempo e dinheiro (que eu mesmo tenho que pagar) para dar suporte ao seu novo framework, por que então você está criando _seu_ problema _meu_ problema?

Olhando para o seu cenário de 4 etapas: adicionar métodos às interfaces não é um problema SE você vir o CoreFX como uma estrutura separada. E não é esse o caso? É o mesmo que com o Compact Framework todos aqueles anos atrás (para o qual eu fiz a portabilidade de minha estrutura, e aprendi algumas lições difíceis que me dizem que a portabilidade para CoreFX não será simples, rápida e fácil e manterá duas bases de código também não): começamos com 1 API, então alguém esqueceu algo ou algum time dentro do MS precisa de algo, e viola uma mudança significativa em que apenas alguns desenvolvedores de pilha de baixo nível irão se deparar e assim por diante e os dois caminhos se dividirão.

(exemplo: Compact Framework esqueceu 'SerializableAttribute'. Eles adicionaram isso com um atributo fictício que não fazia nada em uma versão posterior, mas que quebrou o código que antecipou que ele não estava presente e que definiu o seu próprio)

Dividir estradas é compreensível: tentar manter as coisas compatíveis é muito restritivo. Prevejo aqui agora que essa regra será quebrada no futuro.

Ver as coisas como 'compatíveis' é importante não apenas no nível da assinatura da API, mas também no nível do _behavior_ da API. Confiar que esses dois serão completamente iguais (CoreFX e .NET Full) no comportamento da API é muito arriscado: um desenvolvedor de framework terá que testar a mesma funcionalidade no CoreFX e no .NET full, não há como testar sozinho o CoreFX o suficiente para assumir que o código funcionará 100% igual no .NET completo no futuro: porque como você pode garantir isso? Uma pilha de chamadas com 20 chamadas profundas no CoreFX afetou muitos outros códigos além do .NET full, um pequeno detalhe aqui e ali e as coisas mudam.

O ponto em tudo isso é: é uma estrutura separada: pode-se esperar que o código compilado no CoreFX seja diferente do código compilado no .NET full.

Existem algumas situações:

1) uma estrutura tem uma base de código 100% compilada no CoreFX. Isso fornece uma dll que pode ser executada em .NET full
2) uma estrutura tem uma base de código da qual 70% é compilada em CoreFX e 100% em .NET full. Isso dá 2 dlls: um para CoreFX e outro para .NET full. É uma tolice rodar a versão CoreFX no .NET full, pois faltam 30% da funcionalidade.

No caso de 1) Eu entendo seu ponto. No caso de 2) (que é o caso de todos os frameworks completos de segmentação .NET atuais, entre eles _todos_ ORMs de terceiros), seu ponto é realmente sem sentido, pois eles terão que trabalhar com 2 dlls de qualquer maneira: efetivamente 2 bases de código que precisam ser mantidos separadamente, testados separadamente e migrados para suas próprias novas versões separadamente. Especialmente se o CoreFX receber novos recursos que ainda não fazem parte do .NET full (o que será o caso). (aliás: se você adicionar DbDataReader.GetSchemaTable () ao CoreFX, que retorna uma estrutura de dados diferente de uma DataTable, porque o MS se recusa a portar isso, o código usando DbDataReader.GetSchemaTable no CoreFX vai quebrar no .NET completo também. Se você nomeá-lo de forma diferente ele irá quebrar assim que o método não estiver lá. Iow: o código irá quebrar se coisas que não estão em _both_ frameworks forem usadas. Isso não significa que as coisas, portanto, não deveriam estar presentes no CoreFX).

Não ter interfaces no CoreFX torna a situação do framework na situação 2) persistente: eles não podem se mover para se tornar um framework que se encaixa em 1) porque, por exemplo, sua API expõe as interfaces.

A Microsoft reescrever suas próprias coisas para que seus frameworks se tornem frameworks na situação 1) é legal, no entanto, não temos um orçamento de um milhão de $, mais de 15 pessoas no tempo de execução ORM e uma grande máquina de relações públicas do nosso lado que irá suavizar as rugas do quebrando todos os aplicativos que existem. Portanto, ou estamos presos em 2) ou precisamos de uma ajudinha de MS para passar para 1).

É isso que está em jogo aqui. Você disse no twitter "diga-nos o que você precisa". Nós fizemos. Repetidamente. Especialmente em relação a System.Data, não comunicação. Nada. Sem planos futuros, sem discussão sobre o que fazer, apenas becos sem saída e, às vezes, se uma pessoa com EM é alguém que não tem interesse real no assunto. Agradeço o seu tempo nisso, quanto mais histórico tivermos, melhor, mas ao mesmo tempo, é como conversar com um colega de trabalho sobre isso: não será resolvido porque a (s) pessoa (s) responsáveis ​​estão ausentes e não participando da discussão.

Se isso me faz parecer frustrado e Deus me livre de 'não profissional', então que seja.

Obrigado pela atenção. Btw, não tenho ilusões sobre System.Data: será um desastre de API para portar o código e como não há comunicação das pessoas responsáveis ​​com os desenvolvedores que escrevem frameworks importantes em cima de sua API, há pouca ou nenhuma esperança de que as coisas vai mudar. Não é sua culpa, @davkean , não é nada pessoal.

Devo repetir as frustrações acima sobre a falta de comunicação. Precisamos de inserções em massa e informações de esquema também. Não houve nenhum avanço ou comunicação em mais de um mês (consulte dotnet / runtime # 15269 e dotnet / runtime # 14302) dessas funcionalidades centrais ausentes (em ambos os sentidos). Ainda assim, a Microsoft está rotulando o código atual como "um candidato para lançamento", o que em si é uma mensagem de "é bom o suficiente". Não é. Faltam coisas essenciais que precisam ser adicionadas e, se você seguir esses tópicos, precisará estar na primeira versão por motivos de controle de

Veja a última atualização em dotnet / runtime # 14302 ("Por que DataTable / View / Set Absent?"), É de 22 dias atrás perguntando:

Então System.Data.Common está completo agora para V1 do CoreCLR?

Sim, a frustração pode parecer pouco profissional. O tom e o contexto do texto são uma merda e sempre foi, mas é a isso que estamos restritos aqui. Acho que todos estão tentando ser produtivos aqui, mas estamos ficando um pouco bloqueados do lado do CoreFX sobre o progresso real na área de System.Data e isto é, para ser franco, irritante tanto como autor de biblioteca e um usuário desses bits.

Precisamos dessas peças funcionais centrais, interfaces ou não - eu não sou muito rígido em interfaces e portamos o Dapper sem elas. Mas a falta de DataTable, informações de esquema de resultado, inserção em massa e outros são inaceitáveis ​​em um "candidato a lançamento". A Microsoft é quem está aumentando a frustração em rotular o código atual como RC, quando é quase universalmente aceito que não está pronto para lançamento. Sim, é apenas um rótulo, mas é um rótulo incorreto e aumenta drasticamente o nível de urgência porque é baseado em uma programação arbitrária (que deveria ter mudado para refletir a realidade). Não acho que ninguém neste tópico seja responsável por esse cronograma, mas vale a pena afirmar como um fator importante no _level_ da frustração.

Vamos voltar à raiz do problema. Precisamos dessas peças, e muitos de nossos milhões de usuários precisam. Então, vamos consertar.

Não vamos esquecer o NHibernate com mais de 1 milhão de downloads :

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

A percepção atual é que ADO.NET/CoreCLR está sendo redesenhado para fornecer suporte de primeira classe para EF e SQL Server com o resto do ecossistema sendo desconsiderado - decisões de quebra não transparentes como esta só servem para reforçar este estereótipo .

Essa percepção é reforçada por coisas como esta: https://github.com/dotnet/corefx/issues/4646

Pelo que eu posso dizer, não há nenhuma maneira de implementar essa API de qualquer maneira útil fora do assembly SqlClient.

No momento, estou bem com o teste sem interfaces. Mas, honestamente, não entendo o raciocínio com relação a versão e compatibilidade de interface.

A ideia do .NET Core não é que ele seja uma nova estrutura sem o fardo da compatibilidade e que esteja agrupado com o seu aplicativo, para que você não precise lidar com problemas como esse? O provedor já é incompatível com os do .NET devido à falta de coisas como esquemas e tabelas de dados, então, com o que quebraria a compatibilidade? Se a interface mudar, apenas compile com a nova versão e empacote-a com seu aplicativo.

Parece que a maioria das desculpas para o design são apenas preocupações da estrutura antiga que não são aplicáveis ​​à nova. De qualquer forma, vamos ver como fica na prática.

Para aqueles que pretendem oferecer suporte a vários frameworks e têm historicamente como alvo as interfaces ... Eu só quero compartilhar uma pilha de feios que Dapper usa; Não estou dizendo que isso seja _bom_, mas é o suficiente para fazê-lo compilar. Claro, ele está duplicado em uma enorme pilha de arquivos ... Estou compartilhando isso principalmente para enfatizar mais um dos impactos:

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

Não podemos adicionar membros a interfaces

Correto, e esse é um _bom_ recurso de interfaces. A preferência por classes básicas abstratas é a maneira mais segura de ajudar na entropia da API, em vez de combatê-la.

Embora você não tenha que seguir os princípios do OOD , sugiro que o faça ao criar APIs OO. Resumindo, o Princípio de Segregação de Interface (ISP) afirma que _nenhum cliente deve ser forçado a depender de métodos que não usa_.

Se você adicionar novos métodos a uma abstração existente, você violará automaticamente o ISP.

Você pode decidir que 'não precisa aderir ao SÓLIDO' porque você é a Microsoft e está trabalhando com o BCL, portanto, 'as regras normais não se aplicam' (não as citações reais; apenas parafraseando o normal contra-argumentos).

Tendo mantido alguns projetos de código aberto por 6 a 7 anos, na minha experiência é melhor manter as interfaces pequenas. Se você precisar adicionar novos recursos a uma abstração, introduza uma nova interface.

Se houvesse votos positivos aqui, eu teria votado a @ploeh +1000

Comentário perspicaz de @ploeh, como de costume.

@FransBouma , estaremos adicionando a funcionalidade de substituição de DbDataReader.GetSchemaTable () de uma forma que não quebrará a estrutura completa.
@NickCraver , SqlBulkCopy está em nosso plano futuro e estamos trabalhando no esquema. Demoramos para fazer progresso no esquema, pois também precisamos fazer um progresso razoável para fazer nossa pilha funcionar na plataforma x.
@mythz , obrigado por fornecer os exemplos, números e avaliação do impacto no cliente. Nós os revisaremos.

@YoungGah Por favor, atualize os problemas envolvidos com as informações para que esses problemas permaneçam atualizados, por exemplo, https://github.com/dotnet/corefx/issues/1039 , caso contrário, as informações esparsas estão espalhadas por toda parte. É bom que você adicione GetSchemaTable (e o equivalente de DbConnection, não se esqueça disso!), Mas é tão difícil obter qualquer informação sobre o que vai acontecer e _quando_. Existe algum plano sobre o que será adicionado quando? Tudo o que precisamos prosseguir agora é uma dica de que algo pode ser adicionado no "futuro". Isso não é muito bom para o planejamento de uma portabilidade de uma base de código, para ser honesto.

@FransBouma , sim, vou atualizar os outros tópicos também. Quanto ao seu pedido de mais informações sobre o que e quando estará disponível, entendo perfeitamente por que vocês precisam. Vou publicar a lista que indicará se o recurso / funcionalidade estará disponível na v1, removido propositalmente, estará disponível após a v1 ou o design da disponibilidade futura está pendente. Vou tentar postá-lo nas próximas 2 semanas. Quanto à função get schema em DbDataReader e função DbConnection, nosso plano é disponibilizá-la para o lançamento rc2. Se o plano mudar por algum motivo imprevisível, atualizaremos a comunidade.

O que quer que aconteça aqui e para referência futura @YoungGah; IDataReader tem uma dependência de DataTable, que tem uma dependência de DataSet (que consideramos uma camada separada - porque é pesado para políticas, ao contrário desses tipos que não têm políticas), então há algum trabalho de design aqui para quebrar isso se essas interfaces existissem trazer de volta.

Eu colocaria outro voto aqui para a abordagem de @ploeh ; têm interfaces, mas muito, muito mais refinadas do que a maioria das interfaces atualmente no BCL. Isso lida com os comentários de @davkean sobre desacoplamento e também com o controle de versão.

Por que você não pode simplesmente ter uma nova interface herdando a antiga. Obsoleto o antigo. Remova o antigo no futuro. Pelo menos então você pode estendê-lo e não interromper os usos existentes.

Ou várias interfaces menores. Eu só tenho que comentar.

Não entendo a necessidade de compatibilidade perfeita com o .NET original. Não há nada a ser quebrado aqui, esta é uma nova estrutura e a oportunidade perfeita para romper os laços com o código legado e aplicar alterações que são necessárias por um longo tempo, mas que prejudicariam na estrutura original.

Quando propus as interfaces de volta, não estava pensando em trazer as interfaces 1.1 originais, mas atualizar as interfaces com o novo design. Pode haver ainda mais deles, como disse @ploeh .

Interfaces sim, suporte legado se possível, mas não deve ser uma prioridade neste momento.

Reabrindo pois há muito interesse neste tópico.

Não há nada a ser quebrado aqui, esta é uma nova estrutura e a oportunidade perfeita para romper os laços com o código legado e aplicar alterações que são necessárias por um longo tempo, mas que prejudicariam na estrutura original.

Então, a oportunidade perfeita para se livrar das interfaces originais mal projetadas e padronizar em classes base como o ADO.NET já está fazendo? :cara de gozo:

Sério, você tem uma escolha entre uma API limpa ou compatibilidade com versões anteriores. Escolha um. Não vejo como ter seu bolo e comê-lo também.

@JamesNK, mas esse é exatamente o ponto. Compatibilidade com versões anteriores não é necessária, ponto final.

Você brinca, mas o mau design da API com interfaces era ruim porque eles eram mal projetados, não porque eram interfaces. =) Não é como se as interfaces não fossem usadas em todos os lugares da NET ou isso é algo novo. Apenas projete a coisa corretamente desta vez e siga em frente.

ADO.NET é uma das partes de código mais estáveis ​​em todo o .NET. Teve duas ou três mudanças sérias em 15 anos. É a API perfeita para estabilizar em uma interface e torná-la mais simples para todos.

Como uma nota, este tópico aqui é um dos assuntos mais comentados também, e teve a mesma longa discussão sobre interfaces vs métodos virtuais, testabilidade e outras coisas.

@nvivo Devo admitir que estou confuso. Depois de estabelecermos que as classes básicas ativavam a testabilidade, esse thread se transformou em trazer de volta as interfaces para permitir a portabilidade do código do .NET Framework para o .NET Core. Como redesenhar as interfaces e introduzir algo novo ajuda nisso?

Por que não podemos ter interfaces originais para compatibilidade com versões anteriores e seguir em frente com o que você optar (classes abstratas ou pequenas interfaces)? As interfaces originais podem ficar no topo da nova pilha e fornecer compatibilidade com versões anteriores. Esta parte também pode ser opcional.
Isso tornaria a portabilidade fácil e ainda permitiria um novo caminho.

@davkean

Não posso responder por todos que comentaram aqui. Propus usar interfaces como a API do ADO.NET, atualizado para a nova API atual. Não pedi para trazer as interfaces originais e todos os problemas que ela tinha. O objetivo era ter uma API definida mais limpa e tornar mais fácil simular e testar o código que depende dela, principalmente bibliotecas de abstração de dados e não código do usuário.

As interfaces são melhores para descrever APIs como @ploeh sabiamente dito, e muito mais fáceis de zombar. O design atual é péssimo em simulação e requer que você implemente quase todo o provedor como uma biblioteca para fazer testes simples. Se isso não for importante para todos, posso entender. Mas eu definitivamente não concordo que seja testável o suficiente hoje. Testar se um método A foi chamado com o parâmetro X, verificando se o método B chamado C com os parâmetros X, Y, Z não é ideal.

Agora, só para ver como as classes já estão criando um design ruim:

  • Por que DbCommand tem uma propriedade DesignTimeVisible ? O suporte em tempo de design é um requisito para que uma conexão seja definida como uma conexão?
  • Por que há um evento para notificar mudanças de estado, mas não notifica outras coisas, como comandos executados ou transações iniciadas? A notificação é mesmo um requisito para a existência de conexões ou algo que torna mais fácil construir UIs?
  • É um ConnectionStringBuilder um requisito para a existência de um provedor? Ou algo mais legal para fazer os assistentes do VS funcionarem imediatamente?
  • Por que DbDataReader define métodos Get para alguns tipos de núcleo, mas não adiciona para outros como GetByteArray() e GetDateTimeOffset() ? E a recuperação de um TextReader é mesmo necessária se isso puder ser feito fora, usando strings ou fluxos? Se fosse uma interface, métodos como esses seriam adicionados à API ou criados como métodos de extensão ou auxiliares nas classes concretas (como a família GetSql* em SqlDataReader)?

Todas essas são perguntas retóricas. Tenho certeza de que todos eles têm respostas convincentes e coisas foram consideradas. A questão é que o design atual claramente não é algo que foi pensado como uma definição de API , algo que provavelmente receberia mais atenção com interfaces.

Honestamente, do lado de fora, a discussão sobre o design parece que foi assim:

  • vamos manter esses eventos aqui porque é mais fácil para os assistentes do Visual Studio trabalharem imediatamente
  • vamos remover os métodos de recuperação de esquema porque temos EF e é isso que todos no mundo deveriam estar usando, ponto final.
  • vamos manter esses métodos de conveniência porque são suportados desde o .NET 1.1 e não podemos quebrar a compatibilidade NUNCA!
  • vamos remover datatables e datasets e fazer com que todos que vêm do .NET 1.1 alterem toda a sua base de código de qualquer maneira!
  • vamos construir um novo modelo de provedor focado em computação em nuvem, comunidade, código aberto e os aplicativos de amanhã ...
  • ... e vamos construir este modelo com base nas necessidades de ontem usando SqlClient como o caso de teste _sole_!
  • vamos construir um novo framework que será empacotado com cada aplicativo, então ninguém precisa se preocupar com atualizações quebrando seu aplicativo _nunca mais_!
  • .. então vamos tomar a decisão de não adicionar interfaces porque qualquer mudança pode interromper suas atualizações!

Sim, tem um pequeno discurso lá e essa discussão não vai a lugar nenhum =) ... Mas só queria tirar isso do meu peito. É 2015, quebra tudo o tempo e estamos acostumados. Haverá 20 atualizações para a ASP.NET MVC nos próximos anos que causarão muito mais mudanças significativas do que mudanças de interface no ADO.NET.

Ainda amo o .NET e o que você está fazendo com ele em geral, tenho certeza de que é uma corrida para lançar o .NET Core v1 a tempo e nem tudo será perfeito. Só espero que a comunidade possa ajudar a direcionar isso para outras direções com o passar do tempo e que você não tenha medo de quebrar as coisas enquanto avançamos.

Para mantenedores ORM, por que não fazer

`` `c #

se COREFX

namespace System.Data {
interface pública IDbConnection {...}
}
`` `

e usar o padrão do adaptador para envolver o novo System.Data com suas próprias implementações? Na verdade, você poderia fazer um pacote de código-fonte aberto para ele e compartilhar.

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 é o problema; em vez de uma abordagem considerada, estamos obtendo uma reescrita apressada para cumprir um prazo arbitrário.
Eu preferia ser tarde, no quarto trimestre de 16 se necessário, do que ter alguma porcaria quebrada novinha em folha. É 2015 e tudo quebra é uma justificativa terrível.

@thefringeninja que adiciona uma dependência completamente desnecessária e confusa que só funciona com metade dos sistemas (no caso compartilhado), ou leva a colisões de nomes que exigem extern alias para serem desmarcados (e muita confusão sobre o motivo do método um System.Data.IDbConnection não aceitará o diferente-mas-mesmo System.Data.IDbConnection que você está oferecendo). Basicamente, isso tornaria as coisas 10 vezes piores.

Você pode dar um exemplo concreto @mgravell ? Posso ver como isso funcionaria se você usasse Type.GetType("System.Data.IDbConnection, System.Data") ou talvez em cenários PCL.

Se orm A define System.Data.IDbConnection e orm B define
System.Data.IDbConnection, então agora existem dois completamente diferentes e
interfaces incompatíveis que têm o mesmo nome / namespace, conflito e
realmente não funciona a partir de nenhum dos provedores de banco de dados. Não resolve nada,
basicamente. Pior: aluga APIs inutilizáveis ​​onde alguém espera passar
em um SqlConnection ou NgpSqlConnection - e não funciona.

Na verdade, você poderia fazer um pacote de código-fonte aberto para ele e compartilhar.

Se não for System.Data.Common, isso significa que DbConnection não o implementa e: você também pode não se preocupar.

Você nunca obteria consenso para cada mantenedor do provedor ORM ou ADO .NET assumir uma dependência externa de terceiros (praticamente garantia que o SQL Server não) e 2 bibliotecas que redefinem uma interface BCL principal não podem ser usadas juntas. Pior ainda para estruturas de alto nível (como ServiceStack) que referenciam interfaces System.Data em bibliotecas centrais (que agora precisam ser definidas) não serão mais capazes de usar qualquer ORM que não referencie as mesmas interfaces - que não -um faria ou deveria.

A única maneira de garantir que todas as bibliotecas façam referência às mesmas interfaces System.Data é se elas forem restauradas com as classes base que as implementam - o que ainda não estou claro sobre os danos que isso tem.

@mgravell ah dependência transitiva, não tinha considerado isso. : +1:

Você sabe que não vejo por que isso é um problema, a menos que você esteja acoplando fortemente seu código a um código que não é seu. Proteja seu código de suas dependências! Embrulhe-o e abstraia-o. Há muitas maneiras de fazer isso e tornar seu código testável. Muitos são mencionados acima. Sua integração testa os bits que você não possui. É assim que funciona. Você não deve zombar de objetos BCL! Se estiver, seu design não é bom.

@nvivo Eu entendo que este é o seu problema original, mas a direção dele agora se transformou em um tópico sobre como trazer de volta as interfaces da era v1 por razões de compatibilidade. Vamos manter o foco nisso - se você gostaria de discutir como fazer alterações na área de superfície atual, registre novos problemas.

@mythz Há dois problemas que tivemos com as interfaces; 1) eles trouxeram uma (pesada) dependência do DataSet, que não pertence às abstrações livres de política e 2) eles trouxeram um conjunto paralelo de abstrações para as classes base, mas estão bloqueados com a área de superfície v1. Queríamos evitar essa confusão.

Eu concordo que não é viável para um terceiro fornecer essas interfaces - elas precisam ser implementadas nas abstrações principais para serem úteis.

Eu entendo que este é o seu problema original, mas a direção dele agora se transformou em um tópico sobre como trazer de volta as interfaces da era v1 por razões de compatibilidade. Vamos manter o foco nisso - se você gostaria de discutir como fazer alterações na área de superfície atual, registre novos problemas.

Isto não faz absolutamente nenhum sentido.

@nvivo significa que não é você que está com o problema, apesar de tê-lo arquivado - evidente por tê-lo fechado. O problema é restaurar as interfaces System.Data para aliviar a carga de suporte de todo o ecossistema que depende delas. Você parece estar bem com:

É 2015, quebra tudo o tempo e estamos acostumados.

Mas esta não é uma estratégia satisfatória para aqueles de nós que temos que suportar as bases de código existentes e as bases de código de nosso Cliente, e definitivamente não deve ser a estratégia padrão para custodiantes de bibliotecas BCL que afetam todo o .NET.

Mas esta não é uma estratégia satisfatória para aqueles de nós que temos que oferecer suporte a bases de código existentes

@mythz Isso está fora de contexto. Isso não foi o que eu quis dizer. Todo mundo aqui tem que oferecer suporte a bases de código existentes, eu duvido que haja algum recém-chegado ao .NET na discussão.

O problema com o que essa conversa se transformou é que não faz muito sentido. O .NET Core é uma nova estrutura, não uma atualização. Grande parte da API .NET completa existente não está lá e não estará. A compatibilidade com versões anteriores não funcionará assim de qualquer maneira.

@nvivo Esse sentimento exato é o motivo pelo qual esse problema não se aplica a você. Se você acha que a compatibilidade com versões anteriores não é importante, você nunca tentou oferecer suporte a uma base de código significativa voltada para várias plataformas - você também não está falando em nome da equipe CoreCLR. Essas bibliotecas não foram desenvolvidas do zero; se você leu acima, descobrirá que é um objetivo principal para as bibliotecas CoreCLR rodarem no .NET Framework completo. CoreCLR é outro alvo de plataforma para o qual portar bibliotecas existentes é vital para seu sucesso, algo que a equipe .NET está encorajando ativamente e que essas interfaces ausentes estão impedindo.

Com toda essa conversa sobre interfaces não serem compatíveis com versões, me faz pensar em como a linguagem de programação Go contorna esse problema com interfaces implícitas.

Pediram-me para expandir o que quero dizer com abstrações _sem políticas_.

Basicamente, sem política, quero dizer que as abstrações System.Data.Common contêm quase zero regras de negócios - tudo o que fazem é fornecer uma forma de APIs que um determinado provedor deve implementar. Isso contrasta com o DataSet, que possui muitas regras de negócios e códigos. Os tipos sem política, devido à sua própria construção, tendem a criar versões com menos frequência do que os tipos que contêm política, pois há menos código e, portanto, menos bugs e alterações de design. Queremos abstrações e tipos que são _trocados_ [1] entre bibliotecas de terceiros para versões com pouca freqüência [2] para reduzir o número de problemas que você encontra ao coordenar dependências em um gráfico de pacote grande. Você não pode incorporar ou ilmergir tipos de troca como parte de seu aplicativo, enquanto tipos de não troca você pode. Portanto, por que queremos que eles se separem.

[1] Por _exchanged_, quero dizer tipos que tendem a aparecer em APIs públicas. Por exemplo, consideramos Collection<T> um tipo de troca, mas não List<T> .
[2] O uso de versionamento semântico para estes causaria interrupções quase idênticas às anteriores com as interfaces removidas, porque você "bifurca" o ecossistema; as bibliotecas precisam tomar uma decisão se devem direcionar a biblioteca antes ou depois do intervalo, ou se bifurcam para lidar com a divisão.

@mythz Tenho que oferecer suporte a orms / clientes como parte de um aplicativo comercial e código existente ... Não estou dizendo que concordo com nenhum dos lados, mas o fato da questão é que dnx é um framework / runtime completamente novo. Se não tiver alguma interface, trate disso ... com uma diretiva do compilador.

Se não tiver alguma interface, trate disso ... com uma diretiva do compilador.

Oh? que tal um método que retorna IDataReader ou um método que aceita IDbConnection? Ou IDbTransaction? Como você vai #ifdev em torno disso, se seu código de _customers_ usa essa API?

'Lide com isso' ... que tipo de arrogância é essa?

Simples, atualize o pacote subjacente (sua organização) para retornar seu próprio tipo ou o tipo base que eles suportam desde 2.0. Se você está almejando versões mais antigas do .net, pode usar uma diretiva para retornar a interface e marcá-la como obsoleta.

Sim, é uma merda, mas tenho certeza de que eles (indivíduos realmente inteligentes que pensam nisso o tempo todo) fizeram isso por um bom motivo na época (de volta ao .net 2.0). Pode ocorrer uma conversa e talvez isso seja alterado com certeza ... Mas o fato da questão é que as pessoas atualizando para o novo tempo de execução terão que fazer algum trabalho. Não é clicar em um botão em uma interface do usuário sofisticada e ter trabalho para eles. Mas, ao mesmo tempo, concordo com você que atualizar para o novo framework deve ser mais fácil do que substituir um monte de tipos.

@FransBouma é provavelmente alguém que defende nomes fortes também.

@FransBouma @ phillip-haydon pode levá-lo a outros lugares, você não pode esperar agir assim e as pessoas o levarão a sério. Dê uma olhada nas minhas contribuições / projetos de código aberto envolvidos se você tiver alguma dúvida ... Eu terei que lidar com isso ... de qualquer maneira ...

Só para constar, sou contra a nomenclatura forte.

lide com isso...

E você diz que estou trollando?

" @FransBouma é provavelmente alguém que defende nomes fortes também." está fora do assunto e não é muito útil para esta palestra. Sim, parece que você está trollando ..

Um fato importante a ser considerado nesta discussão é que as interfaces IDb * foram descontinuadas como API para ADO.NET no .NET 2.0 uma década atrás, quando as classes básicas foram introduzidas. Eles não foram marcados como obsoletos, mas qualquer coisa construída desde aquela época depende das classes básicas. Provedores de app.config e suporte de string de conexão vêm à mente.

Se você tem código que depende dessas interfaces, você está codificando em uma API muito obsoleta, sem suporte para coisas como métodos assíncronos, o que significa que você precisará atualizá-lo de qualquer maneira se quiser que as pessoas continuem a usá-lo.

Simples, atualize o pacote subjacente (sua organização) para retornar seu próprio tipo ou o tipo base que eles suportam desde 2.0. Se você está almejando versões mais antigas do .net, pode usar uma diretiva para retornar a interface e marcá-la como obsoleta.

É uma API pública, usada por muitos milhares de aplicativos e a API é pública e está em uso desde .net 1.0. Não é 'simples', pelo contrário. Não podemos simplesmente mudar a API porque a Microsoft acha que é isso que eles devem fazer para tornar nossas vidas melhores: será um grande fardo para nossos usuários e, portanto, para nós.

Sim, é uma merda, mas tenho certeza de que eles (indivíduos realmente inteligentes que pensam nisso o tempo todo) fizeram isso por um bom motivo na época (de volta ao .net 2.0). Pode ocorrer uma conversa e talvez isso seja alterado com certeza ... Mas o fato da questão é que as pessoas atualizando para o novo tempo de execução terão que fazer algum trabalho. Não é clicar em um botão em uma interface do usuário sofisticada e ter trabalho para eles. Mas, ao mesmo tempo, concordo com você que atualizar para o novo framework deve ser mais fácil do que substituir um monte de tipos.

é isso: não é visto como um 'novo tempo de execução'. Se fosse esse o caso, como já discuti, não seria um problema. No entanto, a Microsoft tem a ideia de que as dlls direcionadas ao CoreFX também precisam funcionar no .NET full, portanto, não há uma nova estrutura. Para mantenedores de bibliotecas voltadas para o .NET full também, com funcionalidade que não está no CoreFX (muitas das bibliotecas que existem hoje) se divertirão muito, pois terão que manter 2 versões. Não 2 ou 3 #ifdevs e o problema está resolvido, mas muitos deles. Talvez seja diferente para você, pois talvez você possa cobrar de seu cliente as horas que você tem que gastar para trocar o apis. É diferente quando você cria um sistema de uso geral que é usado por muitos.

Se o suporte a framework compacto for alguma diretriz, suportar um ORM em .net completo e CoreCLR será uma perda de tempo terrível, muita frustração e, na verdade, não muito ganho: você não obterá novos recursos, você apenas contornará a _ausência_ deles .

(e antes que alguém comece com: "mas roda em Linux, você vai ganhar isso": nosso material roda em Mono há muitos anos. Então não, não é realmente um novo recurso a se ganhar, ele já estava lá).

SellMeThisFramework. Oh, por que estou me incomodando.

mas qualquer coisa construída desde aquela época depende das classes básicas em vez disso

_cough_ Linq-to-SQL DataContext _cough_

Como, de fato, muitos ORMs não-MS, daí o problema. Para mais elegante, apenas mordemos
o marcador e migrou para DbConnection. Se tivéssemos o tempo de novo, eu
sugeriria fortemente que o MS use [Obsoleto] quando tornar algo obsoleto. Mas:
não podemos mudar o passado.

É _é_ um problema muito doloroso de resolver, no entanto, especialmente porque a maioria
autores de bibliotecas precisam continuar com ambas as APIs (compilando uma maneira para
net40 etc, e outra forma de DNX). Eu postei anteriormente a terrível bagunça
que dapper usa para fazer isso: não é bonito, e não é tão simples quanto

E se.

Em 27 de novembro de 2015, 20:07, "Natan Vivo" [email protected] escreveu:

Um fato importante a considerar nesta discussão é que o IDb *
interfaces foram preteridas como API para ADO.NET no .NET 2.0 há uma década
quando as classes básicas foram introduzidas. Eles não foram marcados como obsoletos, mas
qualquer coisa construída desde aquela época depende, em vez disso, das classes básicas.
Provedores de app.config e suporte de string de conexão vêm à mente.

Se você tiver um código que depende dessas interfaces, você está codificando em um
API muito obsoleta, sem suporte para coisas como métodos assíncronos, o que significa
você precisará atualizá-lo de qualquer maneira se quiser que as pessoas continuem usando.

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -160198453.

Não consigo ver o ponto com que os métodos assíncronos não puderam ser adicionados como resultado do uso de interfaces. Você poderia ter criado novas interfaces para os métodos assíncronos. IAsyncDbCommand , IAsyncDataReader etc. Então você poderia fazer as classes base implementarem ambos os tipos de interfaces.

Os usuários do ADO.NET estão usando a versão assíncrona ou as versões síncronas, não ambas. Então isso teria funcionado muito bem.

Para desenvolvedores de bibliotecas, realmente não importa se a funcionalidade cresce e as interfaces permanecem as mesmas. Não é esse o propósito? Introduzir novas interfaces para novas funcionalidades. Trabalhar com classes básicas é uma dor de cabeça.

Posso apenas resumir o tópico aqui?

Vários especialistas independentes e reconhecidos da comunidade em ferramentas de dados .NET,
incluindo vários autores e mantenedores de ORM estão dizendo a você - bastante
claramente - que isso representa um conjunto significativo de problemas. Eu não acho
qualquer um de nós é ignorante das sutilezas ou ingênuo da programação
princípios, e a maioria, senão todos nós, conhecemos bem a história por trás,
porque estávamos lá na época.

A resposta oficial parece ser "parece bom para nós, e a EF está feliz".
Sim, sabemos disso, porque a decisão foi tomada em primeiro lugar.

Bem, todos nós expressamos nossas opiniões, mesmo que não tenha sido frutífera.
Em 27 de novembro de 2015 20:41, "Jonas Gauffin" [email protected] escreveu:

Não consigo ver o ponto com que os métodos assíncronos não podem ser adicionados como um
resultado do uso de interfaces. Você poderia ter criado _novas_ interfaces para
os métodos assíncronos. IAsyncDbCommand, IAsyncDataReader etc. Então você poderia
fazer com que as classes básicas implementem ambos os tipos de interfaces.

Os usuários do ADO.NET estão usando a versão assíncrona ou síncrona
versões, não ambas. Então isso teria funcionado muito bem.

Para desenvolvedores de bibliotecas, não importa se a funcionalidade cresce e
as interfaces permanecem as mesmas. Não é esse o propósito? Apresentar novo
interfaces para novas funcionalidades. Trabalhar com classes básicas é uma dor de cabeça.

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -160201361.

Caras .. atualize seu código e dê um salto em sua versão principal. Feito.

Sim, mas não há razão para que você não possa preencher pollyfill essa interface com uma diretiva de compilador ao direcionar o núcleo. Fiz isso com alguns de nossos pacotes pcl.

Eu acho que a Microsoft precisa reiterar que o core não está cheio de pontos, mas isso ainda não ajuda. Eu acho que a Microsoft precisa limpar algumas interfaces para ser honesta. Recentemente, publicou um blog onde há interfaces muito inconsistentes e você nunca sabe qual escolher. Acho que definir uma segunda interface assíncrona é uma droga. Seria bom se tudo fosse assíncrono ..

Seria bom se todo o framework fosse analisado para garantir que as coisas que precisam ser marcadas como obsoletas sejam ... E lançadas como 4.6.2

@mgravell +100. Bem dito, 100% concordou.

Quanto é realmente afetado? Estamos falando sobre coreclr aqui? O desktop .NET continuará ativo por muitos anos, até que o coreclr possa alcançá-lo. Exatamente para quem reclama, o que você cobre de perda aqui? Muitas pessoas estão basicamente dizendo que é o fim do mundo.

@leppie Na verdade, ele existirá por muitos anos. E teremos que manter esses hacks e soluções alternativas em nosso código por anos também. O ponto de discórdia aqui é a remoção da ponte comum entre os dois. Essa carga de trabalho foi transferida para todos os desenvolvedores da biblioteca, em vez de no BCL. Eu entendo os dois lados dos prós e contras da interface, mas não entendo a atitude "é pequena, siga em frente" de alguns aqui.

Vamos ser francos aqui: se todos os consumidores de bibliotecas tiverem que fazer a mesma coisa, isso deve estar no BCL . O debate é "que forma isso assume?"

No lado positivo de remover as interfaces, há um mecanismo de versão _adicional_ com o novo modelo de empacotamento agora em jogo: quais classes estão disponíveis em X, Y e Z agora é muito melhor suportado por ferramentas. Por exemplo, dotnet5.2 vs 5.4 atualmente. Mas também existem desvantagens. Por exemplo, SqlClient ainda não está a ponto de implementar as interfaces como estão hoje (veja dotnet / runtime # 14302 e dotnet / runtime # 15269), e dado o que @YoungGah disse (a menos que eu esteja interpretando

Então, o que acontece com 5,6 (1,5)? Se mais membros forem adicionados às classes abstratas, espera-se que cada provedor de dados mantenha o ritmo com o alvo móvel que pode mudar com cada pessoa em movimento? Cada consumidor precisa determinar a versão dos recursos disponíveis em cada um. Precisamos compilar uma versão do assembly para cada versão da plataforma avançando para corresponder à base abstrata da classe que está sendo passada? Como tudo isso funciona com as adições não está 100% claro. Uma interface que não muda é muito mais clara. Além disso, está a documentação: quais recursos, métodos, etc. estão disponíveis em quais versões _e plataformas_ será um grande problema para todos os autores de bibliotecas daqui para frente. Essa preocupação está apenas parcialmente relacionada aqui, mas está em jogo.

Por enquanto, estou aguardando ansiosamente a atualização nas próximas 2 semanas. Meu medo é que efetivamente seja, como a mensagem o tempo todo: "Estamos fazendo X porque funciona para SQL e Entity Framework, e isso é bom o suficiente" - sem usar nenhuma dessas palavras. A frustração do lado do autor da biblioteca, para mim, tem sido a falta de progresso (tanto no código quanto na discussão) nessas frentes há meses.

100% concordam.

Quando projetei a v2 (e superior) do SqlFu (meu Micro ORM), tive que decidir onde anexar os métodos de extensão: para DbConnection / DbCommand ou para as interfaces. Eu encontrei isso e decidi ir para as aulas abstratas.

Portanto, eu não sou MESMO afetado, embora seja afetado pela remoção de IDataReader , porque a MS, em sua sabedoria, decidiu não deixar muito claro quais interfaces devem ser tratadas como obsoletas e quais não devem. Mas, no meu caso, não é difícil substituir a interface.

No entanto, vejo o valor de ter interfaces dedicadas e não acho que seja TÃO difícil para a MS mantê-las / adicioná-las de volta. Se eles decidirem que as interfaces antigas não são tão bem projetadas, ótimo! Projete os novos para serem mais específicos. Pelo menos no futuro não teremos que lidar com o mesmo problema.

(Esta é a semana de Ação de Graças nos EUA, então as respostas dos Microsofties serão bastante limitadas até que eles voltem ao escritório na próxima semana)

Só quero reiterar - para que não perca nenhuma boa sugestão / bug neste tópico, se você tiver problemas com as classes base / área de superfície atuais, e / ou a forma como elas serão versões daqui para frente, registre um novo problema, vamos manter essa discussão apenas sobre as interfaces v1.

@NickCraver Semelhante à compatibilidade de versão a versão do .NET Framework, não haverá mudanças significativas entre as versões da área de superfície do .NET Core. Por exemplo, a adição de membros abstratos às classes base seria um exemplo de uma mudança que _não_ será feita.

@davkean quão confiante você está de que isso não acontecerá? Visto que estamos vendo uma área de superfície incompleta e nenhuma garantia de que isso vai mudar, é difícil acreditar que as peças que faltam não vão aparecer mais tarde, se é que vão aparecer. No entanto, eu acho que nenhuma alteração significativa é uma coisa _mais_ importante, o que tenho certeza que a maioria dos autores de bibliotecas aqui também assumiriam. Isso significa que é ainda mais crítico que esses itens sejam tratados bem antes de o RTM ser atingido.

Para registro, há questões separadas arquivadas na área de superfície, consulte dotnet / runtime # 14302 e dotnet / runtime # 15269 com as atualizações mais recentes da Microsoft em 25 de setembro e 2 de outubro, respectivamente - apesar de solicitar atualizações e atividades várias vezes depois disso. São 2 meses e 2 lançamentos que se passaram, com silêncio. Isso apesar do dotnet / runtime # 14302 ser o problema mais ativo neste repo (e este acabou de se tornar o segundo). Você pode entender nossa frustração?

Estou absolutamente confiante de que não faremos alterações significativas, a equipe do Data Common está procurando fazê-las sem apresentar membros abstratos.

@NickCraver Desculpe, estamos indo mal aqui - esses problemas não foram esquecidos, @YoungGah forneceu uma atualização acima sobre eles, vou garantir que ela atualize os problemas com o progresso. Muitos MS ainda estão se acostumando com isso _trabalhar ao ar livre_, vai ficar melhor com o tempo - obrigado por nos alertar sobre isso.

@niemyjski

dnx é um framework / runtime completamente novo

Se você acha que as bibliotecas dnx runtime e corefx surgiram do nada, está subestimando seriamente o tempo que levaria para desenvolvê-las do zero. O fato de que as bibliotecas CoreFx são executadas no .NET Framework completo deve dar uma pista de que, não, não é completamente novo.

Se não tiver alguma interface, trate disso ... com uma diretiva do compilador.

Sim, mas não há razão para que você não possa preencher pollyfill essa interface com uma diretiva de compilador ao direcionar o núcleo.

Se você se incomodou em ler os comentários antes de voar para este tópico, você saberá que a) há razões eb) esta é uma estratégia inviável quebrada para interfaces BCL centrais.

@nvivo

Um fato importante a ser considerado nesta discussão é que as interfaces IDb * foram descontinuadas como API para ADO.NET no .NET 2.0 uma década atrás, quando as classes básicas foram introduzidas.

Se fosse esse o caso, você não teria aberto um problema dizendo a eles para voltarem a usar interfaces que você sabia que estavam obsoletas pelas classes base uma década atrás. A maneira como você comunica que uma API está obsoleta é usar o atributo [Obsolete] que é seu único propósito de existir. Se não for amplamente divulgado, não será descontinuado, independentemente do que você pensa agora. O fato de que a maioria dos não-MS .NET ORMs dependem deles deve dar uma indicação de que sua depreciação foi mal comunicada, se é que foi.

Se você tem código que depende dessas interfaces, você está codificando em uma API muito obsoleta, sem suporte para coisas como métodos assíncronos, o que significa que você precisará atualizá-lo de qualquer maneira se quiser que as pessoas continuem a usá-lo.

Um falso espantalho - um não implica o outro. Já adicionamos suporte para APIs assíncronos, e não, adicioná-los não quebrou as bases de código existentes do Cliente, nem nenhuma das APIs existentes precisou ser alterada.

Ok, todo esse negócio de começar do zero é ótimo, mas posso fazer uma pergunta: quais concessões foram feitas para oferecer suporte a seus próprios frameworks? Que horrores do passado foram migrados porque são necessários para, digamos, o Entity Framework funcionar?

Seria uma pena fazer os MicroORMs desaparecerem, eles tornam o código .Net um tanto performante (EF é uma besta inutilizável para aplicativos onde 500ms para carregar algumas linhas não é aceitável).

Quanto às interfaces vs classes básicas: as classes básicas são ótimas, desde que tudo o que possa ser reutilizado seja virtual. Por exemplo, uma das decisões de design mais irritantes no WCF é o uso abundante de classes lacradas que contêm muitas funcionalidades. Digamos que você precise fazer um pequeno ajuste na maneira como as mensagens XML são tratadas (porque: interoperabilidade). Em vez de herdar e substituir uma pequena função, você deve reimplementar. No exemplo do WCF, o S em SOLID foi ignorado, de modo que geralmente você fica implementando uma grande interface sem nenhum dos testes necessários para garantir a qualidade da produção.

Então: classes básicas que podemos adaptar são uma coisa boa.

Estou absolutamente confiante de que não faremos alterações significativas, a equipe do Data Common está procurando fazê-las sem apresentar membros abstratos.

@davkean Isso é impossível de garantir, e você sabe disso. ADO.NET é um subsistema para comunicação com software de terceiros com uma ampla variedade de recursos, que são expostos por meio de uma API comum com alguns pontos de extensão. As coisas mudam, mesmo no terreno do banco de dados, e essas mudanças se propagam para a API usada para se comunicar e consumir esses serviços de banco de dados externos. Além disso: as mudanças no comportamento _é_ também uma mudança decisiva. E vimos isso também no ADO.NET (por exemplo, sobre tratamento de erros) nos últimos anos.

Toda a API ADO.NET está repleta dos efeitos colaterais dessas alterações, geralmente impulsionadas pelo SQL Server; dificilmente as coisas foram projetadas em geral e depois movidas para o SQL Client, mas o contrário (por exemplo, não há muitos, se houver, recursos nas classes base que são ignorados pelo SqlClient). Somado a isso, coisas necessárias para terceiros nunca chegaram à API.

Resumindo, é uma API que, no início com .NET 1.0 tinha um design geral (que provou ser seriamente falho em muitas áreas) e que, desde então, foi corrigido com recursos à esquerda e à direita para lidar com as mudanças em a paisagem. _A maioria_ deles ainda está na API (se não todos). E agora a Microsoft removerá um: as interfaces.

Não há absolutamente _nada_ ganho com a remoção de uma parte aleatória da API: nenhum recurso é adicionado por meio disso (você poderia gastar tempo nisso em vez de empurrar para trás aqui, por exemplo), mas o código que aproveita essa parte da API _não funcionará. Se o ponto por trás de toda essa mudança é 'começar de novo', então, por favor, faça, mas faça isso redesenhando a API para torná-la uma verdadeira API de propósito geral, livre-se de _todo_ o lixo que foi acumulado ao longo dos anos .

Mas isso não foi feito. Ninguém precisa se perguntar por quê, todos nós sabemos por quê. Também sabemos que se uma equipe dentro da MS tivesse se machucado gravemente com a remoção das interfaces, elas nunca teriam sido removidas em primeiro lugar.

Se a Microsoft pode adicionar uma nova funcionalidade por meio de classes base para que os provedores de terceiros implementem automaticamente 'uma forma de' o recurso (como com Async, onde o método async volta para o método de sincronização se o provedor não o implementa), ótimo: significa que nós, desenvolvedores ORM terceirizados (e convenhamos: muitos e muitos desenvolvedores acessam _sua_ API através do código de ORMs terceirizados (micro)), podemos simplesmente direcionar uma classe e pronto.

Mas não há garantia de que você fará isso. Por exemplo, ninguém na Microsoft jamais se preocupou com recursos específicos para Oracle ou PostgreSql a serem adicionados ao ADO.NET. Por exemplo, uso de UDTs, árvores de documentos, vários conjuntos de resultados por meio de cursores, cada provedor ADO.NET precisa encontrar sua própria maneira de lidar com esses recursos. E se (quando?) O SQL Server obtiver um recurso de árvore de documentos, por exemplo, com documentos JSON, o ADO.NET será atualizado com uma nova API para isso? Como você fez no passado com relatórios de erros de provedores ADO.NET?

Você não pode dar garantias como essa, e não é sensato afirmar aqui que o MS não quebrará nada no futuro. Eles sempre tiveram e às vezes até tiveram que fazê-lo: afinal, toda API tem falhas, e o ADO.NET está cheio delas; de uma forma ou de outra, algum dia alguém irá 'consertá-los'.

Portanto, para recapitular: as interfaces são parte do ADO.NET, assim como as partes remendadas em outras partes da API são parte do ADO.NET. Esses não são removidos para consertar a API, nem a API é refatorada para torná-la uma API de propósito mais geral: ela foi deixada como está, com alguns elementos removidos como DataSet / Table dependendo de elementos porque eles não são portados (e não há outras questões debatendo que com progresso semelhante), exceto ... as interfaces são removidas.

Só desse ponto de vista já não faz sentido.

@mythz

Se fosse esse o caso, você não teria aberto um problema dizendo a eles para voltarem a usar interfaces que você sabia que estavam obsoletas

Você não pode ler o OP e entender isso. Essas discussões estão ficando muito religiosas e você está apenas presumindo coisas que ninguém disse.

Abri este problema porque acredito que as interfaces são melhores para descrever uma API e ajudar nos testes. Se estiver pronto, não acho que eles deveriam ser compatíveis com uma API de 15 anos que tinha seus problemas. A compatibilidade com versões anteriores nunca foi um ponto do problema até que vocês mudaram a discussão para isso.

Não é que eu acredite que as coisas devam quebrar apenas por causa disso. Mas o controle de versão da interface é um problema do passado. Se corefx muda algo entre as versões principais, suas versões principais esperadas têm alterações significativas. Se eles quebram a interface entre versões secundárias, isso é apenas desleixo.

Já adicionamos suporte para APIs assíncronas

Você não pode adicionar uma API assíncrona em cima de uma API de sincronização. Se você fez isso usando IDbConnection ou IDbCommand, você fez errado. Se você não estiver usando essas interfaces, não terá nenhum ponto em defender qualquer compatibilidade com as versões anteriores.

Já adicionamos suporte para APIs assíncronas

Você não pode adicionar uma API assíncrona em cima de uma API de sincronização. Se você fez isso usando IDbConnection ou IDbCommand, você fez errado. Se você não estiver usando essas interfaces, não terá nenhum ponto em defender qualquer compatibilidade com as versões anteriores.

No ADO.NET, era isso que eles faziam: os métodos Async, por padrão, voltam às variantes de sincronização. Desta forma, todos os provedores ADO.NET que não suportam Async (leia-se: todos eles, exceto SqlServer neste momento) não precisam implementar coisas que não suportam: código de terceiros em ORMs que oferecem uma API Async pode programar contra os métodos assíncronos no ADO.NET e se o provedor ado.net não oferecer suporte ao assíncrono, ninguém saberá. Bem ... você saberá porque é mais lento, mas deixando isso de lado.

Agora também é uma boa ilustração da ausência de qualquer 'design' ou arquitetura geral no ADO.NET: _nenhuma_ maneira de criar um ponto de salvamento transacional na API geral. Embora quase _todos_ os bancos de dados suportem isso com um método 'Salvar (string)' em sua classe derivada de DbTransaction. Todos, exceto OleDbTransaction (como MS Access não oferece suporte, pelo menos essa é minha suspeita).

Não é fácil, mas ninguém disse que seria fácil. Este problema não é novo, OleDB e ODBC lidaram com ele por muitos anos, JDBC encontrou uma maneira de resolvê-lo, a Microsoft não precisa reinventar a roda para superar coisas como essa. Também não é exclusivo para o domínio do banco de dados: por exemplo, cada placa de vídeo oferece suporte a um subconjunto diferente de recursos por meio de sua API, exposto ao desenvolvedor por meio do Direct3D / X. Na verdade, é interessante como os designs funcionam nesses outros mundos: a API é projetada e as partes que precisam de suporte (drivers JDBC, gravadores de driver OleDB etc.) têm que implementá-los. Seu driver não suporta o X? Seu driver não é compatível com X. "Oracle não suporta ADO.NET v10". Ninguém dentro da Oracle quer ler isso. Em vez disso, SqlClient é o líder, e o que sai do vagão é adicionado ao ADO.NET e pronto.

No ADO.NET, era isso que eles faziam: os métodos Async, por padrão, voltam às variantes de sincronização.

Não, não é. A API expõe métodos assíncronos que retornam aos métodos de sincronização por padrão, mas os provedores substituem por operações assíncronas reais. O que @mythz está declarando é que ele está usando IDbCommand e IDbConnection e fazendo isso.

Isso não é possível, ponto final. Se você fizer isso, ou não está fazendo certo ou não está usando a interface. Você não pode inventar assíncrono se a API subjacente não for assíncrona.

Não, não é. A API expõe métodos assíncronos que retornam aos métodos de sincronização por padrão, mas os provedores substituem por operações assíncronas reais. O que @mythz está declarando é que ele está usando IDbCommand e IDbConnection e fazendo isso.

Nenhum provedor oficial faz isso, exceto SqlClient, todos os outros, por exemplo, ODP.NET, não implementam qualquer forma de código assíncrono e, portanto, o código de chamada volta para as variantes de sincronização (os métodos assíncronos em DbDataReader / DbCommand etc. que realmente executam o código de sincronização ) portanto, o código do usuário chama uma variante assíncrona, que está nos bastidores fazendo operações de sincronização. O que resulta em nenhuma execução assíncrona na prática (já que todo código é simplesmente sincronizado na prática). Talvez os provedores de devart implementem uma API assíncrona em sua própria implementação, não tenho certeza.

De qualquer forma, não se trata de fazer da maneira certa, mas de criar versões de APIs.

@nvivo

Abri este problema porque acredito que as interfaces são melhores para descrever uma API e ajudar nos testes. Se estiver pronto, não acho que eles deveriam ser compatíveis com uma API de 15 anos que tinha seus problemas. A compatibilidade com versões anteriores nunca foi um ponto do problema até que vocês mudaram a discussão para isso.

Ok, então você sabia que as interfaces principais do ADO.NET foram descontinuadas há 10 anos, com tudo movido para classes básicas, mas você pensou que eles deveriam simplesmente abandonar isso agora e voltar para as interfaces, coincidentemente usando os mesmos nomes das interfaces existentes, mas o interfaces existentes não devem mais existir, porque a compatibilidade com versões anteriores não é necessária, certo? claro, parece legítimo.

Se você deseja mover uma plataforma adiante, você desenvolve as APIs ao longo do tempo e as oferece suporte em paralelo, dando a todos a capacidade de também oferecer suporte a APIs paralelas e permitir que planejem seu caminho e seus clientes a partir delas. Rasgá-los sem aviso interrompe desnecessariamente o ecossistema que depende deles e empurra a complexidade para cada dependência downstream.

Você não pode adicionar uma API assíncrona em cima de uma API de sincronização. Se você fez isso usando IDbConnection ou IDbCommand, você fez errado. Se você não estiver usando essas interfaces, não terá nenhum ponto em defender qualquer compatibilidade com as versões anteriores.

Eu gostaria que você parasse de poluir este tópico com comentários sobre coisas que você claramente não tem conhecimento. Leia o código-fonte se quiser saber como as APIs Async são implementadas - e pare de espalhar falsidades cegamente. É impossível para uma biblioteca de terceiros estender as interfaces System.Data. Nós fornecemos uma API agnóstica de implementação que, a fim de oferecer suporte a todos os principais RDBMS, expõe a dependência mínima que todo provedor ADO.NET implementa em sua API externa - ou seja, as interfaces System.Data centrais. As APIs assíncronas são métodos de extensão de IDbConnection que, nos bastidores, aproveitam as APIs assíncronas em provedores ADO.NET concretos que têm suporte para eles. Internamente, já existem dependências concretas em cada provedor ADO.NET com suporte, independentemente do suporte assíncrono. Sua sugestão de que "não temos nenhum ponto em defender qualquer compatibilidade com eles" é inexperiente e completamente infundada.

Deixe-me perguntar isso para o lado da cerca da Microsoft (cc @davkean @YoungGah): Digamos que seja um mundo perfeito e nada tenha surgido. Quando _do_ você quer quebrar as coisas? Versões principais como 6.0? Em alguma outra hora? O argumento contra as interfaces é que elas não possuem versões. Bem, sim, isso é válido - mas _se não estivermos mudando as classes abstratas também_, também é um ponto discutível. Então ... podemos obter alguma clareza aí?

Acompanhamentos:
Se a resposta for sim (em algum ponto pós-RTM haverá mudanças), então que tipo de quebras veríamos? Adições, novos métodos? Se eu herdar a classe base do meu provedor, o que acontece quando você adiciona um método conflitante que as pessoas estão usando, etc.?

Se a resposta for não (nunca): por que não adicionar as interfaces de volta?

Este tópico está um pouco preso à discussão _ agora_ - o que é principalmente uma coisa boa porque essas coisas precisam ser consertadas o mais rápido possível. Todos os autores de bibliotecas aqui sabem como é difícil conseguir qualquer coisa após o lançamento, e é por isso que pressionamos tanto. Infelizmente, a falta de um plano claro para adições e mudanças _futuras_, se houver, torna o argumento mais mal informado.

Quais são os planos para o futuro?

Não devemos forçar a implementação por meio de classes abstratas neste caso. IMO

A Microsoft não fará alterações prejudiciais ao .NET 4.5. Faz parte do Windows. A compatibilidade é rei.

A Microsoft pode fazer alterações que estão interrompendo no .NET Core e não afetam o 4.5. Duvido que eles postem 1.0 RTM em algo de baixo nível como ADO.NET, mas a barra é menor. Não faz parte do Windows e as versões do .NET Core podem ser implantadas lado a lado.

As classes abstratas podem ser alteradas - isso não é interrompido. Basta adicionar um método que seja virtual com uma implementação padrão. Isso já foi feito com os métodos assíncronos do ADO.NET. Com a introdução do .NET Core, acredito que as mudanças nas classes compartilhadas precisariam ser feitas em conjunto com as versões do .NET 4.5 para manter a compatibilidade. Alguém me corrija se isso estiver errado e se aplica apenas a interfaces: sorriso:

@FransBouma

Nenhum provedor oficial faz isso, exceto SqlClient, todos os outros, por exemplo, ODP.NET, não implementam qualquer forma de código assíncrono e, portanto, o código de chamada volta para as variantes de sincronização

Você está certo, mas isso não é um problema com a API, e mais preguiça ou falta de compreensão por parte dos implementadores. O conector MySql, por exemplo, reimplementou todos os seus métodos assíncronos criando um TaskCompletionSource e completando-os com métodos de sincronização, o que é ridículo. Eles poderiam apenas excluir metade de sua base de código e permanecer com o mesmo comportamento.

Não dizer que as interfaces resolveriam isso, mas não ter um comportamento padrão para async faria pelo menos alguns deles pensarem sobre isso. O fato de 90% das pessoas muito técnicas não entenderem as operações assíncronas também não ajuda.

As classes abstratas podem ser alteradas - isso não é interrompido. Basta adicionar um método que seja virtual com uma implementação padrão. Isso já foi feito com os métodos assíncronos do ADO.NET.

Isso ESTÁ quebrando. Está quebrando para todas as bibliotecas que subclassificaram esta implementação sem saber que ela foi adicionada e as pessoas então consomem esse pensamento. Oh, esta implementação agora é compatível com postgresql BAM ERROR wtf aconteceu ...

A implementação forçada de uma abstração de banco de dados está errada.

Não importa se suas interfaces ou uma classe base. Haverá mudanças significativas. Mas a implementação predefinida forçada está errada.

O polimorfismo não funciona assim. Você não pode substituir um método sem saber. Se sua referência for um DbConnection e você chamar QueryAsync, ele só chamará esse método ou o que quer que tenha sido substituído. Um método chamado QueryAsync que já existe em uma subclasse não será chamado.

Você está confuso substituindo um versículo de método escondendo-o com o mesmo nome.

@JamesNK

Se os métodos são definidos como abstratos, nenhuma implementação existe na classe base. Isso quebra o contrato para terceiros, pois exige que eles adicionem implementação na subclasse.

Se você tornar o método virtual para que ele possa ser sobrescrito, existe uma implementação na classe base que não faria sentido para a subclasse. Isso ainda está interrompendo porque existe uma implementação que não foi implementada pelo autor da biblioteca. Claro que seu aplicativo pode ser compilado e tudo está ótimo, mas alguém chama esse método e não é válido para a subclasse. Isto é errado. Essa é uma implementação forçada que não pertence à subclasse.

Portanto, classe abstrata onde pode existir implementação que não pertence à subclasse. Ou interfaces onde não existe implementação padrão para terceiros.

@phillip-haydon é por isso que é implementado como um método virtual, não um método abstrato.

Você pode adicionar coisas que só quebrarão subclasses que já tenham um membro com a mesma assinatura (nome / args). Se os argumentos forem diferentes, isso pode introduzir bugs sutis se os desenvolvedores confundirem as sobrecargas.

Essa é uma implementação forçada que não pertence à subclasse.

Então não o coloque lá.

@jamesnk

Não coloque isso aí. É por isso que estamos discutindo a remoção de interfaces.

Torná-lo virtual não resolve o problema. Não deve haver implementação predefinida. Fim da história

@JamesNK Neste caso não o colocamos lá, _Microsoft_ o incluiu incluindo no resumo. Adicionando métodos que supostamente funcionam em _todos_ os provedores que já herdaram, eu realmente não vejo um desempenho suave ou eficiente, mesmo que não seja tecnicamente interrompido (vou admitir que os avisos de compilação "deve usar novo" _tecnicamente_ não estão interrompendo). Simplesmente não haverá uma implementação compartilhada ou imediatamente compartilhada na _maioria_ dos casos. Então, qual é a alternativa? throw new NotImplementedException() dentro desse virtual? Isso dificilmente é um argumento para ele existir, em primeiro lugar, ele está repleto de mais problemas (de tempo de execução).

Vejamos hoje: eu prefiro muito mais ver um IDbAsyncConnection adicionado quando um provedor oferece suporte, em vez de um monte de métodos que são síncronos ocultos, levando a confusão e ineficiência, que é o que temos hoje nesses resumos.

Prefiro ver um IDbAsyncConnection adicionado quando um provedor oferece suporte, em vez de um monte de métodos síncronos ocultos, levando a confusão e ineficiência

@NickCraver +1000 para isso. Como este bug aqui, onde a equipe do oracle simplesmente não entende o que significa assíncrono.

Você poderia fazer isso com interfaces. O problema com eles é que você não pode aceitar argumentos que exijam várias interfaces, por exemplo, eu preciso de um tipo que seja IDbAsyncConnection e IDbConnection. Você perde a digitação forte e tem que começar a consultar as interfaces de estilo COM, o que eu não acho muito amigável. É uma ferramenta de design de API que tem seu lugar, mas não sei se a usaria por padrão.

Se a implementação padrão estava lançando NotImplementedException, então fixá-la na classe base é a coisa errada a fazer. Como eu disse, não coloque aí então. Se você vir alguém fazendo isso, levante um problema.

De qualquer forma, sejam interfaces ou classes base abstratas, minha experiência é adicionar novos recursos a uma biblioteca que não foi originalmente projetada para eles, sem quebrar o mundo, é muito difícil.

@JamesNK presumivelmente IDbAsyncConnection herdaria IDbConnection aqui, mas não precisa ser necessariamente o caso - eles poderiam compartilhar membros comuns ou herdar de uma base comum. Por exemplo, no Dapper, provavelmente implementaríamos da seguinte forma:

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

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

Imagino que a maioria das bibliotecas com métodos de sincronização / async teria usos e implementações semelhantes.

_Editar: _ depois de digitar isso, eu percebi o quão melhor Async no final do nome seria para todos estes ...

ADO.NET teve as seguintes mudanças importantes ao longo dos anos:

  1. Em 2003 (1.1), eles fizeram uma alteração significativa do 1.0 e o redesenharam
  2. Em 2005 (2.0) eles mudaram para o modelo de provedor com as classes básicas que existem hoje
  3. Em 2012 (4.5), eles adicionaram suporte assíncrono, que na verdade não mudou nada além de adicionar novos métodos que faziam as mesmas coisas de forma assíncrona.

Voltar para a forma como a API de 2003 foi definida é uma mudança que vai quebrar a compatibilidade (o que as pessoas não querem) ou remover recursos adicionados na última década. Mas adicionar uma nova interface ao .NET Core com o design atual é _compatível com a fonte_ com qualquer versão do .NET. Recompilar é tudo que você precisa para manter a maior parte do código escrito nos últimos 15 anos funcionando. E você precisará recompilar de qualquer maneira para direcionar o corefx.

Esta API está estável há muito tempo. Ele poderia ser redesenhado como interfaces, se as pessoas desejassem. Como de costume, não há problemas técnicos aqui, tudo se resume a cicatrizes, preferências e ego.

Por que não trazer as interfaces de volta e marcá-las como obsoletas?

Embora essa ideia tenha sido abandonada, estou me perguntando se inferfaces neutras de montagem poderiam ter ajudado neste tipo de situação, consulte http://davidfowl.com/assembly-neutral-interfaces/ e, em seguida, sua implementação
http://davidfowl.com/assembly-neutral-interfaces-implementation/

Acho que as interfaces neutras de montagem são uma pista falsa aqui; se alguma coisa for
acontecer, isso é uma coisa totalmente "comum", uma vez que existe "comum". Mais isso
é discutível, pois o recurso evaporou.
Em 28 de novembro de 2015, 17:38, "Shahid Khan" [email protected] escreveu:

Embora essa ideia tenha sido abandonada, estou me perguntando se a montagem neutra
inferfaces poderia ter ajudado neste tipo de situação ver este
http://davidfowl.com/assembly-neutral-interfaces/ e então seus
implementação
http://davidfowl.com/assembly-neutral-interfaces-implementation/

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -160323344.

O que observo:

  • As pessoas por trás da ideia de CoreClr (nova estrutura brilhante) têm uma mentalidade completamente diferente das pessoas por trás de sua implementação (compatibilidade com versões anteriores com base de código de 15 anos é o rei).

O que eu penso:

  • Ao remover interfaces, você está piorando as coisas .
  • Nada é obsoleto até que seja decorado com [Obsoleto]. E não importa quem pensa o quê em qualquer ponto do tempo. Este é o contrato e se você simplesmente não o estiver cumprindo, então você não está.

O que eu quero:

  • Use interface (s) como superfície base da API em vez de classe (s) base.

O que eu sinto:

  • Estamos no final de 2015 e é frustrante e triste ver pessoas discutindo em torno disso.

interfaces não podem ser versionadas.

Certamente as interfaces podem ser facilmente alteradas com herança de interface? E se estiver fazendo algo totalmente diferente; bem, é uma interface diferente.

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

class SomeClass : IOldInterface, INewInterface, IDifferentInterface
{
}

Apenas não o chame de IInterfaceV2 , IInterfaceV3 ele precisa explicar o que adiciona.

Para colocar no contexto [Obsolete] a interface antiga, e usar algumas interfaces novas e chamá-las do que realmente são; em vez dos métodos não assíncronos que parecem normais nos dias de hoje.

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

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

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

@abatishchev

As pessoas por trás da ideia de CoreClr (nova estrutura brilhante) têm uma mentalidade completamente diferente das pessoas por trás de sua implementação (compatibilidade com versões anteriores com base de código de 15 anos é o rei).

Obrigado por deixar isso claro, parece que precisa ser destacado. Geralmente, acredito que as equipes de MS se preocupam profundamente com a compatibilidade com versões anteriores, que é vital para a evolução de ambas as linguagens e suas plataformas. Acontece que as decisões em torno de System.Data não estão sendo feitas levando-se em consideração o ecossistema mais amplo - ao qual essas abstrações centrais deveriam servir igualmente.

  • Ao remover interfaces, você está piorando as coisas.
  • Nada é obsoleto até que seja decorado com [Obsoleto]. E não importa quem pensa o quê em qualquer ponto do tempo. Este é o contrato e se você simplesmente não o estiver cumprindo, então você não está.

Precisamente.

Em relação ao versionamento de interfaces. Corrija-me se eu estiver errado, mas achei que o objetivo do CoreClr é um controle mais refinado e independente de versões? Quebrando a mudança? Estrondo! Nova versão principal lançada.
Parece que depois de 15 anos de experiência em design, você repetirá os mesmos erros, mas agora terá versões independentes de pacotes nuget. Os argumentos acima são idênticos à boa e velha estrutura monolítica, embora não seja mais.
Se a compatibilidade com versões anteriores for obrigatória, você simplesmente não pode remover essas interfaces. Se não for, vamos parar de falar sobre isso e projetar a API do zero, desta vez ouvindo os principais autores de ORM e outros desenvolvedores experientes em ADO.NET.
Obrigada.

@abatishchev pontos muito bons. Eu também me perguntei: qual é realmente o ponto do argumento da compatibilidade com versões anteriores? Se um novo recurso for adicionado ao CoreClr, tudo o que o usa não será executado em .net completo, portanto, por segurança, só se pode usar o comportamento comum. (isso nunca funcionou bem).

Desculpe pelo longo silêncio da minha parte.

Portando para .NET Core

Em primeiro lugar, vamos falar sobre o elefante na sala, que é que o .NET Core não tem tantas APIs disponíveis como muitas pessoas - incluindo nós - esperariam.

Estou trabalhando com minha equipe para reunir um conjunto de documentos sobre como abordaremos a área de portabilidade de ativos existentes para o .NET Core.

Estamos planejando portar mais funcionalidades que atualmente só existem no .NET Framework / Mono para o .NET Core. Este documento explicará como faremos isso, como priorizamos e como será a mecânica. Não gostaria apenas que esse trabalho acontecesse abertamente, mas também permitir que a comunidade nos ajude a portar mais funcionalidades.

Quebrando mudanças

Existe uma área que causa muita confusão quando as pessoas falam sobre o .NET Core. Deixe-me esclarecer uma coisa:

Não é uma alteração significativa se o .NET Core tiver menos APIs do que o .NET Framework.

O motivo é que o .NET Core é uma nova plataforma e pode tecnicamente ter um conjunto arbitrário de APIs. No entanto, é claro, não queremos um conjunto arbitrário - é o que fazíamos no passado. O objetivo do .NET Core é ter uma história em que as pessoas possam criar bibliotecas (e com aplicativos de console até certo ponto, até mesmo aplicativos) que serão executados no .NET Framework e .NET Core. Isso requer que haja um subconjunto de ambas as plataformas 100% compatível. Nesse contexto, você nos ouvirá falar sobre mudanças importantes.

Além disso, nossa intenção é ter uma alta barra de compatibilidade no .NET Core. Em outras palavras, não planejamos realizar alterações importantes na API entre uma versão de uma API .NET Core e outra.

Interfaces

Adicionar membros a interfaces é - por definição - uma mudança importante. Algumas pessoas argumentam que o impacto é baixo e que existem maneiras de modelá-lo, mas como está até hoje, é uma mudança binária e decisiva na fonte.

O WinRT, que é baseado em COM e, portanto, fortemente dependente de interfaces, resolve esse problema criando mais interfaces, como IFoo , IFoo2 , IFoo3 . É factível, mas certamente confuso sem um tempo de execução ou recurso de linguagem para torná-lo suportável. Até agora, esse recurso não existe. No entanto, como membro da equipe de design de linguagem, estou bastante interessado em ouvir propostas. Outras linguagens e plataformas têm ideias relacionadas naquele espaço, e também estou procurando ativamente por opções (como mix-ins / traits, extensão-tudo do Swift ou membros padrão para interfaces).

Como ainda nos preocupamos com a compatibilidade com versões anteriores, geralmente preferimos tipos de base abstratos em vez de interfaces.

Interfaces ADO.NET

Com tudo isso dito, vamos falar sobre a pergunta original neste tópico: expor as interfaces do provedor ADO.NET.

Como David explicou: nós os consideramos obsoletos quando os tipos de base abstratos foram introduzidos, que era no .NET 2 / Visual Studio 2005. Parece que há uma forte crença de que ter essas interfaces é fundamental para portar estruturas ORM para .NET Core. Para mim, isso fornece evidências suficientes de que devemos portar as interfaces para o .NET Core.

No entanto, como acontece com todas as novas APIs, precisamos estar atentos a um dos objetivos principais do .NET Core, que é ter uma pilha com componentes. A interface IDataReader depende de DataTable , que depende de DataSet . Conforme descrito em dotnet / runtime # 14302, não nos opomos a adicionar suporte para DataTable mas consideramos DataSet legado. No entanto, ainda podemos adicioná-lo como um pacote separado, mas de qualquer forma isso exigiria quebrar a cadeia de dependências das interfaces -> DataTable -> DataSet . Vou trabalhar com @YoungGah para ver o que podemos fazer lá.

Esta abordagem resolveria as preocupações?

A menos que eu esteja enganado, a única dependência DataTable em IDataReader é o
Método GetSchemaTable (), já discutido detalhadamente na DataTable
cadeia. Reconheço prontamente, no entanto, que o fato de haver um
espero adicionar _algumas_ mente de funcionalidade semelhante em uma data posterior (se
via DataTable ou não) torna muito difícil expor isso no
interface, já que estender a interface mais tarde é problemático. Não seria
tão simples quanto "remova o método por enquanto, adicione outra coisa mais tarde"
Em 5 de dezembro de 2015, às 12h17, "Immo Landwerth" [email protected] escreveu:

Desculpe pelo longo silêncio da minha parte.
Portando para .NET Core

Em primeiro lugar, vamos falar sobre o elefante na sala, que é o .NET
O Core quase não tem tantas APIs disponíveis quanto muitas pessoas - incluindo
nós - esperaria.

Estou trabalhando com minha equipe para reunir um conjunto de documentos sobre como estamos indo
para abordar a área de portabilidade de ativos existentes para .NET Core.

Estamos planejando portar mais funcionalidades que atualmente apenas
existe no .NET Framework / Mono para .NET Core. Este documento irá chamar a atenção
como vamos fazer isso, como priorizamos e o que a mecânica vai
ser. Não só gostaria que esse trabalho acontecesse abertamente, também gostaria de
permitir que a comunidade nos ajude a portar mais funcionalidades.
Quebrando mudanças

Existe uma área que causa muita confusão quando as pessoas falam sobre
.NET Core. Deixe-me esclarecer uma coisa:

Não é uma mudança significativa se o .NET Core tiver menos APIs do que o .NET
Estrutura.

A razão é que o .NET Core é uma nova plataforma e pode tecnicamente
tem um conjunto arbitrário de APIs. No entanto, é claro, não queremos um
conjunto arbitrário
http://blogs.msdn.com/b/dotnet/archive/2014/12/04/introducing-net-core.aspx
- isso é o que fizemos no passado. O objetivo do .NET Core é ter um
história em que as pessoas podem criar bibliotecas (e com aplicativos de console para um determinado
estender até mesmo aplicativos) que serão executados no .NET Framework e .NET Core. Esse
requer que haja um subconjunto de ambas as plataformas que seja 100%
compatível. Nesse contexto, você nos ouvirá falar sobre mudanças importantes.

Além disso, nossa intenção é ter uma alta barra de compatibilidade no .NET Core.
Em outras palavras, não planejamos realizar alterações de quebra de API entre
uma versão de uma API .NET Core e outra.
Interfaces

Adicionar membros a interfaces é - por definição - uma mudança importante.
Algumas pessoas argumentam que o impacto é baixo e que existem maneiras de modelar
isso, mas do jeito que está até hoje, é uma mudança de quebra de código e binário.

WinRT, que é baseado em COM e, portanto, fortemente dependente de interfaces,
resolve esse problema criando mais interfaces, como IFoo, IFoo2,
IFoo3. É factível, mas certamente confuso sem um tempo de execução ou
recurso de linguagem para torná-lo suportável. Até agora, esse recurso não existe.
No entanto, como membro da equipe de design de linguagem, estou bastante interessado em
ouvir propostas. Outras linguagens e plataformas têm ideias relacionadas em que
espaço, e também estou procurando ativamente por opções (como
mix-ins / traços, extensão-tudo do Swift ou membros padrão para
interfaces).

Uma vez que ainda nos preocupamos com a compatibilidade com versões anteriores, geralmente favorecemos
tipos de base abstratos sobre interfaces.
Interfaces ADO.NET

Com tudo isso dito, vamos falar sobre a pergunta original neste tópico:
expondo as interfaces do provedor ADO.NET.

Como David explicou: nós os consideramos obsoletos quando a base abstrata
tipos foram introduzidos, que estava no .NET 2 / Visual Studio 2005. Parece
que há uma forte crença de que ter essas interfaces é fundamental para
portar estruturas ORM para .NET Core. Para mim, isso fornece evidências suficientes
que devemos transportar as interfaces para o .NET Core.

No entanto, como acontece com todas as novas APIs, precisamos estar atentos a um dos
objetivos principais do .NET Core, que é ter uma pilha com componentes. o
interface IDataReader tem uma dependência de DataTable, que tem um
dependência de DataSet. Conforme descrito em dotnet / runtime # 14302
https://github.com/dotnet/corefx/issues/1039 , não nos opomos a adicionar
suporte para DataTable, mas realmente não queremos portar DataSet. Então
adicionar as interfaces exigirá quebrar essa dependência. Vou trabalhar com
com @YoungGah https://github.com/YoungGah para ver o que podemos fazer lá.

Esta abordagem resolveria as preocupações?

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -162115855.

Sim, IDataReader tem uma dependência de DataTable que tem uma dependência de DataSet.

Conforme mencionado anteriormente, não podemos remover membros de interfaces, portanto, precisaríamos quebrar essa dependência de outra maneira; ou por não portar a interface ou por quebrar a dependência DataTable -> DataSet.

Esta abordagem resolveria as preocupações?

Sim, restaurar as interfaces ADO.NET mesmo sem o método GetSchemaTable() resolveria as dependências pesadas de problemas de interface ADO.NET que estou enfrentando atualmente.

Estou assumindo que quebrar a dependência DataTable -> DataSet se significasse que GetSchemaTable() também seria incluído, seria a abordagem preferida para aqueles que dependem de GetSchemaTable() (então esse seria o meu voto) - mas Eu daria mais peso aos desenvolvedores que isso afeta.

@terrajobst, obrigado por seu resumo e por compartilhar suas considerações.

@terrajobst obrigado pelo resumo e explicações. Eu mantenho o Npgsql, portanto, estou escrevendo da perspectiva de um provedor ADO.NET, não de um ORM.

No .NET Core, a Microsoft parece ter desejado remover alguns recursos legados de longa data do ADO.NET, nomeadamente DataTable / DataSet e as interfaces. Isso cria uma API melhor e mais limpa no futuro, mas cria um trabalho substancial para os usuários que já dependem das interfaces e da API DataTable / DataSet.

Eu pessoalmente acho que matar DataTable / DataSet é uma coisa boa, e que uma nova e melhor API de metadados deve ser introduzida (acho que isso é verdade mesmo sem matar DataTable / DataSet). Eu não minimizo
a dor causada por essa quebra para os consumidores de ORM (e outros), mas uma coisa a lembrar é que, se houver uma oportunidade de limpar e introduzir a quebra, o .NET Core é essa oportunidade.

Um fator adicional frustrante aqui é que manter as interfaces ADO.NET também significa manter DataTable / DataSet. Em outras palavras, embora eu pessoalmente não tenha uma opinião muito forte sobre o problema das interfaces, mantê-las significa manter DataTable / DataSet e isso parece mais problemático.

Como não sei quantos ORMs existem por aí que ainda dependem das interfaces, e também não sei quanto esforço seria para eles fazerem a transição para as classes base, a escolha não é muito claro aqui. Mas meu pressentimento é aproveitar esta oportunidade e limpar as coisas, mesmo que isso signifique que algumas pessoas sofram ...

PS Faça o que fizer, não vá na rota da explosão da interface, ou seja, IFoo2, IFoo3. Isso é apenas uma solução não.
PPS Se você decidir criar uma nova API de metadados não DataTable no .NET Core e quiser que as bibliotecas do .NET Core sejam executadas no .NET Framework, isso significa que você terá que lançar um novo .NET Framework com a nova API junto com .NET Core.

@terrajobst Obrigado por quebrar o silêncio;). Acho que o principal problema é que não há design presente para ADO.NET. Não existe há muito tempo e isso mostra em várias partes da API que é uma mistura de recursos preenchidos sem pensar nas coisas por mais de alguns minutos (ao que parece). Eu concordo com @roji sobre isso: o .NET core é uma chance única de fazer algo sobre as coisas, então o .NET core não deve ser impedido pela regra (na minha opinião, boba) de que deve ser compatível com as versões anteriores .NET cheio.

Dito isso, se as coisas não vão mudar para melhor para o ADO.NET (ou seja, ele obtém um design real onde uma API geral é projetada que é especializada no SqlClient e não o contrário! Portanto, recursos não disponíveis no SQL Server, mas em outros bancos de dados são adicionados à API geral e não são deixados para o gravador do provedor ADO.NET), então a próxima melhor coisa é portar o máximo possível, incluindo as interfaces e nós mesmos, devs de terceiros #ifdef em torno dos buracos que serão deixados.

A dependência DataTable pode ser um problema, no entanto, já que a interface IDataReader tornará difícil manter as coisas compatíveis com versões anteriores com .NET _if_ tabela de dados completa em qualquer forma que não seja portada. Mas acho que é uma causa perdida de qualquer maneira. Foi dito pela MS várias vezes que o .NET full não receberá tantas / tão frequentes atualizações quanto o .NET core, então, se algo _novo_ for adicionado ao .NET core, ninguém pode usá-lo, já que usá-lo torna a biblioteca imediatamente incompatível com .NET full. Provavelmente perdi algo aqui, então, se for esse o caso, corrija-me :)

Isso por si só já torna a situação estranha: tem que haver compatibilidade com versões anteriores, mas na prática isso parece difícil de conseguir e, de qualquer maneira, uma pista falsa. IMHO, se isso for abordado primeiro, o resultado disso pode ser usado para tomar decisões adequadas sobre o que fazer com as APIs do .NET core, ou seja, melhorá-las em vez de arrastar todo o material antigo e mal projetado do .NET. Isso não quer dizer que todas as APIs são mal projetadas, no entanto, com o ADO.NET (como descrevi em um post anterior), as coisas não foram feitas corretamente por muitos anos. Fazer uma pausa limpa e, em vez disso, implementar um sistema que pode ser mais robusto (o JDBC ainda está forte, o ODBC ainda funciona como fazia 25 anos atrás) e adaptável às mudanças é preferível.

Pensando um pouco mais sobre isso, o comentário de @FransBouma sobre o requisito de compatibilidade com versões anteriores faz muito sentido. Uma das promessas do .NET Core são iterações mais rápidas graças ao pacote Nuget com componentes - em vez das atualizações do .NET Framework chegando uma / duas vezes por ano, as atualizações do .NET Core podem ser lançadas sempre que necessário. Parece que o valor disso é severamente limitado se as atualizações nunca puderem quebrar a compatibilidade com versões anteriores do .NET Framework ...?

Eu entendo que a compatibilidade com versões anteriores é um requisito que vai muito além desta discussão específica do ADO.NET, estou apenas pensando.

É o contrário do @roji : iterações curtas são habilitadas por não quebrar APIs.

Se você é um desenvolvedor .Net e sua compilação continua quebrando todas as semanas porque a API da estrutura subjacente está mudando constantemente, não demorará muito para você começar a considerar outra plataforma.

Portanto, é uma iteração rápida impulsionada por mudanças ininterruptas.

(editado)
Você só pode iterar sem interromper as mudanças até que tenha que, por exemplo, adicionar algo a uma interface de classe, mudança de comportamento (!) Ou adição de comportamento, o que não é uma mudança significativa no sentido literal (mudança de comportamento é tho), mas usando-o em .net core tornará seu código não compatível com .net full, então, nesse sentido, fará com que .net core não seja mais compatível com .net full _para aquele pedaço de código_. Qual IMHO se resume a .NET core sempre terá as mesmas interfaces (de classe) e comportamento que em .NET full, até o último byte (que é IMHO insustentável) ou obterá novos recursos que são portados posteriormente (que é literalmente o que MS disse btw) para .NET full, efetivamente tornando-o não compatível com versões anteriores. Depende de que lado da cerca você está, é claro: se você disser: 'há um conjunto comum de interfaces (de classe) X com comportamento B definido, e tanto o .NET core quanto o .NET as implementam totalmente e X e B vencem' t mudar no futuro ', ainda significará que há uma nova estrutura fora de X & B que terá coisas novas e que é precisamente onde as coisas podem mudar e também onde está o futuro.

Pode-se ir muito longe com isso, por exemplo, que as interfaces / classes usadas em X e B no núcleo .net são, na verdade, wrappers em torno das novas classes / interfaces no núcleo .NET. Fica então a cargo do desenvolvedor que os usa usar o X&B ou os novos com melhor design e sem uma API em comum com o .NET full.

Como já temos que #ifdef nossa maneira de contornar as coisas que faltam no X & B, ao oferecer suporte a ambos os frameworks, IMHO é preferível simplesmente ser capaz de direcionar novas interfaces / classes no .NET core, conforme mais cedo ou mais tarde mudanças de comportamento (talvez até muito sutis , como uma exceção diferente em uma determinada situação) surgirá lá de qualquer maneira, então 'portar' o código para o núcleo .NET com sua API _nova_ (não X&B) seria melhor. Temos que portar de qualquer maneira, pelo menos é assim que eu vejo.

@ryanbnl , concordo com @FransBouma. A questão não é que queiramos quebrar APIs. É que restringir o .NET Core para ser executável dentro do .NET Framework significa que você nunca poderá adicionar nada a qualquer interface do .NET Core ou adicionar um membro abstrato a qualquer classe base abstrata. Essas duas mudanças realmente não quebram a compatibilidade com versões anteriores em nenhum sentido real para alguém que usa o .NET Core, elas quebram a compatibilidade com o .NET Framework.

@roji, a menos que seja um novo pacote externo do framework (por exemplo, não GAC) quando pode ser compatível com o Framework e o núcleo e rodar em sua própria cadência; mas isso pode ser ainda mais mudanças para provedores de ORMs e o nome System.Data.IDbConnection já está sendo usado ...

@benaadams esse é um comentário válido - eu só tinha APIs de estrutura não nuget, como ADO.NET, em mente. No entanto, eles parecem cobrir espaço de API suficiente para ser uma preocupação - o .NET Core não será capaz de evoluir nenhum deles (leia: adicionar métodos de interface ou métodos abstratos) sem se tornar impossível de executar

Não tenho certeza do que você quer dizer com relação ao IDbConnection ...

@roji significa apenas um novo pacote que fornece esses tipos, por exemplo System.Data.Database ; para permanecer compatível com o core e full, não poderia redefinir nenhum tipo que seria do GAC no framework completo ou entraria em conflito.

No ponto de pepita; se houver alguma razão para que isso não possa viver em nuget para full e core; e descontinuar a atual System.Data api post 4.6.1+?

Isso causaria mais dor agora; mas alguma compatibilidade já está sendo quebrada, onde está descartando as interfaces, ou apenas descartando DataSet então algum retrabalho já precisa ser feito para o coreclr pelos provedores de ORM.

Uma API inteiramente nova que vivia em nuget e fora da estrutura do GAC poderia ser compatível com versões anteriores para uso com netstandard1.2, por exemplo, 4.5.2+, coreclr, UWP, mono / Xamarin etc. Embora fosse um pouco mais trabalhoso agora - mas é provavelmente um momento melhor do que mais tarde.

Dado que as novas APIs estão em jogo para achema e tal, denotando DataSet não virá (ou DataTable ), isso deve ser fechado? Parece que as classes básicas são o caminho a seguir com base nos comentários em dotnet / corefx # 5609 e encaminhamento de tipo, o que significa que as interfaces não têm uso dado GetSchemaTable() e alguns outros não estão lá para trazê-los para compat ...É justo dizer isso?

O que deve ser fechado? Se não podemos ter GetSchemaTable() por causa da dependência DataTable / DataSet, isso é uma coisa, mas as interfaces ainda devem ser restauradas (sem GetSchema se necessário) e facilitar a portabilidade das bases de código existentes com dependências profundas delas. As interfaces ausentes são um bloqueador, ainda estou esperando por um lançamento com elas antes de começarmos o trabalho de suporte ao dnx / core.

Concordo com @mythz , as interfaces são outro assunto e muito importante. Talvez não para a maioria dos usuários, mas esses mesmos usuários usam código que é escrito por um pequeno grupo e esse código _está_ baseado nessas interfaces (bem como em outros recursos ADO.NET ausentes importantes, como DbProviderFactory).

Para ser honesto, com o impulso extremo em direção a um rótulo 'RTM', tenho pouca esperança de obter uma API sólida com 1.0. Será como o .NET 2.0 de novo: todos os erros da primeira versão serão corrigidos.

@FransBouma

Adicionar as interfaces ao .NET Core seria uma alteração aditiva. Portanto, mesmo que não seja adequado para a V1, pode ser adicionado em qualquer versão posterior, até mesmo 1.1.

todos os erros cometidos pela primeira versão serão corrigidos.

Sem ofensa, mas é assim que o software funciona.

@terrajobst

Portanto, mesmo que não seja adequado para a V1, pode ser adicionado em qualquer versão posterior, até mesmo 1.1.

sim, não é que não seja tecnicamente possível, é que os resultados de fazê-lo (ou a falta de fazê-lo) têm consequências (de longo alcance), das quais a Microsoft não sofre, somos nós. Até agora, vi pouca apatia por isso. É muito legal e empolgante trabalhar em novos frameworks, mas não é desenvolvido em uma sala limpa para um novo público. Também não é o caso de não haver uma história para aprender, pelo contrário.

Sem ofensa, mas é assim que o software funciona.

Veja, isso é exatamente o que eu quis dizer acima: você não é aquele que tem que lidar com as consequências de suas decisões, eu tenho que fazer. E eu já lidei com as consequências de uma decisão semelhante de seus antecessores, então avisei para que não cometesse o mesmo erro.

Eu sei como o software funciona, sou desenvolvedor de software profissional há mais de 21 anos. Dei meu conselho honesto, não como um novato, mas como um especialista experiente nessa área em particular. Você pode fazer o que quiser com isso, só espero que você pense duas vezes antes de tomar uma decisão levianamente aqui, pois as consequências são de longo alcance, e como eu disse: temos que lidar com isso, não você.

Até mesmo um erro pode ser consertado depois, mas ainda não foi cometido, é um bom motivo para não fazer em primeiro lugar, não é?

Vocês estão exagerando. Duvido que os efeitos disso tenham as mesmas consequências que o antigo .NET.

Com o coreclr agrupado em cada aplicativo, legado tem um significado muito diferente. Como praticamente ninguém se importa se os recursos do asp.net mvc 5 não são portados para o mvc 4 ou 3. Legacy aqui terá um significado diferente, e há história em outros projetos para mostrar isso.

Ainda bem que

@nvivo Por favor, não tente minimizar as consequências _Eu_ tenho que lidar, já que você não precisa lidar com elas, mas eu sim.

Obrigado @FransBouma por me colocar no meu lugar. Foi meu erro pensar que poderia comentar sobre o assunto. Você certamente é mais qualificado do que eu para saber que tipo de coisas afetam meu trabalho.

Na verdade, embora eu tenha aberto a edição, ela não tem absolutamente nenhum efeito em meu trabalho ou nas coisas que me interessam. Eu estava pensando sobre os pobres desenvolvedores como você, que fazem todo o trabalho árduo do planeta.

Estou muito feliz que pessoas como você estejam aqui para cuidar dos problemas difíceis. Por favor, não hesite em nos dizer repetidas vezes (e novamente) quão mais importantes são as questões _você_.

Obrigado @FransBouma.

_spiro_ onde posso dizer tudo isso? Tudo o que eu digo é, por favor, não subestime as coisas, já que você faz isso com 'você está exagerando', eu não acho que estou exagerando. Com 'você não tem que lidar com eles' quero dizer: as consequências para mim. Porque eu sei o que são, eu reajo da maneira que fiz. Aparentemente, isso é uma 'reação exagerada'.

Mas de qualquer forma.

Temos respostas para este problema aqui e aqui .

A resposta oficial é: as interfaces não estarão no .NET Core 1.0 e, embora improváveis, podem ser consideradas para versões futuras de alguma forma diferente da que existia no .NET.

Estou encerrando este problema porque a questão original foi abordada.

@nvivo Obrigado, mas é melhor deixar qualquer resposta oficial para as pessoas realmente responsáveis ​​pelo projeto, que também são capazes de encerrar os problemas por conta própria, uma vez que tenham decidido que ele foi resolvido.

@terrajobst Existe uma resposta / cronograma oficial atualizado para as interfaces? e qual é a melhor maneira de rastrear esse item de trabalho daqui para frente? devemos abrir um novo problema ou você continuará a fornecer atualizações aqui?

Vamos deixar isso em aberto por enquanto. A meu ver, a resposta não foi "não vamos expor as interfaces". A resposta foi "vamos encontrar uma maneira de expô-los, mas vamos pensar sobre o que isso significa para a dependência DataTable".

Desculpe por voltar para vocês tão tarde. Depois de discutir várias opções dentro de nossa equipe, decidimos trazer de volta as interfaces com uma classe vazia de DataTable. Não é uma solução ideal, mas dado o período de tempo do RTM, essa abordagem garantirá que possamos buscar opções viáveis ​​em torno de DataTable / DataSet no futuro. Tentaremos trazer as interfaces para System.Data.Common por v1 RTM; SqlClient não implementará as interfaces por v1. Obrigado pelo seu feedback e paciência. Seu feedback é uma parte fundamental para tornar a pilha de dados um produto viável.

@YoungGah obrigado pela atualização, se as classes DataTable forem apenas espaços reservados vazios, o que está exigindo tanto tempo / esforço (ou seja, impedindo-os) de ser implementado pelo SqlClient v1?

@mythz O custo está na implementação das interfaces no tipo base / encaminhando-as aos métodos existentes. O custo deve ser mínimo, mas geralmente as coisas aparecem: sorria:

Adicionamos as seguintes interfaces ao .Net CoreFX em System.Data.Common

IDataParameter
IDataParameterCollection
IDataReader
IDataRecord
IDbCommand
IDbConnection
IDbDataParameter
IDbTransaction

Isso foi feito no PR https://github.com/dotnet/corefx/pull/6359

@ saurabh500 Grande coisa,

: +1:

: +1:

Incrível; existe um marco para isso atingir o nuget? rc3?

Em 25 de fevereiro de 2016 às 02:54, Saurabh Singh [email protected]
escreveu:

Adicionamos as seguintes interfaces ao .Net CoreFX em System.Data.Common

IDataParameter
IDataParameterCollection
IDataReader
IDataRecord
IDbCommand
IDbConnection
IDbDataParameter
IDbTransaction

Isso foi feito no PR dotnet / corefx # 6359 https://github.com/dotnet/corefx/pull/6359

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/dotnet/corefx/issues/3480#issuecomment -188577701.

Cumprimentos,

Marc

Conforme comentado no PR, precisamos entender por que não há métodos assíncronos nessas interfaces. Como foi proposto, esta é basicamente uma versão reduzida das interfaces ADO.NET 1.1, e não acho que a ideia deva ser apenas a compatibilidade com o código antigo.

As interfaces devem se concentrar no estado atual de ado.net, já que os métodos assíncronos devem ser a maneira padrão de acessar qualquer banco de dados hoje. Sem suporte real para métodos assíncronos, essas interfaces são inúteis para o desenvolvimento moderno
desenvolvimento.

E mesmo incluindo os métodos assíncronos do .NET 4.5, alguns métodos adicionais, como DbTrabsaction.CommitAsync também devem ser adicionados.

O provedor postgres adicionou alguns métodos adicionais, como CommitAsync à sua API, que são bastante úteis e necessários.

As interfaces atuais estão bem como estão. A implicação de alterá-los é muito grande.

O modelo assíncrono é bem diferente do síncrono e, como você deve saber, se for para o modelo assíncrono, deverá fazê-lo de tudo. Portanto, não há realmente nenhuma razão para ter as mesmas interfaces para ambas as APIs. Crie novos para a API assíncrona.

Se a equipe .NET deseja fornecer uma API mais moderna, por que não apenas criar uma nova API que não seja chamada de ADO.NET? Sem legado para ser prejudicado e sem reclamações da comunidade. Isso também se encaixa bem com a forma como o dnx será distribuído? ou seja, pacotes independentes.

: +1: nas interfaces, bom compromisso.

Não acho que a ideia deva ser apenas compatibilidade com código antigo.

Essa é a ideia _entire_ aqui. Caso contrário, as classes básicas estariam bem. É muita dor de transferência que queremos evitar.

Sem suporte real para métodos assíncronos, essas interfaces são inúteis para o desenvolvimento moderno.

Discordo disso, mas não discordo necessariamente de uma versão assíncrona das interfaces (que ninguém implementa hoje). Este seria um novo recurso. Não podemos adicionar membros retroativamente às interfaces existentes, isso simplesmente quebra muitas coisas. Ter um IDbReaderAsync ou algo assim não é loucura IMO, mas é uma discussão diferente.

Eu acredito fortemente que os métodos async _não_ deveriam estar nas classes base, se a implementação padrão for um wrapper de sincronização - isso é ativamente ruim e desperdiçador. Se houver outra proposta, que seja, mas de novo: isso deveria ser uma questão diferente de qualquer maneira.

Ok, talvez eu tenha me expressado da maneira errada aqui ou tenha sido muito forte com minhas palavras.

Sou a favor de uma interface adicional para assíncrona, se necessário. O que não me agrada é ter algo que define um contrato oficial para ADO.NET (isso é o que são as interfaces), mas não tem métodos assíncronos em nenhum lugar.

Mas então, ter interfaces alternativas para métodos assíncronos provavelmente causaria outros problemas ...

Eu acredito fortemente que os métodos assíncronos não devem estar nas classes base, se a implementação padrão for um wrapper de sincronização - isso é ativamente ruim e desperdiçador.

Eu concordo, esta é a principal razão pela qual a maioria dos provedores não se preocupa em implementar uma API assíncrona real. Mas mudar isso quebraria muito mais código e provavelmente causaria muito mais ruído do que remover interfaces, já que as classes básicas têm sido a API real para provedores desde 2.0.

Atualizar uma biblioteca para não usar nenhuma das interfaces 1.1 causaria impacto quase zero em comparação com a remoção de todo o código assíncrono escrito nos últimos anos, o que seria desastroso. O compromisso é ter os dois. Qualquer código escrito hoje deve usar apis assíncrona, portanto, deixar isso de fora não faz sentido.

Qualquer código escrito hoje deve usar APIs assíncronas.

Não quero ferir muito, mas esse mundo ideal está muito longe da realidade. Async é muito difundido e infeccioso. Você simplesmente não pode contar apenas com APIs assíncronas em bibliotecas e esperar que aplicativos inteiros sejam consumidores assíncronos (alterando um _ton_ de seu código para ser assíncrono também) por capricho. Sincronizar -> Async em todos os lugares também é muito ruim por muitos impasses e razões de eficiência. Haverá código síncrono escrito por muitos anos.

Há uma grande necessidade de ambas as APIs. A questão é: não vamos remover os atuais ou atrasar sua presença para um novo conjunto hipotético e ainda não projetado. Podemos nos preocupar com o segundo / novo conjunto independentemente.

Atualizar uma biblioteca para não usar nenhuma das interfaces 1.1 causaria quase nenhum impacto em comparação com a remoção de todo o código assíncrono escrito nos últimos anos

A que você está se referindo? Não houve APIs assíncronas para a existência de tal código. Se você está dependendo dessas APIs, elas não estão em uma classe base ou interface, mas diretamente em um provedor. Isso não será afetado por isso.

Qualquer código escrito hoje deve usar apis assíncrona, portanto, deixar isso de fora não faz sentido.

Deixar de lado muitas coisas não faz sentido ... exceto pelo fato de que todos nós somos limitados por recursos (especialmente tempo). Não acredito que alguém tenha deixado algo de fora _permanentemente_. Nada está fora de questão. Simplesmente ainda não foi alcançado. Eu abriria outro problema especificamente para iniciar uma especificação em interfaces assíncronas para uma geração futura.

A que você está se referindo? Não houve APIs assíncronas para a existência de tal código. Se você está dependendo dessas APIs, elas não estão em uma classe base ou interface, mas diretamente em um provedor. Isso não será afetado por isso.

.NET 4.5 introduziu métodos assíncronos nas classes de base do provedor. Isso foi em 2012, quase 4 anos atrás, por isso faz parte da API do provedor ADO.NET por um tempo. Entity Framework 6 (lançado em 2013) depende dessas APIs assíncronas para todos os provedores.

Os métodos assíncronos já fazem parte do ADO.NET por tempo suficiente que muitas pessoas gritariam se isso não fosse incluído no .NET Core. Eu tenho _legacy code_ que usa métodos assíncronos no ADO.NET.

Estou defendendo que, uma vez que _já_ fazem parte do ADO.NET, isso também deve estar presente na nova API de interface.

Se as pessoas querem (e deveriam) usar as APIs assíncronas, elas já podem fazer
que _antes desta mudança_ usando os tipos de base. Em última análise, o pedido
para suportar as interfaces foi feito por razões de compatibilidade com versões anteriores;
adicionar métodos a uma interface _sobra completamente isso da água_.
Dito isso, é realmente possível como _métodos de extensão_ e
verificação de tipo contra os tipos-base abstratos, mas ... muito feio e não
vale a pena a dor OMI.

Então; versão curta: Eu pessoalmente não posso atrasar a adição de assíncrono ao
interfaces, pois isso destrói a única coisa que queríamos no primeiro
Lugar, colocar. Se você quiser assíncrono: você precisa codificar contra as classes base, ou usar
ferramentas que encobrem esses detalhes para você.

Estou defendendo que, como eles já fazem parte do ADO.NET, eles também devem estar presentes na nova API de interface.

Você está interpretando mal o propósito dessas interfaces ADO.NET, que é manter a compatibilidade com o código existente. Estas não são interfaces _novas_, são interfaces _existentes_. Se você deseja acessar as APIs mais recentes, faça referência aos tipos de base concretos.

@nvivo Desculpas, não estou te seguindo - estava falando sobre APIs de _interface_ - essas nunca existiram. Os tipos básicos já têm todos os mesmos *Async métodos - há algo específico que está faltando? Acho que você está argumentando que eles deveriam ser agrupados em interfaces ... sim, claro, mas esse é outro problema que eu encorajo você a abrir.

Eu preferia que eles fossem uma interface desde o início, já que as implementações básicas necessárias para fazer a abordagem atual funcionar (assíncrono sobre sincronização) são compensações terríveis para fazer toda a abordagem funcionar. Mas, também não podemos ter as duas coisas: ou eles se movem para interfaces ou estão presentes (como é o caso atual) para minimizar quebras.

Sim, acho que estamos andando em círculos aqui. Eu disse isso antes, não acho que as interfaces devam ser adicionadas _just_ para ajudar na portabilidade do código. Do ponto de vista da compatibilidade, as classes básicas têm sido a API oficial do ADO.NET desde 2005 e é isso que os provedores implementam. Qualquer coisa que use um IDbCommand ou IDbConnection pode ser facilmente portado (e deveria ter sido portado) para usar classes básicas com uma busca / substituição e não tem desvantagens.

Eu sei que você não é fã de ifdefs, mas suportar isso para uma nova plataforma só fará parte da migração de qualquer maneira.

Concordo que deveriam ter sido interfaces o tempo todo, mas como não foram, gostaria que esse problema não se repetisse. Se interfaces estão sendo adicionadas, elas devem representar pelo menos a API atual, e não o que eram há uma década. Os métodos assíncronos são parte integrante da API atual e é nessa direção que a Microsoft está se movendo há algum tempo. Ainda seria compatível com a fonte, apenas mais completo.

@mgravell

Se as pessoas desejam (e deveriam) usar as APIs assíncronas, elas já podem fazer isso _antes desta mudança_ usando os tipos básicos.

Não se trata de ser capaz de fazer nada. É sobre arquitetura. As interfaces são contratos, o .NET Core é uma nova estrutura que está adicionando este contrato a uma versão reprojetada da API.

O .NET core não deve adicionar um contrato oficial a uma nova API apenas para ajudar a migrar códigos realmente antigos, enquanto a maioria das outras coisas estarão ausentes de qualquer maneira. Se isso for uma preocupação, as pessoas simplesmente não estão procurando o suficiente pelos motivos pelos quais precisarão alterar seu código de qualquer maneira.

Se isso é tudo que a equipe está fazendo, então tudo bem .. é apenas uma má escolha, OMI.

Qualquer coisa que use um IDbCommand ou IDbConnection pode ser facilmente portado (e deveria ter sido portado) para usar classes básicas com uma busca / substituição e não tem desvantagens.

Falso. Os problemas foram discutidos inúmeras vezes neste tópico de vários autores de biblioteca com experiência em primeira mão afetada por isso.

Eu sei que você não é fã de ifdefs

Quaisquer soluções que exijam que os clientes finais usem ifdefs é uma experiência de desenvolvimento falha e não inicial, ou seja, nunca haverá um produto de sucesso exigindo que os clientes sujem seu código com #defs quando houver alternativas.

Se interfaces estão sendo adicionadas, elas devem representar pelo menos a API atual

Essas não são interfaces novas, são interfaces restauradas. As APIs atuais e futuras são as classes básicas, não essas interfaces. Não deve haver nenhum problema aqui, você pode esquecer que essas interfaces existem e continuar usando os tipos de base exatamente como antes dessas interfaces serem restauradas.

Não há mais nenhum valor sendo adicionado a este tópico. As interfaces ADO.NET existentes foram restauradas para que este encadeamento possa ser encerrado. A única coisa necessária neste tópico são atualizações para DataTable e GetSchemaTable() pois eles pertencem às interfaces existentes. Se você quiser propor mudanças arquitetônicas ou defender novas interfaces, abra um novo problema - o que impedirá que todos nesta lista recebam spam.

@mythz vamos concordar em discordar.

Apenas adicionando meus 2 centavos como outro desenvolvedor ORM, classes abstratas são sempre um cheiro de código quando não são apoiadas por uma interface. Adoraria ver novas interfaces fornecidas para corresponder às classes abstratas e assinaturas de método sobrecarregadas com uma API de interface de requisito mínimo.

Perfeito para a comunidade por se manifestar.

classes abstratas são sempre um cheiro de código quando não são apoiadas por uma interface

@psibernetic Você pode me ajudar a entender essa afirmação? Que tal isso ser um cheiro de código?

@psibernetic

As interfaces e classes abstratas nos fornecem um contrato, ambas fornecem uma abstração e uma boa definição para a API. As interfaces são mais úteis ao implementar classes que podem implementar mais de uma interface ou são subclasses de outra classe base (assumindo que seja uma grande vantagem dessa classe base). Neste caso em particular, classes concretas para Conexão, Comando e assim por diante para provedores específicos têm um forte relacionamento IS A com as definições abstratas da API. Eu realmente não consigo imaginar um cenário em que algum desenvolvedor precise adicionar uma implementação concreta para IDbConnection ou IConnection a uma subclasse. O quase único cenário será de novas classes que derivam apenas para a classe abstrata e "duplicar" a mesma definição em uma Interface é mais trabalhoso (desnecessário) para o designer de API.

Você vê uma vantagem ou cenário específico e concreto para ter duas abstrações iguais? Quando a interface fornece benefícios práticos e reais _sobre_ a classe abstrata neste design de API específico?

A única vantagem que posso imaginar para as interfaces é a compatibilidade com versões anteriores de que precisamos com as antigas para quebrar menos código em execução real que dependia dessas interfaces. Se não tivéssemos as interfaces antigas, tenho certeza de que as classes abstratas serão suficientes.

@eocampo Você está certo ao dizer que as classes abstratas provavelmente fornecem abstrações e contratos "bons o suficiente". Sempre tento fornecer interfaces muito estreitas que representam ações que podem ser executadas, como IAsyncCommand e similares. Isso permite que minhas estruturas sejam conectadas de maneiras que podem não ter sido consideradas no tempo de design da estrutura, com menos chance de NotSupportedExceptions ou NotImplementedExceptions terríveis.

@davkean O cheiro do código é que, na maioria dos casos, embora não em todos, você está exigindo que um implementador implemente ou herde todo um conjunto básico de funcionalidades que pode não ser relevante. Lembro-me de ter visto implementações de IDataReader que lêem de um cache ou na memória. Não tenho certeza se a classe abstrata DbDataReader permitiria isso, mas o nome implica não.

O modelo de melhores práticas seguido predominantemente em dot net foi expor interfaces e herdar de classes base, não foi?

O modelo de melhores práticas seguido predominantemente em dot net foi expor interfaces e herdar de classes base, não foi?

@psibernetic Bem, nem sempre. Por exemplo, esta recomendação no site do MSDN tem mais de uma década lá. E essa diretriz é muito comum a partir do .Net Framework 2.0, pelo menos.

Além disso, esta é uma boa referência das diretrizes para design de biblioteca em .Net desde os primeiros dias:

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

De qualquer forma, acho que a discussão difícil aqui é sobre dois assuntos agora:

a) As interfaces são apenas para compatibilidade com versões anteriores ou podemos "começar do zero" (quebrar o código) para permitir uma interface e design de API mais limpos.
b) Quanto podemos ir para um design moderno e limpo ao custo de não ser compatível com o framework .Net completo. (Compatibilidade especificamente entre .Net Core e Full core no acesso a dados [não é o nível mais baixo e compatibilidade obrigatória])

Do meu ponto de vista, se tivermos as classes básicas abstratas como o contrato principal e preferencial, as _interfaces_ devem corresponder às antigas apenas para compatibilidade. Eu entendo que @nvivo já declarou que depois do .Net 2.0 o contrato oficial eram as classes de base abstratas, então nós _podemos_ pensar que as interfaces não resolverão o problema de compatibilidade, mas @mythz e @mikeobrien também forneceram dados concretos aqui sobre a dependência de provedores nas interfaces 1.1.

Para parar de enviar spam e discutir os tópicos aqui, precisaremos ler novamente esta longa conversa e não sei se podemos concordar com a LISTA de tópicos específicos que estamos abordando ou se é uma boa ideia criar dois ou três novos questões para cada tópico específico. Eu sou mais da primeira sugestão porque há muitos pontos positivos aqui. Não tenho uma boa ideia de como podemos resumir tudo isso e eliminar alguns ruídos (até mesmo o meu).

Falando em interfaces, existem planos para finalmente tornar genéricas partes de System.Data? Sempre me incomodou que System.Data nunca tenha realmente atualizado sua API além do .NET 1.1, deixando as pessoas com a necessidade de usar hacks como o método de extensão .AsEnumerable () para obter um IEnumerablefora de um DataTable. Por que coleções como DataRowCollection não foram atualizadas para implementar as interfaces genéricas quando todo o resto na estrutura foi quando 2.0 saiu?

Haverá um stub System.Data com redirecionamentos de tipo? Preciso usar o ODP.NET, mas agora não consigo.

Criado dotnet / corefx # 7874

@mgravell @ploeh "Rickasaurus" implicava typeclasses no horizonte (para F # pelo menos, não tenho certeza sobre C # ou .NET em geral https://news.ycombinator.com/threads?id=Rickasaurus). Se for o caso de eles virem para todo o .NET, isso resolveria o problema?

Não sou um especialista em Haskell, mas meu entendimento é que eles permitiriam que você usasse um simples IDbConnection , IDbConnectionAsync e qualquer interface compartilhada futura após o fato, sem interromper o código ou compatibilidade binária e sem forçar os provedores de terceiros a implementar tudo. Isso, ao mesmo tempo em que mantém a facilidade de zombar.

Este é um entendimento correto? Em caso afirmativo, há alguma chance desse recurso estar chegando ao .NET de verdade?

Esta página foi útil?
0 / 5 - 0 avaliações