Exceptions in C# - c#

I am trying to write a well-documented code that will be also easy to debug if some exception is handled. Therefore my methods throw exceptions and they're gathered in the log that I can browse. However, it is not really suitable to write complex messages when throwing a new exception. I would like to write a simple utility that would gather:
name of the current class
name of the current method
parameters of the method
Can this be achieved in some easy way? Also, is there a better way to make such logs of exceptions? Perhaps this issue is being solved in a different way, so please share your experience with me :)

An exception already contains this information in its StackTrace.
To get access to it, simply use the public getter:
catch(Exception ex)
{
string trace = ex.StackTrace;
}
If you want to log it, most loggers have an overload which takes an exception. The message and stack trace will be logged.
For example, with log4net, you can use the method void Error(object message, Exception t);:
catch(Exception ex)
{
logger.Error("More details", ex);
}

hi to get the name of the method with signatures the fix is
//get the stack trace
StackTrace stackTrace = new StackTrace();
//the count may vary
MethodBase CallingMethod = stackTrace.GetFrame(2).GetMethod();
//Method called by
string _signatures = MethodInfoExtensions.GetSignature((MethodInfo)CallingMethod));
and the class
public static class MethodInfoExtensions
{
/// <summary>
/// Return the method signature as a string.
/// </summary>
/// <param name="method">The Method</param>
/// <param name="callable">Return as an callable string(public void a(string b) would return a(b))</param>
/// <returns>Method signature</returns>
public static string GetSignature(this MethodInfo method, bool callable = false)
{
var firstParam = true;
var sigBuilder = new StringBuilder();
if (callable == false)
{
if (method.IsPublic)
sigBuilder.Append("public ");
else if (method.IsPrivate)
sigBuilder.Append("private ");
else if (method.IsAssembly)
sigBuilder.Append("internal ");
if (method.IsFamily)
sigBuilder.Append("protected ");
if (method.IsStatic)
sigBuilder.Append("static ");
sigBuilder.Append(TypeName(method.ReturnType));
sigBuilder.Append(' ');
}
sigBuilder.Append(method.Name);
// Add method generics
if (method.IsGenericMethod)
{
sigBuilder.Append("<");
foreach (var g in method.GetGenericArguments())
{
if (firstParam)
firstParam = false;
else
sigBuilder.Append(", ");
sigBuilder.Append(TypeName(g));
}
sigBuilder.Append(">");
}
sigBuilder.Append("(");
firstParam = true;
var secondParam = false;
foreach (var param in method.GetParameters())
{
if (firstParam)
{
firstParam = false;
if (method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), false))
{
if (callable)
{
secondParam = true;
continue;
}
sigBuilder.Append("this ");
}
}
else if (secondParam == true)
secondParam = false;
else
sigBuilder.Append(", ");
if (param.ParameterType.IsByRef)
sigBuilder.Append("ref ");
else if (param.IsOut)
sigBuilder.Append("out ");
if (!callable)
{
sigBuilder.Append(TypeName(param.ParameterType));
sigBuilder.Append(' ');
}
sigBuilder.Append(param.Name);
}
sigBuilder.Append(")");
return sigBuilder.ToString();
}
/// <summary>
/// Get full type name with full namespace names
/// </summary>
/// <param name="type">Type. May be generic or nullable</param>
/// <returns>Full type name, fully qualified namespaces</returns>
public static string TypeName(Type type)
{
var nullableType = Nullable.GetUnderlyingType(type);
if (nullableType != null)
return nullableType.Name + "?";
if (!type.IsGenericType)
switch (type.Name)
{
case "String": return "string";
case "Int32": return "int";
case "Decimal": return "decimal";
case "Object": return "object";
case "Void": return "void";
default:
{
return string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName;
}
}
var sb = new StringBuilder(type.Name.Substring(0,
type.Name.IndexOf('`'))
);
sb.Append('<');
var first = true;
foreach (var t in type.GetGenericArguments())
{
if (!first)
sb.Append(',');
sb.Append(TypeName(t));
first = false;
}
sb.Append('>');
return sb.ToString();
}
}

Related

ADO.NET - Resilient Connection to Oracle Database using OracleClient

