.NET: Qual exceção a ser lançada quando uma configuração necessária está ausente?

123

Aqui está um cenário padrão:

if(string.IsNullOrEmpty(Configuration.AppSettings["foobar"]))
   throw new SomeStandardException("Application not configured correctly, bozo.");

O problema é que não tenho muita certeza de qual exceção SomeStandardExceptiondeve ser.

Examinei o Framework 3.5 e encontrei dois prováveis ​​candidatos: ConfigurationExceptione ConfigurationErrorsException.

System.Configuration.ConfigurationException

A exceção que é lançada quando ocorre um erro no sistema de configuração.

Observações

A ConfigurationExceptionexceção será lançada se o aplicativo tentar ler ou gravar dados no arquivo de configuração, mas não tiver êxito. Alguns motivos possíveis para isso podem incluir XML malformado no arquivo de configuração, problemas de permissão de arquivo e propriedades de configuração com valores inválidos.

Nota:

O ConfigurationExceptionobjeto é mantido para compatibilidade com versões anteriores. O ConfigurationErrorsException objeto o substitui pelo sistema de configuração.

Essa exceção realmente parece perfeita para o que eu preciso, mas foi marcada como obsoleta.

Isso nos leva ao intrigante ConfigurationErrorsException:

System.Configuration.ConfigurationErrorsException

O valor atual não é um dos valores EnableSessionState.

Como você pode ver, sua documentação é completamente inútil. (É assim na ajuda local e on-line.) Um exame da turma mostra que é um exagero drástico para o que eu quero.

Em poucas palavras, preciso de uma exceção padrão que deve ser lançada quando uma configuração do aplicativo estiver ausente ou contiver um valor inválido. Você pensaria que o Framework tivesse uma exceção para os aplicativos. (Aparentemente, mas foi marcado como obsoleto e foi substituído por algo muito maior em escopo.)

Quais soluções, se houver, vocês estão usando para isso, e eu vou ter que aceitar e lançar minha própria exceção?

Editar Adendos

Alguns perguntaram se eu poderia ou não fornecer um valor padrão e continuar. Em certos casos, sim, e nesses casos, a exceção não seria lançada. No entanto, para determinadas configurações, isso não se aplica. Por exemplo: nomes e credenciais do servidor de banco de dados, servidores de autenticação e caminhos para aplicativos de terceiros instalados.

Também vale a pena notar que o aplicativo no qual estou trabalhando principalmente é um aplicativo de console em execução no modo em lote e quero que ele lance uma exceção capturada pelo método principal e registrada adequadamente se a coisa não estiver configurada adequadamente. (É o código legado que eu herdei e atualmente apenas assume que tudo é pêssego.)

Mike Hofer
fonte
1
A documentação para System.Configuration.ConfigurationErrorsException foi atualizada.
Slolife

Respostas:

36

Você não está limitado ao lançar exceções às exceções existentes no Framework. Se você decidir usar exceções existentes, não precisará seguir absolutamente a documentação à risca. A documentação descreverá como a estrutura usa uma determinada exceção, mas não implica nenhuma limitação em como você escolhe usar / reutilizar uma exceção existente.

É seu aplicativo - desde que você o documente e indique claramente a exceção que será lançada no caso específico de um valor de configuração ausente, você poderá usar qualquer exceção que desejar. Se você deseja uma indicação muito específica de um valor ausente , considere gravar sua própria exceção ConfigurationSettingMissing:

[Serializable]
public class ConfigurationMissingException : ConfigurationErrorsException
{}

EDIT: Escrever sua própria exceção neste caso traz o benefício adicional de garantir que nunca haverá qualquer confusão sobre de onde a exceção vem - a estrutura ou seu aplicativo. A estrutura nunca lançará suas exceções personalizadas.

UPDATE: Concordo com os comentários, por isso alterei a subclasse para ConfigurationErrorsException from Exception. Eu acho que geralmente é uma boa ideia subclassar exceções personalizadas das exceções existentes do Framework sempre que possível, evitando a classe Exception, a menos que você precise de uma exceção específica do aplicativo.

Dave Swersky
fonte
17
Eu discordo um pouco. Se você usar uma exceção existente, ela deverá ser EXATAMENTE como a documentação diz que é usada; caso contrário, você confundirá aqueles que a seguirem. Se você quiser fazer algo diferente, crie sua própria exceção, como você disse.
22380 Robert C. Barth
1
Discordo. A menos que você tenha um cenário para capturar sua exceção personalizada, improvável no caso de um erro de configuração, é melhor reutilizar um Tipo existente (ConfigurationErrorsException). E se você criar um Type personalizado, eu o derivaria de um Type relacionado, como ConfigurationErrorsException.
Joe Joe
@ Robert & Joe - Não estou sugerindo que as exceções existentes do Framework sejam usadas de maneira inconsistente com a função pretendida, apenas que haja alguma flexibilidade, desde que o caso específico (valor ausente) se encaixe no caso geral (ConfigurationErrorsException).
Dave Swersky
1
Pode haver problemas com isso se você estiver lançando a exceção em um aplicativo ASP.NET, pois o ConfigurationErrorsException e as classes derivadas dele não serão capturadas no método OnError protegido ou pelo evento Global ASAX Error. Veja esta pergunta que eu postei .... stackoverflow.com/questions/25299325/… #
Mick
48

