practices - try catch sql exception c#
rendimiento de retorno con try catch, ¿cómo puedo resolverlo (9)
Tengo un código:
using (StreamReader stream = new StreamReader(file.OpenRead(), Encoding))
{
char[] buffer = new char[chunksize];
while (stream.Peek() >= 0)
{
int readCount = stream.Read(buffer, 0, chunksize);
yield return new string(buffer, 0, readCount);
}
}
Ahora tengo que rodear esto con un bloque try-catch
try
{
using (StreamReader stream = new StreamReader(file.OpenRead(), Encoding))
{
char[] buffer = new char[chunksize];
while (stream.Peek() >= 0)
{
int readCount = stream.Read(buffer, 0, chunksize);
yield return new string(buffer, 0, readCount);
}
}
}
catch (Exception ex)
{
throw ExceptionMapper.Map(ex, file.FullName)
}
No puedo ver ninguna manera de hacer lo que quiero.
EDITAR El método tiene la firma.
public IEnumerable<string> ReadPieces(int pieces)
Necesito una try catch
con una llamada al ExceptionMapper
en el caso de catch
. El método es utilizado diferido por todos los que llaman.
Las excepciones que tengo que atrapar provienen de estas llamadas.
File.OpenRead()
stream.Read()
Aquí hay un fragmento de código, que funciona para mí (no alcancé la condición de error).
while (true)
{
T ret = null;
try
{
if (!enumerator.MoveNext())
{
break;
}
ret = enumerator.Current;
}
catch (Exception ex)
{
// handle the exception and end the iteration
// probably you want it to re-throw it
break;
}
// the yield statement is outside the try catch block
yield return ret;
}
Debido a que desea mantener el flujo abierto durante la enumeración Y lidiar con las excepciones Y cerrar adecuadamente el identificador del archivo, no creo que pueda usar un acceso directo de enumeración regular (el bloque de iterador, rendimiento-retorno / rendimiento- descanso).
En su lugar, simplemente haga lo que el compilador hubiera hecho por usted y agregue algunos:
Al implementar IEnumerator usted mismo, también puede agregar IDisposable
public class LazyStream : IEnumerable<string>, IDisposable
{
LazyEnumerator le;
public LazyStream(FileInfo file, Encoding encoding)
{
le = new LazyEnumerator(file, encoding);
}
#region IEnumerable<string> Members
public IEnumerator<string> GetEnumerator()
{
return le;
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return le;
}
#endregion
#region IDisposable Members
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
if (le != null) le.Dispose();
}
disposed = true;
}
}
#endregion
class LazyEnumerator : IEnumerator<string>, IDisposable
{
StreamReader streamReader;
const int chunksize = 1024;
char[] buffer = new char[chunksize];
string current;
public LazyEnumerator(FileInfo file, Encoding encoding)
{
try
{
streamReader = new StreamReader(file.OpenRead(), encoding);
}
catch
{
// Catch some generator related exception
}
}
#region IEnumerator<string> Members
public string Current
{
get { return current; }
}
#endregion
#region IEnumerator Members
object System.Collections.IEnumerator.Current
{
get { return current; }
}
public bool MoveNext()
{
try
{
if (streamReader.Peek() >= 0)
{
int readCount = streamReader.Read(buffer, 0, chunksize);
current = new string(buffer, 0, readCount);
return true;
}
else
{
return false;
}
}
catch
{
// Trap some iteration error
}
}
public void Reset()
{
throw new NotSupportedException();
}
#endregion
#region IDisposable Members
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
if (streamReader != null) streamReader.Dispose();
}
disposed = true;
}
}
#endregion
}
}
No probé esto, pero creo que está cerca.
utilizado de esta manera:
using (var fe = new LazyStream(new FileInfo("c://data.log"), Encoding.ASCII))
{
foreach (var chunk in fe)
{
Console.WriteLine(chunk);
}
}
EDITAR: Me había olvidado totalmente de agregar las ubicaciones de bloque try-catch. Ups.
Desafortunadamente, no ha descrito qué es lo que quiere hacer, pero podría intentar forzar a los usuarios de la función que está definiendo para que los intente / atrape a sí mismos:
public IEnumerable<string> YourFunction(...)
{
//Your code
}
//later:
//...
try{
foreach( string s in YourFunction(file) )
{
//Do Work
}
}
catch(Exception e){
throw ExceptionMapper.Map(e, file.FullName);
}
Echa un vistazo a esta pregunta . Puede yield break
en el caso excepcional, yield value
después de la cláusula try/catch
. Me preocupaba el rendimiento, pero se cree que el try
no tiene una influencia en el rendimiento, mientras que no se lanzan excepciones.
Editar: esta respuesta es realmente incorrecta, debido a las razones detalladas en los comentarios: "SOLO la generación del enumerador está envuelta, pero no la iteración en sí". - Pero estoy dejando esta respuesta aquí como un ejemplo de cómo a veces lo que puede parecer que funciona no se debe a las complejidades del lenguaje.
Considérelo un cuento de advertencia, gracias a UOS. =)
Aquí hay una opción: separe su método en dos métodos, uno público y otro privado. El método público es un contenedor (con try / catch) alrededor de una llamada al método privado, que es su generador. Por ejemplo:
public IEnumerable<string> YourFunction(...)
{
try
{
return _yourFunction(...);
}
catch (Exception e)
{
throw ExceptionMapper.Map(e, file.FullName);
}
}
private IEnumerable<string> _yourFunction(...)
{
// Your code here
}
Esto permitirá que sus usuarios confíen en que el generador tenga incorporado el manejo de excepciones. Además, podría realizar más validación en sus entradas en el método público, lanzando las excepciones que sean necesarias debido a entradas incorrectas, y realizar esas validaciones inmediatamente cuando se llama al método, en lugar de esperar la primera vez que se enumera el enumerable.
No se pueden usar construcciones de yield
en un bloque try / catch. Restrinja el bloque try al código que puede lanzar, no todo. Si no puedes hacer esto, estás fuera de suerte, tendrás que ir más lejos en la pila.
Otra consideración: si está consumiendo un método de implementación IEnumerable
que yield
una excepción internamente, no puede detectar ese error individual y continuar la enumeración, consulte la sección "Manejo de excepciones" de https://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
ejemplo:
void Main()
{
// even is okay, odd will cause exception
var operations = new[] { 2, 16, 5 /* ! */, 8, 91 /* ! */ };
var results = process(operations);
var en = results.GetEnumerator();
"Regular Enumeration".Title();
testEnumeration(en);
results = process(operations, ex => log("Handled: {0}", ex.Message));
en = results.GetEnumerator();
"Handled Exceptions".Title();
testEnumeration(en);
results = process(operations, ex => log("Handled+: {0}", ex.Message), true);
en = results.GetEnumerator();
"Handled Exceptions and Continue".Title();
testEnumeration(en);
}
/// run the test and debug results
void testEnumeration(IEnumerator en) {
int successCount = 0, failCount = 0;
bool keepGoing = false;
do {
try {
log("==={0}===", "before next");
keepGoing = en.MoveNext();
log("==={0}=== (keepGoing={1}, curr={2})", "after next", keepGoing, en.Current);
// did we have anything?
if(keepGoing) {
var curr = en.Current;
log("==={0}===", "after curr");
log("We did it? {0}", curr);
successCount++;
}
}
catch(InvalidOperationException iopex) {
log(iopex.Message);
failCount++;
}
} while(keepGoing);
log("Successes={0}, Fails={1}", successCount, failCount);
}
/// enumerable that will stop completely on errors
IEnumerable<int> process(IEnumerable<int> stuff) {
foreach(var thing in stuff) {
if(thing % 2 == 1) {
throw new InvalidOperationException("Aww, you broked it");
}
yield return thing;
}
}
/// enumerable that can yield from exceptions
IEnumerable<int> process(IEnumerable<int> stuff, Action<Exception> handleException, bool yieldOnExceptions = false) {
bool shouldYield = false;
foreach(var thing in stuff) {
var result = thing;
try {
if(thing % 2 == 1) {
throw new InvalidOperationException("Aww, you broked it");
}
shouldYield = true;
}
catch(Exception ex) {
handleException(ex);
// `yield break` to stop loop
shouldYield = yieldOnExceptions;
if(yieldOnExceptions) result = -1; // so it returns a different result you could interpret differently
}
if(shouldYield) yield return result;
}
}
void log(string message, params object[] tokens) {
Console.WriteLine(message, tokens);
}
resultados en
Regular Enumeration --------------------------- ===before next=== ===after next=== (keepGoing=True, curr=2) ===after curr=== We did it? 2 ===before next=== ===after next=== (keepGoing=True, curr=16) ===after curr=== We did it? 16 ===before next=== Aww, you broked it ===before next=== ===after next=== (keepGoing=False, curr=16) Successes=2, Fails=1 Handled Exceptions -------------------------- ===before next=== ===after next=== (keepGoing=True, curr=2) ===after curr=== We did it? 2 ===before next=== ===after next=== (keepGoing=True, curr=16) ===after curr=== We did it? 16 ===before next=== Handled: Aww, you broked it ===after next=== (keepGoing=True, curr=8) ===after curr=== We did it? 8 ===before next=== Handled: Aww, you broked it ===after next=== (keepGoing=False, curr=8) Successes=3, Fails=0 Handled Exceptions and Continue --------------------------------------- ===before next=== ===after next=== (keepGoing=True, curr=2) ===after curr=== We did it? 2 ===before next=== ===after next=== (keepGoing=True, curr=16) ===after curr=== We did it? 16 ===before next=== Handled+: Aww, you broked it ===after next=== (keepGoing=True, curr=-1) ===after curr=== We did it? -1 ===before next=== ===after next=== (keepGoing=True, curr=8) ===after curr=== We did it? 8 ===before next=== Handled+: Aww, you broked it ===after next=== (keepGoing=True, curr=-1) ===after curr=== We did it? -1 ===before next=== ===after next=== (keepGoing=False, curr=-1) Successes=5, Fails=0
Tenga en cuenta que la Current
del enumerador está "atascada" en el último MoveNext
exitoso durante la "enumeración regular", mientras que las excepciones manejadas le permiten completar el ciclo.
Una estrategia es que eficaz (si es un poco más difícil de leer ...) es romper y envolver cada sección que pueda yield return
llamada de yield return
real. Esto soluciona el problema, por lo que el yield
sí no está en un bloque try / catch, pero las partes que podrían fallar todavía están contenidas.
Aquí hay una posible implementación de su código:
StreamReader stream = null;
char[] buffer = new char[chunksize];
try
{
try
{
stream = new StreamReader(file.OpenRead(), Encoding);
}
catch (Exception ex)
{
throw ExceptionMapper.Map(ex, file.FullName);
}
int readCount;
Func<bool> peek = () =>
{
try
{
return stream.Peek() >= 0;
}
catch (Exception ex)
{
throw ExceptionMapper.Map(ex, file.FullName);
}
};
while (peek())
{
try
{
readCount = stream.Read(buffer, 0, chunksize);
}
catch (Exception ex)
{
throw ExceptionMapper.Map(ex, file.FullName);
}
yield return new string(buffer, 0, readCount);
}
}
finally
{
if (stream != null)
{
stream.Dispose();
stream = null;
}
}
intente este enfoque:
public IEnumerable<ReturnData> toto()
{
using (StreamReader stream = new StreamReader(File.OpenRead(""), Encoding.UTF8))
{
char[] buffer = new char[1];
while (stream.Peek() >= 0)
{
ReturnData result;
try
{
int readCount = stream.Read(buffer, 0, 1);
result = new ReturnData(new string(buffer, 0, readCount));
}
catch (Exception exc)
{
result = new ReturnData(exc);
}
yield return result;
}
}
}
public class ReturnData
{
public string Data { get; private set; }
public Exception Error { get; private set; }
public bool HasError { get { return Error != null; } }
public ReturnData(string data)
{
this.Data = data;
}
public ReturnData(Exception exc)
{
this.Error = exc;
}
}
Solo debe tener cuidado con este enfoque: deberá filtrar las excepciones según la gravedad. Algunas excepciones tendrán que detener todo el proceso, otras solo se pueden omitir y registrar.