We have a legacy C# Windows Service application (.Net Framework 4.8) that performs some statistical analysis which usually takes hours to complete as the underlying database has millions of rows of historical data.
It works fine if there is no underlying network interruption. However, we recently started using a database which is only accessible over the VPN. Now if there is any VPN connection issue, the analysis stops.
Is there any way to recover from these transient faults silently and gracefully and continue the work?
For the SqlClient I have found a sample code at https://learn.microsoft.com/en-us/sql/connect/ado-net/step-4-connect-resiliently-sql-ado-net?view=sql-server-ver15
The sample code from the link is shown below (just in case if the link dies).
using System; // C#
using CG = System.Collections.Generic;
using QC = Microsoft.Data.SqlClient;
using TD = System.Threading;
namespace RetryAdo2
{
public class Program
{
static public int Main(string[] args)
{
bool succeeded = false;
int totalNumberOfTimesToTry = 4;
int retryIntervalSeconds = 10;
for (int tries = 1;
tries <= totalNumberOfTimesToTry;
tries++)
{
try
{
if (tries > 1)
{
Console.WriteLine
("Transient error encountered. Will begin attempt number {0} of {1} max...",
tries, totalNumberOfTimesToTry
);
TD.Thread.Sleep(1000 * retryIntervalSeconds);
retryIntervalSeconds = Convert.ToInt32
(retryIntervalSeconds * 1.5);
}
AccessDatabase();
succeeded = true;
break;
}
catch (QC.SqlException sqlExc)
{
if (TransientErrorNumbers.Contains
(sqlExc.Number) == true)
{
Console.WriteLine("{0}: transient occurred.", sqlExc.Number);
continue;
}
else
{
Console.WriteLine(sqlExc);
succeeded = false;
break;
}
}
catch (TestSqlException sqlExc)
{
if (TransientErrorNumbers.Contains
(sqlExc.Number) == true)
{
Console.WriteLine("{0}: transient occurred. (TESTING.)", sqlExc.Number);
continue;
}
else
{
Console.WriteLine(sqlExc);
succeeded = false;
break;
}
}
catch (Exception Exc)
{
Console.WriteLine(Exc);
succeeded = false;
break;
}
}
if (succeeded == true)
{
return 0;
}
else
{
Console.WriteLine("ERROR: Unable to access the database!");
return 1;
}
}
/// <summary>
/// Connects to the database, reads,
/// prints results to the console.
/// </summary>
static public void AccessDatabase()
{
//throw new TestSqlException(4060); //(7654321); // Uncomment for testing.
using (var sqlConnection = new QC.SqlConnection
(GetSqlConnectionString()))
{
using (var dbCommand = sqlConnection.CreateCommand())
{
dbCommand.CommandText = #"
SELECT TOP 3
ob.name,
CAST(ob.object_id as nvarchar(32)) as [object_id]
FROM sys.objects as ob
WHERE ob.type='IT'
ORDER BY ob.name;";
sqlConnection.Open();
var dataReader = dbCommand.ExecuteReader();
while (dataReader.Read())
{
Console.WriteLine("{0}\t{1}",
dataReader.GetString(0),
dataReader.GetString(1));
}
}
}
}
/// <summary>
/// You must edit the four 'my' string values.
/// </summary>
/// <returns>An ADO.NET connection string.</returns>
static private string GetSqlConnectionString()
{
// Prepare the connection string to Azure SQL Database.
var sqlConnectionSB = new QC.SqlConnectionStringBuilder();
// Change these values to your values.
sqlConnectionSB.DataSource = "tcp:myazuresqldbserver.database.windows.net,1433"; //["Server"]
sqlConnectionSB.InitialCatalog = "MyDatabase"; //["Database"]
sqlConnectionSB.UserID = "MyLogin"; // "#yourservername" as suffix sometimes.
sqlConnectionSB.Password = "MyPassword";
sqlConnectionSB.IntegratedSecurity = false;
// Adjust these values if you like. (ADO.NET 4.5.1 or later.)
sqlConnectionSB.ConnectRetryCount = 3;
sqlConnectionSB.ConnectRetryInterval = 10; // Seconds.
// Leave these values as they are.
sqlConnectionSB.IntegratedSecurity = false;
sqlConnectionSB.Encrypt = true;
sqlConnectionSB.ConnectTimeout = 30;
return sqlConnectionSB.ToString();
}
static public CG.List<int> TransientErrorNumbers =
new CG.List<int> { 4060, 40197, 40501, 40613,
49918, 49919, 49920, 11001 };
}
/// <summary>
/// For testing retry logic, you can have method
/// AccessDatabase start by throwing a new
/// TestSqlException with a Number that does
/// or does not match a transient error number
/// present in TransientErrorNumbers.
/// </summary>
internal class TestSqlException : ApplicationException
{
internal TestSqlException(int testErrorNumber)
{ this.Number = testErrorNumber; }
internal int Number
{ get; set; }
}
}
However, I couldn't find any helpful material for the OracleClient. Any ideas, please?

How to return a File in a different method than the main one?

I want to download a file via Results.File(). It works when executing it in the main method (app.MapGet), however returning it in a different method does do nothing. The line where it should send the file is executed in the Debugger, however it does not return, but jumps back to the main method to execute the last return (The one that never should be executed). This last return does indeed return a result.
I have also used several methods like Result.BadRequest(), however nothing is executed when it is not in the main method.
I saw people using IResult as return method but I am not sure whether this is even right.
My guess is maybe the wrong return type or executing a Task or so.
Whole methods:
app.MapGet("/", (HttpContext context) =>
{
if (context.Request.Query.ContainsKey("amount"))
{
if (context.Request.Query["amount"].Count > 1)
{
return Results.BadRequest(new { Error = "The query parameter 'amount' may only be used once." });
}
if (int.TryParse(context.Request.Query["amount"], out int amount))
{
if (amount <= 0)
{
return Results.BadRequest(new { Error = "The specified amount must be greater or equal to 1." });
}
var list = new List<string>();
for (int i = 0; i < amount; i++)
{
list.Add(Ulid.NewUlid().ToString());
}
CheckIfDownload(context, (list, null));
return Results.Json(new { Ulids = list });
}
else
{
return Results.BadRequest(new { Error = "The specified amount is not a valid number." });
}
}
string ulid = Ulid.NewUlid().ToString();
CheckIfDownload(context, (null, ulid));
return Results.Json(new { Ulid = ulid });
});
static IResult? CheckIfDownload(HttpContext context, (List<string>? list, string? single) ulidListOrSingle)
{
if (context.Request.Query.ContainsKey("download"))
{
if (context.Request.Query["download"].Count > 1)
{
return Results.BadRequest(new { Error = "The query parameter 'download' may only be used once." });
}
if (context.Request.Query["download"] == "" || (bool.TryParse(context.Request.Query["download"], out bool download) && download))
{
if (ulidListOrSingle.list != null)
{
return SendJsonFile(JsonSerializer.Serialize(ulidListOrSingle.list));
}
if (ulidListOrSingle.single != null)
{
return SendJsonFile(JsonSerializer.Serialize(ulidListOrSingle.single));
}
return Results.BadRequest(new { Error = "An unknown error occurred." });
}
}
return null;
}
static IResult SendJsonFile(string data)
{
byte[] buffer = Encoding.UTF8.GetBytes(data);
var stream = new MemoryStream(buffer);
return Results.File(stream, "application/json", $"UlidGenerator_{DateTime.Now:MM-dd-yyyy_HH-mm-ss}.json");
}
when you inline an entire method block (not just a single expression), you must return what you want to be the output of the block. In your case you are not capturing the result of SendJsonFile to return it:
app.MapGet("/download", () =>
{
string data = "json data";
var result = SendJsonFile(data);
return result;
});

Recursively get all children of SAP Gui Session in c#