Pessoalmente, eu usaria InvalidOperationException , pois é um problema com o estado do objeto - não com o sistema de configuração. Afinal, você não deve permitir que essas configurações sejam definidas por código e também não? A parte importante aqui não é que não havia linha no app.config, mas que uma informação necessária não estava presente.

Para mim, o ConfigurationException (e seu substituto, o ConfigurationErrorsException - apesar dos documentos enganosos do MSDN) são por causa de erros ao salvar, ler etc. da Configuração.

Mark Brackett
fonte
I optou por InvalidOperationException, como ele é tratado pela página ASP.NET protegido OnError método, enquanto ConfigurationErrorsException e todas as exceções derivadas dela não são
Mick
2
Isso realmente me surpreenderia, se eu receber uma InvalidOperationException se eu errar na configuração.
keuleJ
18

Como Daniel Richardson disse, o ConfigurationErrorsException é o único a ser usado. Em geral, é recomendável criar seus próprios tipos de exceção personalizados se você tiver um cenário para lidar com eles. No caso de erros de configuração, que geralmente são fatais, esse raramente é o caso; portanto, geralmente é mais apropriado reutilizar o tipo ConfigurationErrorsException existente.

Antes do .NET 2.0, a recomendação era usar System.Configuration.ConfigurationException . O ConfigurationException ficou obsoleto no .NET 2.0, por motivos que nunca foram claros para mim, e a recomendação foi alterada para usar o ConfigurationErrorsException.

Eu uso um método auxiliar para lançar a exceção, para que seja fácil alterar a exceção que está sendo lançada em um local ao migrar do .NET 1.x para 2.0, ou se a Microsoft decidir alterar a recomendação novamente:

if(string.IsNullOrEmpty(Configuration.AppSettings("foobar")))
{
   throw CreateMissingSettingException("foobar");
}

...

private static Exception CreateMissingSettingException(string name)
{
    return new ConfigurationErrorsException(
        String.Format
        (
        CultureInfo.CurrentCulture,
        Properties.Resources.MissingConfigSetting,
        name
        )
        );
}
Joe
fonte
16

Que tal System.Configuration.SettingsPropertyNotFoundException?

Laurent
fonte
IMO, esta é a resposta real. +1
LC
Na verdade não, porque: Fornece uma exceção para objetos SettingsProperty que não foram encontrados. Que vem de: Usado internamente como a classe que representa os metadados sobre uma propriedade de configuração individual. O que não é o mesmo que um valor ausente de configuração.
Karol Haliński 18/03
7

ConfigurationErrorsExceptioné a exceção correta a ser lançada na situação que você descreve. Uma versão anterior da documentação do MSDN para ConfigurationErrorsExceptionfaz mais sentido.

http://msdn.microsoft.com/en-us/library/system.configuration.configurationerrorsexception(VS.80).aspx

O resumo e as observações anteriores do MSDN são:

  • A exceção que é lançada quando ocorre um erro no sistema de configuração.
  • A ConfigurationErrorsException exceção é lançada quando ocorre algum erro enquanto as informações de configuração estão sendo lidas ou gravadas.
Daniel Richardson
fonte
7
from documentation: "Esta API suporta a infraestrutura do produto e não se destina a ser usada diretamente em seu código. Inicializa uma nova instância da classe ConfigurationErrorsException."
21816 Oleg Sh
6

A classe ConfigurationElement (que é a classe base de muitas classes relacionadas à configuração, como ConfigurationSection) possui um método chamado OnRequiredPropertyNotFound (também existem outros métodos auxiliares). Talvez você possa ligar para eles.

O OnRequiredPropertyNotFound é implementado assim:

protected virtual object OnRequiredPropertyNotFound(string name) {
    throw new ConfigurationErrorsException(SR.GetString("Config_base_required_attribute_missing", new object[] { name }), this.PropertyFileName(name), this.PropertyLineNumber(name)); }
Gaspar Nagy
fonte
1

Eu sugaria e rolaria por conta própria ... mas antes de fazer isso, é possível que o sistema assuma um valor padrão para esta configuração? Geralmente, eu tento fazer isso para todas as configurações que podem ser perdidas pelo pessoal da Ops Management ... (ou talvez eu deva dizer, para o maior número possível de configurações - para algumas, claramente não é apropriado que o sistema tome uma decisão padrão. ..)