I am looking to loop through all SAP GuiComponents in c# but am struggling to get all the children of children of a gui session.
This is what I have so far (initially passing session.ActiveWindow.Children to nodes):
private void IterateFindIDByNAme(GuiComponentCollection nodes, string searchstring)
{
if (foundID == "")
{
foreach (GuiComponent node in (GuiComponentCollection)nodes)
{
var comp = node;
if (comp.Name.Contains(searchstring))
foundID = comp.Id;
else
{
try
{
FindIDByNAme((GuiComponentCollection)node, searchstring);
}
catch { }
}
}
}
}
Its able to get all the child elements of session.ActiveWindow but when trying to cast all the children to GuiComponentCollections its falling over.
This would be irrelevant if I could use the FindByName function but for some reason it is not working on the SAP screen i am currently working on (it does work on others, not sure why).
The ID of the field is:
wnd[0]/usr/subBCA_SUB_HEADER:SAPLBANK_BDT_CTRL:0100/subSUBS_DETAIL:SAPLBUSS:0028/ssubGENSUB:SAPLBUSS:4038/subA01P02:SAPLBCA_DYN_CN_CNMANO:0002/ctxtBCA_DYN_CONTRACT_ORGENTRY-ORGUNIT
and the function I am trying is:
((GuiTextField)session.FindByName("BCA_DYN_CONTRACT_ORGENTRY-ORGUNIT", "GuiTextField")).Text = "Test";
The findbyid works fine, but not findbyname?
I know this is 2 questions, but kind of related.
ctxtBCA_DYN_CONTRACT_ORGENTRY-ORGUNIT
The type of the control is a GuiCTextField not GuiTextField:
session.FindByName("BCA_DYN_CONTRACT_ORGENTRY-ORGUNIT", "GuiCTextField")
Sample Code :
public DateTime? GetModificationDate(int employeeID)
{
var session = SapHelper.GetActiveSession();
Console.WriteLine("Recherche de la measure [A5 ou A6] avec un motif 90...");
var window = session.BeginTransaction("PA20", "Afficher données de base personnel");
window.FindByName<GuiCTextField>("RP50G-PERNR").Text = employeeID.ToString();
window.FindByName<GuiCTextField>("RP50G-CHOIC").Text = "Mesures (0000)";
window.FindByName<GuiCTextField>("RP50G-SUBTY").Text = null;
window.FindByName<GuiButton>("btn[20]").Press(); // list view
if (window.Text == "Afficher données de base personnel")
{
Console.WriteLine(">> " + window.FindByName<GuiStatusbar>("sbar").Text);
return null;
}
/* Index Type Title Tooltip
0 GuiTextField Début Date de début
1 GuiTextField Fin Date de fin
2 GuiCTextField Mes. Catégorie de mesure
3 GuiTextField Dés. cat. mesure Dés. cat. mesure
4 GuiCTextField MotMe Motif mesure
5 GuiTextField Dés. motif mesure Dés. motif mesure
6 GuiCTextField Client Statut propre client
7 GuiCTextField Activité Statut d'activité
8 GuiCTextField Paiement Statut paiement part */
var result = window.FindByName<GuiTableControl>("MP000000TC3000").AsEnumerable()
.Select(x => new
{
Start = x.GetText(0),
Category = x.GetCText(2),
CategoryText = x.GetText(3),
Reason = x.GetCText(4),
ReasonText = x.GetText(5),
})
.Where(x => (x.Category == "A5" || x.Category == "AG") && x.Reason == "90")
.FirstOrDefault();
if (result == null)
{
Console.WriteLine(">> aucune measure [A5 ou A6] avec un motif 90 trouvée");
return null;
}
else
{
Console.WriteLine(">> {0}:[{1}]{2} [{3}]{4}",
result.Start, result.Category, result.Category, result.Reason, result.ReasonText);
return DateTime.ParseExact(result.Start, "yyyy/MM/dd", CultureInfo.InvariantCulture);
}
}
Xy.Sap :
#region namespace Xy.Sap
namespace Xy.Sap
{
using System.Reflection;
using sapfewse;
using saprotwr.net;
using COMException = System.Runtime.InteropServices.COMException;
public static class SapHelper
{
public static GuiSession GetActiveSession()
{
var rot = new CSapROTWrapper().GetROTEntry("SAPGUI");
if (rot == null)
throw SapException.NotOpened();
var app = (GuiApplication)rot.GetType().InvokeMember("GetScriptingEngine", BindingFlags.InvokeMethod, null, rot, null);
var connectedSession = app.Connections.Cast<GuiConnection>()
.SelectMany(x => x.Children.Cast<GuiSession>())
.Where(x => !string.IsNullOrEmpty(x.Info.User))
.FirstOrDefault();
if (connectedSession == null)
throw SapException.NotOpened();
return connectedSession;
}
}
public class SapException : Exception
{
public SapException(string message) : base(message) { }
public static SapException NotOpened()
{
return new SapException("Veuillez lancer le SAP et de connecter avec votre identité");
}
}
public static class SapExtensions
{
#region GuiSession
/// <summary>
/// Shortcut for PA20 query
/// </summary>
/// <param name="session"></param>
/// <param name="employeeID"></param>
/// <param name="it">Infotype ID</param>
/// <param name="sty">Subtype ID</param>
/// <param name="asListView"></param>
/// <returns></returns>
public static GuiFrameWindow QueryPA20(this GuiSession session, int employeeID, string it, string sty = null, bool asListView = false)
{
var window = session.BeginTransaction("PA20", "Afficher données de base personnel");
window.FindByName<GuiCTextField>("RP50G-PERNR").Text = employeeID.ToString();
window.FindByName<GuiCTextField>("RP50G-CHOIC").Text = it;
window.FindByName<GuiCTextField>("RP50G-SUBTY").Text = sty;
window.FindByName<GuiButton>(asListView ? "btn[20]" : "btn[7]").Press();
if (window.Text == "Afficher données de base personnel")
{
var exception = new InvalidOperationException(string.Format("Failed to access to personal information of {0}", employeeID));
exception.Data["Employee ID"] = employeeID;
exception.Data["Infotype"] = it;
exception.Data["Subtype"] = sty;
exception.Data["View"] = asListView ? "ListView[Mont]" : "RecordView[Glasses]";
exception.Data["Status Message"] = window.FindByName<GuiStatusbar>("sbar").Text;
throw exception;
}
return window;
}
/// <summary>
/// Shortcut for PA30 query
/// </summary>
/// <param name="session"></param>
/// <param name="employeeID"></param>
/// <param name="it">Infotype ID</param>
/// <param name="sty">Subtype ID</param>
/// <param name="asListView"></param>
/// <returns></returns>
public static GuiFrameWindow QueryPA30(this GuiSession session, int employeeID, string it, string sty = null)
{
var window = session.BeginTransaction("PA30", "Gérer données de base HR");
window.FindByName<GuiCTextField>("RP50G-PERNR").Text = employeeID.ToString();
window.FindByName<GuiCTextField>("RP50G-CHOIC").Text = it;
window.FindByName<GuiCTextField>("RP50G-SUBTY").Text = sty;
window.FindByName<GuiButton>("btn[6]").Press();
if (window.Text == "Gérer données de base HR")
{
var exception = new InvalidOperationException(string.Format("Failed to access to personal information of {0}", employeeID));
exception.Data["Employee ID"] = employeeID;
exception.Data["Infotype"] = it;
exception.Data["Subtype"] = sty;
exception.Data["Status Message"] = window.FindByName<GuiStatusbar>("sbar").Text;
throw exception;
}
return window;
}
/// <summary>
/// Start a new transaction and return the active window
/// </summary>
public static GuiFrameWindow BeginTransaction(this GuiSession session, string transactionID, string expectedTitle)
{
return session.BeginTransaction(transactionID,
x => x.Text == expectedTitle,
x =>
{
var exception = new InvalidOperationException(string.Format("Failed to open transaction : {0}", transactionID));
exception.Data["Transaction ID"] = transactionID;
exception.Data["Expected Title"] = expectedTitle;
exception.Data["Current Title"] = x.Text;
exception.Data["Status Message"] = x.FindByName<GuiStatusbar>("sbar").Text;
return exception;
});
}
public static GuiFrameWindow BeginTransaction(this GuiSession session, string transactionID, Predicate<GuiFrameWindow> validation, Func<GuiFrameWindow, string> errorFormatter)
{
return session.BeginTransactionImpl(transactionID, validation, x => new Exception(errorFormatter(x)));
}
public static GuiFrameWindow BeginTransaction(this GuiSession session, string transactionID, Predicate<GuiFrameWindow> validation, Func<GuiFrameWindow, Exception> errorBuilder)
{
return session.BeginTransactionImpl(transactionID, validation, errorBuilder);
}
private static GuiFrameWindow BeginTransactionImpl(this GuiSession session, string transactionID, Predicate<GuiFrameWindow> validation, Func<GuiFrameWindow, Exception> errorBuilder)
{
// force current transaction to end, preventing any blocking(eg: model dialog)
session.EndTransaction();
session.StartTransaction(transactionID);
var window = session.ActiveWindow;
if (!validation(window))
throw errorBuilder(window);
return window;
}
#endregion
#region GuiFrameWindow
public static TSapControl FindByName<TSapControl>(this GuiFrameWindow window, string name)
{
try
{
return (TSapControl)window.FindByName(name, typeof(TSapControl).Name);
}
catch (COMException e)
{
var writer = new StringWriter();
writer.WriteLine("The control could not be found by name and type.");
writer.WriteLine("Name : " + name);
writer.WriteLine("Type : " + typeof(TSapControl).Name);
throw new Exception(writer.ToString(), e);
}
}
#endregion
#region GuiTableControl
/// <summary>Note: Do not iterate through this ienumerable more than once</summary>
public static IEnumerable<GuiTableRow> AsEnumerable(this GuiTableControl table)
{
var container = table.Parent as dynamic;
string name = table.Name, type = table.Type;
int rowCount = table.VerticalScrollbar.Maximum;
Func<GuiTableControl> getTable = () => container.FindByName(name, type) as GuiTableControl;
for (int i = 0; i <= rowCount; i++)
{
getTable().VerticalScrollbar.Position = i;
yield return getTable().Rows.Item(0) as GuiTableRow;
}
}
public static TSapControl GetCell<TSapControl>(this GuiTableRow row, int column)
{
return (TSapControl)row.Item(column);
}
public static string GetCText(this GuiTableRow row, int column)
{
return row.GetCell<GuiCTextField>(column).Text;
}
public static string GetText(this GuiTableRow row, int column)
{
return row.GetCell<GuiTextField>(column).Text;
}
#endregion
}
}
#endregion
EDIT: The link to the LINQPad script mentioned in comment below no longer works. I have re-uploaded it here.
This script helps you to browse SAP GUI:
List control type, name, id, content
List properties and methods
Highlight control
Generate selector : .FindByName<GuiTableControl>("MP000000TC3000")
In case anyone wants to know how to get child elements of any gui component (this will simply write all ids to the console):
private void LoopAllElements(GuiComponentCollection nodes)
{
foreach (GuiComponent node in (GuiComponentCollection)nodes)
{
Console.WriteLine(node.Id);
if (node.ContainerType)
{
var children = ((node as dynamic).Children as GuiComponentCollection);
LoopAllElements(children);
}
}
}
This will loop through all child elements of any GUIComponentCollection you pass to it such as
LoopAllElements(session.Children)
There is probably a better linq way of doing this but this should get you started.
Please also check out this gist created by Xiaoy312 https://gist.github.com/Xiaoy312/a1424bd34acae92554105b27b0c80971
this has a lot of useful functions to debug your current sap session which i have found extremely useful.
You can either view the code as it has a number of functions you can use such as improved findbyid and findbyname or run it in linqpad https://www.linqpad.net/Download.aspx which will allow you to interogate the current SAP session.
If I understood correctly we have to manually start SAP Logon to make line below working:
var rot = new CSapROTWrapper().GetROTEntry("SAPGUI");
Am I right?
If so maybe someone can explain me why at the line:
var app = (GuiApplication)rot.GetType().InvokeMember("GetScriptingEngine", BindingFlags.InvokeMethod, null, rot, null);
I am getting exception:
An exception of type 'System.Runtime.InteropServices.COMException'
occurred in System.Dynamic.dll but was not handled in user code
Additional information: Error loading type library/DLL. (Exception
from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY))
I've already tried to change target platform from Any CPU to x86 and x64 and didn't have success in resolving this problem.
But when I run SAPGUI using following script:
var SapGuiApp = new GuiApplication();
string development = "Development ERP System";
var connection = SapGuiApp.OpenConnection(development);
instead of running SAP Logon manually described above problem is not reproducing.
But in this case I have other problem: GUI is different and for second option GUI looks corrupted - some of GUI controls are not displayed (please see screenshot). Please give me advice what can be wrong? Thank you in advance
UPDATE: I found solution that solves issue described below. You have to use object instead of var while you are trying to get session connection:
CSapROTWrapper sapROTWrapper = new CSapROTWrapper();
object rot = sapROTWrapper.GetROTEntry("SAPGUI");
object engine = rot.GetType().InvokeMember("GetScriptingEngine", System.Reflection.BindingFlags.InvokeMethod, null, rot, null);
GuiConnection connection = (engine as GuiApplication).OpenConnection(connectionName);
GuiSession session = connection.Children.ElementAt(0) as GuiSession;

Issue creating a generic Data Wrapper

I am having an issue creating my (mostly) generic data access wrapper
Code
/// <summary>
/// Get the results of a stronly-typed IList Object
/// </summary>
/// <typeparam name="T">Strongly-Typed class of objects that should be returned</typeparam>
/// <param name="_DBType">The type of database to use</param>
/// <param name="_Qry">The query to run</param>
/// <param name="_QryType">The Query Type to run</param>
/// <param name="_ParamNames">The Parameters' names to pass to the query, if any</param>
/// <param name="_ParamVals">The Parameters' values to pass to the query, if any</param>
/// <param name="_ParamDTs">The Parameters' data types to pass to the query, if any</param>
/// <param name="_ShouldCache">Should we cache the response</param>
/// <param name="_CacheID">Cache item name</param>
/// <returns>Strongly Typed ilist of objects</returns>
public static IList<T> GetResults<T>(Enumerators.DatabaseTypes _DBType,
string _Qry,
CommandType _QryType,
string[] _ParamNames = null,
object[] _ParamVals = null,
Enumerators.DataTypes[] _ParamDTs = null,
bool _ShouldCache = false,
string _CacheID = "") where T : new()
{
// Create a reference to a potential already cached IList
IList<T> _CachedItem = _Cache.Get<IList<T>>(_CacheID);
// If we're already cached, there's no need to fire up the data access objects, so return the cached item instead
if (_CachedItem != null && _ShouldCache)
{
return _CachedItem;
}
else
{
// Fire up our data access object
switch (_DBType)
{
case Enumerators.DatabaseTypes.SqlServer:
SqlServer.Access db = new SqlServer.Access();
break;
case Enumerators.DatabaseTypes.SqlCE:
SqlCE.Access db = new SqlCE.Access();
break;
case Enumerators.DatabaseTypes.MySql:
MySql.Access db = new MySql.Access();
break;
case Enumerators.DatabaseTypes.OLE:
Ole.Access db = new Ole.Access();
break;
case Enumerators.DatabaseTypes.ODBC:
Odbc.Access db = new Odbc.Access();
break;
default:
db = null;
}
using (db)
{
try
{
// create a new ilist reference of our strongly typed class
IList<T> _Query = default(IList<T>);
// set the query type
db.QueryType = _QryType;
// set the query text
db.Query = _Qry;
// make sure we've got some parameters, if we do the set them to our db access object
if (_ParamNames != null)
{
// set the parameter names
db.ParameterNames = _ParamNames;
// set the parameter values
db.ParameterValues = _ParamVals;
// set the parameter data types
db.ParameterDataTypes = _ParamDTs;
}
// start using our db access :) Fire off the GetResults method and return back a SqlDataReader to work on
using (DbDataReader r = db.GetResults())
{
// make sure the data reader actually exists and contains some results
if (r != null)
{
// map the data reader to our strongly type(s)
_Query = Map<T>(r);
}
r.Close();
}
// check if we should cache the results
if (_ShouldCache)
{
// if so, set the query object to the cache
_Cache.Set<IList<T>>(_Query, _CacheID);
}
// return our strongly typed list
return _Query;
}
catch (DbException dEx)
{
// Catch an exception if any, an write it out to our logging mechanism, in addition to adding it our returnable message property
_Msg += "Wrapper.GetResults Exception: " + dEx.Message + db.Message;
ErrorReporting.WriteEm.WriteItem(dEx, "o7th.Class.Library.Data.Wrapper.GetResults", _Msg);
// make sure this method returns a default List
return default(IList<T>);
}
catch (Exception ex)
{
// Catch an exception if any, an write it out to our logging mechanism, in addition to adding it our returnable message property
_Msg += "Wrapper.GetResults Exception: " + ex.Message + db.Message;
ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.Wrapper.GetResults", _Msg);
// make sure this method returns a default List
return default(IList<T>);
}
}
}
}
Access Class - SqlServer
using System;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace o7th.Class.Library.Data.SqlServer
{
internal class Access : IDisposable
{
#region "Properties"
// Set the type of query we are running
private CommandType _QT;
internal CommandType QueryType { set { _QT = value; } }
// Set the actual query text to run
private string _Qry;
internal string Query { set { _Qry = value; } }
// Set the parameter names if there are any
private string[] _PNs;
internal string[] ParameterNames { set { _PNs = value; } }
// Set the parameter values if there are any
private object[] _PVs;
internal object[] ParameterValues { set { _PVs = value; } }
// Set the actual Sql Data Types if there are any
private DataTypes[] _DTs;
internal DataTypes[] ParameterDataTypes { set { _DTs = value; } }
// Check to see if there are any parameters passed
private bool AreParams() {
// Check to see if the values and names are null first
if (_PVs != null && _PNs != null) {
try {
Type _t_pv = _PVs.GetType();
Type _t_pn = _PNs.GetType();
if (_t_pv.IsArray && _t_pn.IsArray) {
return (_PVs.Length > 0 && _PNs.Length > 0) ? true : false;
} else {
return false;
}
} catch {
// yes I meant to do this, we really don't need to get the exception here
return false;
}
} else {
return false;
}
}
// Get a return message if any
private string _Msg;
internal string Message { get { return _Msg; } }
// Set the official Sql Reader object
private SqlDataReader _Rdr;
// Set the official Sql Connection object
private SqlConnection _Conn;
// Set the official Sql Command object
private SqlCommand _Cmd;
// Hack for seeing if we're disposed already
private bool disposedValue;
#endregion
// Constructor
internal Access() {
Invoke();
}
// Official Constructor. We can thread these 2 becuase they are not being used yet, and it makes it slightly more efficient
internal void Invoke() {
try {
Parallel.Invoke(() => {
_Conn = new SqlConnection(AssemblyProperties.GetConnectionString());
}, () =>
{
_Cmd = new SqlCommand();
});
}
catch (SqlException dEx)
{
// Catch an exception if any, an write it out to our logging mechanism, in addition to adding it our returnable message property
_Msg = "Access.Invoke Exception: " + dEx.Message;
ErrorReporting.WriteEm.WriteItem(dEx, "o7th.Class.Library.Data.SqlServer.Access.Invoke", _Msg);
}
catch (Exception ex)
{
_Msg = "Access.Invoke Exception: " + ex.Message;
ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.Invoke", _Msg);
}
}
/// <summary>
/// Return a SqlDataReader based on the properties passed to this class
/// </summary>
/// <returns></returns>
internal SqlDataReader GetResults()
{
try {
// check for parameters
if (AreParams()) {
PrepareParams(_Cmd);
}
// set our connection
_Cmd.Connection = _Conn;
// set the type of query to run
_Cmd.CommandType = _QT;
// set the actual query to run
_Cmd.CommandText = _Qry;
// open the connection
_Cmd.Connection.Open();
// prepare the command with any parameters that may have gotten added
_Cmd.Prepare();
// Execute the SqlDataReader, and set the connection to close once returned
_Rdr = _Cmd.ExecuteReader(CommandBehavior.CloseConnection);
// clear out any parameters
_Cmd.Parameters.Clear();
// return our reader object
return (!_Rdr.HasRows) ? null : _Rdr;
}
catch (SqlException SqlEx)
{
_Msg += "Acccess.GetResults SqlException: " + SqlEx.Message;
ErrorReporting.WriteEm.WriteItem(SqlEx, "o7th.Class.Library.Data.SqlServer.Access.GetResults", _Msg);
return null;
}
catch (Exception ex) {
_Msg += "Acccess.GetResults Exception: " + ex.Message;
ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.GetResults", _Msg);
return null;
}
}
/// <summary>
/// Execute a non-return query, and return the success
/// </summary>
/// <returns></returns>
internal bool Execute() {
try {
// check for parameters
if (AreParams()) {
PrepareParams(_Cmd);
}
// set our connection
_Cmd.Connection = _Conn;
// set the type of query to run
_Cmd.CommandType = _QT;
// set the actual query to run
_Cmd.CommandText = _Qry;
// open the connection
_Cmd.Connection.Open();
// prepare the command with any parameters that may have gotten added
_Cmd.Prepare();
// execute the non-returnable query against the database
_Cmd.ExecuteNonQuery();
// clear out any parameters
_Cmd.Parameters.Clear();
// executed successfully (otherwise would have thrown an exception)
return true;
}
catch (SqlException SqlEx)
{
_Msg += "Access.Execute SqlException: " + SqlEx.Message;
ErrorReporting.WriteEm.WriteItem(SqlEx, "o7th.Class.Library.Data.SqlServer.Access.Execute", _Msg);
return false;
}
catch (Exception ex) {
_Msg += "Access.Execute Exception: " + ex.Message;
ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.Execute", _Msg);
return false;
}
}
/// <summary>
/// Execute a query with a return value. Used in Selecting the ID of the last inserted record.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="_DefVal"></param>
/// <returns></returns>
internal T ExecuteWithReturn<T>(T _DefVal) {
try {
T _Ret;
// check for parameters
if (AreParams()) {
PrepareParams(_Cmd);
}
// set our connection
_Cmd.Connection = _Conn;
// set the type of query to run
_Cmd.CommandType = _QT;
// set the actual query to run
_Cmd.CommandText = _Qry;
// open the connection
_Cmd.Connection.Open();
// prepare the command with any parameters that may have gotten added
_Cmd.Prepare();
T _T = (T)_Cmd.ExecuteScalar();
_Ret = (_T is DBNull) ? default(T) : _T;
// clear out _T
_T = default(T);
// clear out any parameters
_Cmd.Parameters.Clear();
// return the single return value from the query run
return _Ret;
}
catch (SqlException SqlEx)
{
_Msg += "Access.ExecuteWithReturn SqlException: " + SqlEx.Message;
ErrorReporting.WriteEm.WriteItem(SqlEx, "o7th.Class.Library.Data.SqlServer.Access.ExecuteWithReturn", _Msg);
return default(T);
} catch (Exception ex) {
_Msg += "Access.ExecuteWithReturn Exception: " + ex.Message;
ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.ExecuteWithReturn", _Msg);
return default(T);
}
}
/// <summary>
/// Prepare our parameters, adding them and forcing a valid data length
/// </summary>
/// <param name="objCmd"></param>
protected void PrepareParams(SqlCommand objCmd) {
try {
// set our initial Data Size
int _DataSize = 0;
// get the number of Parameter Values passed in
int _PCt = _PVs.GetUpperBound(0);
// begin array check
Type _t_dt = _DTs.GetType();
// start looping over our parameters
for (int i = 0; i <= _PCt; ++i) {
// make sure that the data types are actually an array
if (_t_dt.IsArray) {
// select which datatype, and force the official size
switch ((int)_DTs[i]) {
case 0:
case 33:
case 6:
case 9:
case 13:
case 19:
_DataSize = 8;
break;
case 1:
case 3:
case 7:
case 10:
case 12:
case 21:
case 22:
case 23:
case 25:
_DataSize = _PVs[i].ToString().Length;
break;
case 2:
case 20:
_DataSize = 1;
break;
case 5:
_DataSize = 17;
break;
case 8:
case 17:
case 15:
_DataSize = 4;
break;
case 14:
_DataSize = 16;
break;
case 31:
_DataSize = 3;
break;
case 32:
_DataSize = 5;
break;
case 16:
_DataSize = 2;
break;
}
// add our parameter to the command object
objCmd.Parameters.Add(_PNs[i], (SqlDbType)_DTs[i], _DataSize).Value = _PVs[i];
} else {
// if the datatypes were not set, try to add them generically
objCmd.Parameters.AddWithValue(_PNs[i], _PVs[i]);
}
}
// clean up
_PNs = null;_PVs = null;_DTs = null;
} catch (Exception ex) {
_Msg += "Access.PrepareParams Exception: " + ex.Message;
ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.PrepareParams", _Msg);
}
}
#region "Dispose Support"
protected virtual void Dispose(bool disposing)
{
if (!disposedValue && disposing) {
try
{
_Qry = string.Empty;
_Rdr.Close();
_Rdr.Dispose();
_Cmd.Connection.Close();
_Cmd.Dispose();
if (_Conn.State == ConnectionState.Open)
{
SqlConnection.ClearAllPools();
_Conn.Close();
_Conn.Dispose();
}
_Msg = null;
}
catch(Exception ex) {
ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.Dispose", "");
}
}
disposedValue = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
The issue I am having is, because I need the switch statement to select which database Access method should be used, it is throwing The name 'db' does not exist in the current context errors all over the place.
I've also attempted to var db; prior to the switch statement with the Implicitly-typed local variables error.
I will not know which type of database will be used until it is time to actually use this, so how can I make sure this works?
IDbAccess db;
switch (_DBType)
{
case Enumerators.DatabaseTypes.SqlServer:
SqlServer.Access db = new SqlServer.Access();
break;
case Enumerators.DatabaseTypes.SqlCE:
SqlCE.Access db = new SqlCE.Access();
break;
case Enumerators.DatabaseTypes.MySql:
MySql.Access db = new MySql.Access();
break;
case Enumerators.DatabaseTypes.OLE:
Ole.Access db = new Ole.Access();
break;
case Enumerators.DatabaseTypes.ODBC:
Odbc.Access db = new Odbc.Access();
break;
default:
db = null;
}
is producing Cannot implicitly convert type 'SqlCE.Access to SqlServer.Access' errors down the line, as well as A local variable 'db' is already defined in this scope and A local variable named 'db' cannot be declared in this scope because it would give a different meaning to 'db' which is already used in a parent of current scope to denote something else
All of those various database providers should have some sort of base class they all inherit from or an interface that they all implement. That base class/interface should have all of the operations that you use later on in the method. Create a variable of that base type/interface before the switch and then assign that variable a value in each case.

Reduce casting and better styling

Consider the following (nasty) code:
/// <summary>
/// Calls matching process error code on response.Code
/// </summary>
/// <param name="response">Actually will be of type Response or extend it</param>
/// <returns>true for successful response, false otherwise</returns>
private static bool ProcessErrorCode(object response)
{
bool isOkay = false;
const string unknown = "UNKNOWN";
string errCode = unknown;
if (response.GetType() == typeof(Response<AResponseCode>))
{
AResponseCode code = ((Response<AResponseCode>)response).Code;
isOkay = code == AResponseCode.Ok;
errCode = code.ToString();
}
if (response.GetType() == typeof(Response<BResponseCode>))
{
BResponseCode code = ((Response<BResponseCode>)response).Code;
isOkay = code == BResponseCode.Ok;
errCode = code.ToString();
}
if (response.GetType() == typeof(DataResponse<CResponseCode,string>))
{
CResponseCode code = ((DataResponse<CResponseCode, string>)response).Code;
isOkay = code == CResponseCode.Ok;
errCode = code.ToString();
}
if (isOkay)
{
return true;
}
string msg = "Operation resulted in error code:" + errCode;
LogErrorCode(msg);
return false;
}
I am trying to figure out a way to reduce castings and imrove the method style.
I have no code ownership on Response<TResponseCode>, DataResponse<TResponseCode,string>, AResponseCode, BResponseCode, CResponseCode
response parameter will be of type Response<TResponseCode> or inherit from it (DataResponse<TResponseCode,string> : Response<TResponseCode>)
All *ResponseCode are Enums and they all have an *ResponseCode.Ok entry
You could use a generic subroutine for this:
// Mocking your classes, just to check if it compiles. You don't need this.
class Response<T> {
public T Code { get { return default(T); } }
}
enum AResponseCode { Ok }
enum BResponseCode { Ok }
enum CResponseCode { Ok }
static void LogErrorCode(string msg) { }
private static bool ProcessErrorCode(object response)
{
bool isOkay;
string errCode;
if (!TryProcessErrorCode(response, AResponseCode.Ok, out isOkay, out errCode))
if (!TryProcessErrorCode(response, BResponseCode.Ok, out isOkay, out errCode))
TryProcessErrorCode(response, CResponseCode.Ok, out isOkay, out errCode);
if (isOkay)
{
return true;
}
string msg = "Operation resulted in error code:" + errCode;
LogErrorCode(msg);
return false;
}
// TResponseCode is automatically inferred by passing the okCode
private static bool TryProcessErrorCode<TResponseCode>(
object response, TResponseCode okCode,
out bool isOkay, out string errCode)
{
var resp = response as Response<TResponseCode>;
if (resp == null)
{
isOkay = false;
errCode = "UNKNOWN";
return false;
}
else
{
isOkay = okCode.Equals(resp.Code);
errCode = resp.Code.ToString();
return true;
}
}
Ideally, you'd give Response<TResponseCode> a common interface with an 'OK' function on it. Seeing as you can't, your solution is going to look a bit hacky.
Given that constraint, I'd extract a couple of methods - static bool IsResponseOk(object response) and static string GetResponseError(object response) - which would result in easier to read code, but still not brilliant.
You can get a slightly cleaner solution using generics:
private static void TestErrorCode<TCode>(object response, TCode ok, ref bool isOkay, ref string errCode)
{
Response<TCode> responseTyped = response as Response<TCode>;
if (responseTyped == null)
{
return;
}
TCode code = responseTyped.Code;
isOkay = code.Equals(ok);
errCode = code.ToString();
return;
}
private static bool ProcessErrorCode(object response)
{
bool isOkay = false;
string errCode = "UNKNOWN";
TestErrorCode(response, AResponseCode.Ok, ref isOkay, ref errCode);
TestErrorCode(response, BResponseCode.Ok, ref isOkay, ref errCode);
TestErrorCode(response, CResponseCode.Ok, ref isOkay, ref errCode);
if (isOkay)
{
return true;
}
LogErrorCode("Operation resulted in error code:" + errCode);
return false;
}
I'd swap a switch statement for all the ifs.
From a performance perspective, I'd replace .GetType() == typeof() with the is operator, eg:
if( response is DataResponse<CResponseCode,string> )
The following benchmark shows a performance improvement of roughly 3x on my machine:
var f = new List<string>();
var dummy = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
for(int i=0; i< REPS; i++)
if(f.GetType() == typeof( List<string> )) dummy++;
sw.Stop();
Console.WriteLine(sw.Elapsed);
sw.Reset();
sw.Start();
for(int i=0; i< REPS; i++)
if(f is List<string> ) dummy++;
sw.Stop();
Console.WriteLine(sw.Elapsed);
// Outputs
00:00:00.0750046
00:00:00.0261598
I can't imagine to do something with this without finding person responsible for writing this XResponseZZZ.
Maybe others are smarter than me. But im convinced that you should find the guy and presuade to him that it should be XResponse responsibility to known if specified code is okay and what error message should be thrown. This is C coding style and it is done bad.
I looked at the Heinzi or Daniel generic solutions and I like them most.
Here's a solution with dynamic (can be edited to use reflection instead), untested and not recommended, but short.
It should work identically to your original function:
Since you rely on response.GetType() == typeof(XXX), which is not the same as response is XXX (inheritance is excluded), types.Contains(response.GetType()) is equivalent.
Since you guarantee that all these types have a property Code which is (presumably) an enum type that contains a value Ok, the dynamic portion should always succeed.
The fact that only the eligible types are included means that 'coincidences' cannot happen.
Cons:
Performance?
If the underlying types change, you will not be protected at compile-time.
private static bool ProcessErrorCode(object response)
{
var types = new[] { typeof(Response<AResponseCode>), typeof(Response<BResponseCode>), typeof(DataResponse<CResponseCode, string>)};
var errCode = !types.Contains(response.GetType())
?"UNKNOWN"
:(string)(((dynamic)response).Code.ToString());
if(errCode == "Ok") return true;
LogErrorCode("Operation resulted in error code:" + errCode);
return false;
}
This replaces my old answer as it clearly bombed :) (them's the breaks)
I don't expect this'll do any better - but you might find the approach interesting.
The basic idea is to define a result type that contains your IsOkay and ErrCode values, and then define a dictionary of delegates (keyed by the type of the object that they handle) that you consult. To add new handlers you simply add another delegate to the dictionary.
public class ResponseResult{
public bool IsOkay;
public string ErrCode;
}
public static class ResponseExtracter
{
//STARTING OFF HERE WITH THE NEW VERSION OF YOUR METHOD
public static bool ProcessErrorCode(object response)
{
Func<object, ResponseResult> processor = null;
ResponseResult result = new ResponseResult()
{
IsOkay = false, ErrCode = "UNKNOWN"
};
//try to get processor based on object's type
//then invoke it if we find one.
if (_processors.TryGetValue(response.GetType(), out processor))
result = processor(response);
if (result.IsOkay)
return true;
string msg = "Operation resulted in error code:" + result.ErrCode;
LogErrorCode(msg);
return false;
}
//A lot better no?
//NOW FOR THE INFRASTRUCTURE
static Dictionary<Type, Func<object, ResponseResult>> _processors
= new Dictionary<Type, Func<object, ResponseResult>>();
static ResponseExtracter()
{
//this can be replaced with self-reflection over methods
//with attributes that reference the supported type for
//each method.
_processors.Add(typeof(Response<AResponseCode>), (o) =>
{
AResponseCode code = ((Response<AResponseCode>)o).Code;
return new ResponseResult
{
IsOkay = code == AResponseCode.Ok,
ErrCode = code.ToString()
};
});
_processors.Add(typeof(Response<BResponseCode>), (o) =>
{
BResponseCode code = ((Response<BResponseCode>)o).Code;
return new ResponseResult
{
IsOkay = code == BResponseCode.Ok,
ErrCode = code.ToString()
};
});
_processors.Add(typeof(DataResponse<CResponseCode, string>),
(o) =>
{
CResponseCode code = ((DataResponse<CResponseCode, string>)o).Code;
return new ResponseResult
{
IsOkay = code == CResponseCode.Ok,
ErrCode = code.ToString()
};
});
}
}
Performance-wise it's better than it looks because the Dictionary<Type, TValue> is actually hashing on the integer value that underpins the type's TypeHandle. This is in fact an Int64 - so only the first 32 bits are used, but in practise it's highly unlikely that two types share the same first 32 bits of their handle.
I would be inclined to try to get the main body of code quite simple like this:
/// <summary>
/// Calls matching process error code on response.Code
/// </summary>
/// <param name="response">Actually will be of type Response or extend it</param>
/// <returns>true for successful response, false otherwise</returns>
private static bool ProcessErrorCode(object response)
{
Func<Type, Func<object, string>> process = ...;
var errCode = process(response.GetType())(response);
if (errCode != "Ok")
{
LogErrorCode("Operation resulted in error code:" + errCode);
}
return errCode == "Ok";
}
Then it just becomes a matter of defining the Func<Type, Func<object, string>>. This could be done using a separate method or by dependency injection.
The separate method would look like this:
private static Func<Type, Func<object, string>> _process = null;
private static Func<Type, Func<object, string>> GetProcessFunc()
{
if (_process == null)
{
var d = new Dictionary<Type, Func<object, string>>()
{
{ typeof(Response<AResponseCode>), r => ((Response<AResponseCode>)r).Code.ToString() },
{ typeof(Response<BResponseCode>), r => ((Response<BResponseCode>)r).Code.ToString() },
{ typeof(DataResponse<CResponseCode,string>), r => ((DataResponse<CResponseCode,string>)r).Code.ToString() },
};
_process = t =>
{
if (d.Contains(t))
{
return o => d[t];
}
return o => "UNKNOWN";
};
}
return _process;
}
This still has the same code, per se, but it is now better separated and can be replaced with a dependency injection approach more easily. :-)
Refactored thus, and I'd have been faster if IE hadn't freaked out and reloaded the page spontaneously. :(
I agree with others that this ideally should be gutted and mocked/encapsulated/extended to get where you want, but working within the method as a point of least impact and as an instructional:
bool isOkay = false;
string errCode; // you don't need to default this to anything because it won't be empty if you need to use it
if (response is Response<AResponseCode>) // "is" is a faster and more expressive test
{
var code = (response as Response<AResponseCode>).Code; // "as" is a faster conversion
isOkay = (code == AResponseCode.Ok); // Readability is awesome!
errCode = code.ToString(); // This still sucks
}
else if (response is Response<BResponseCode>) // elsif logically exclusive tests
{
var code = (response as Response<BResponseCode>).Code;
isOkay = code == BResponseCode.Ok;
errCode = code.ToString();
}
else if (response is DataResponse<CResponseCode,string>)
{
var code = (response as DataResponse<CResponseCode, string>).Code;
isOkay = (code == CResponseCode.Ok);
errCode = code.ToString();
}
// Test what you mean to express, i.e. if this isn't ok I need to log it
if (!isOkay)
{
// you don't need a temp variable here...
// and why aren't you just throwing an exception here? Is not being ok a regular unexceptional event, but one you still need to log? Really?
LogErrorCode("Operation resulted in error code:" + errCode);
}
// And try not to return bool literals, it's again unexpressive
return isOkay;

Categories

Resources