em geral, uma exceção personalizada não exige muito esforço ... aqui está um exemplo ...

[Serializable]
public class MyCustomApplicationException : ApplicationException
{
    #region privates
    #endregion privates

    #region properties
    #endregion properties

    public MyCustomApplicationException (string sMessage,
        Exception innerException)
        : base(sMessage, innerException) { }
    public MyCustomApplicationException (string sMessage)
        : base(sMessage) { }
    public MyCustomApplicationException () { }

    #region Serializeable Code
    public MyCustomApplicationException (
       SerializationInfo info, StreamingContext context)
        : base(info, context) { }
    #endregion Serializeable Code
}
Charles Bretana
fonte
2
MSDN: ... um aplicativo que precisa criar suas próprias exceções, [...] derivar exceções personalizadas da classe Exception. Pensou-se originalmente que exceções personalizadas deveriam derivar da classe ApplicationException; no entanto, na prática, não foi encontrado um valor significativo.
Gaspar Nagy
Sim, acho que foi porque os programadores de MS que codificaram a estrutura derivaram inúmeras exceções de CLR do ApplicationException, destruindo assim o significado da distinção. Ainda faço isso, pois não vejo mal algum ...
Charles Bretana
1

Um método alternativo que você poderia usar para seus arquivos de configuração seria usar seções de configuração personalizadas em vez de AppSettings . Dessa forma, você pode especificar que uma propriedade IsRequirede o sistema de configuração cuidem dessa verificação para você. Se a propriedade estiver ausente, ela será lançada, ConfigurationErrorsExceptionentão suponho que seja compatível com a resposta de que você deve usar essa exceção no seu caso.

Yogh
fonte
0

Minha regra geral seria:

  1. Se o caso da configuração ausente não for muito comum e eu acredito que nunca desejaria lidar com esse caso de maneira diferente de outras exceções, apenas uso a classe "Exception" básica com uma mensagem apropriada:

    lançar nova exceção ("minha mensagem aqui")

  2. Se eu quiser, ou achar que há uma alta probabilidade de que eu queira lidar com esse caso de uma maneira diferente da maioria das outras exceções, eu lançaria meu próprio tipo, como as pessoas já sugeriram aqui.

Hershi
fonte
0

Costumo discordar da premissa de sua pergunta:

Em poucas palavras, preciso de uma exceção padrão que deve ser lançada quando uma configuração do aplicativo estiver ausente ou contiver um valor inválido. Você pensaria que o Framework tivesse uma exceção para os aplicativos. (Aparentemente, mas foi marcado como obsoleto e foi substituído por algo muito maior em escopo.)

De acordo com a documentação do MSDN em System.Exception ( Exception Class , você realmente não deve lançar exceções para erros de entrada do usuário, por motivos de desempenho (o que foi apontado por outros usuários no Stack Overflow e em outros lugares). bem - por que sua função não pode retornar false se a entrada do usuário é inserida incorretamente e depois o aplicativo é encerrado normalmente? Isso parece ser mais um problema de design do que um problema com o qual a exceção deve ser lançada.

Como outros já apontaram, se você realmente tiver que lançar uma exceção - por qualquer motivo - não há motivo para não definir seu tipo de exceção herdando System.Exception.

Matt Jordan
fonte
2
Por que exatamente o desempenho importaria nesse cenário específico? IUC, a exceção será lançada exatamente uma vez e o processo provavelmente será interrompido posteriormente (é claro, depois de mostrar um bom pop-up ao usuário). (continua ...)
2
Retornar um código de erro em vez de lançar uma exceção pode ser doloroso, pois você terá que propagar as informações de erro na pilha de chamadas. Você deve usar exceções para erros relativamente raros que podem ser propagados por vários quadros de pilha. Ambos se aplicam aqui.
1
Você perdeu algo: "quando uma configuração do aplicativo está ausente ou contém um valor inválido", isso não é o mesmo que uma entrada incorreta do usuário. Esta é uma configuração básica que está faltando. Deve ser uma exceção personalizada e deve interromper a execução do aplicativo.
jcollum
2
Nesse aplicativo em particular, é quase inteiramente orientado pelas configurações no arquivo de configuração. Se as configurações não estiverem nele (onde estão criptografadas), o aplicativo não pode continuar e deve terminar. Uma exceção é praticamente necessária.
Mike Hofer
1
Isso certamente pode entrar na semântica, e a estrutura do seu código obviamente ditará a melhor implementação para sua situação. Ainda assim, se você tivesse um domínio livre no design, eu ainda usaria um objeto para registrar o erro e uma saída normal em uma exceção, sendo o desempenho um problema ou não.
Matt Jordan
-2

Você pode tentar herdar a exceção XML ou apenas usá-la.

StingyJack
fonte