Issue creating a generic Data Wrapper - c#

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.

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?

Entity Framework - Get Scalar

I'm trying to create a GetScalar method. We use a lot of DataTables in the legacy systems. I'm converting them to EF.
Here's the goal. This is a simple method that takes a Stored Procedure name and parameters, then returns the First Row, First Column of the result set.
public object GetScalar(string command, CommandType type = CommandType.StoredProcedure, List<SqlParameter> parameterList = null)
{
try
{
using (var cnn = new SqlConnection(ConnectionString))
{
using (var cmd = new SqlCommand(command, cnn))
{
cmd.CommandType = type;
if (parameterList != null)
{
foreach (var p in parameterList)
{
cmd.Parameters.Add(p);
}
}
cmd.Connection.Open();
object obj = cmd.ExecuteScalar();
cmd.Connection.Close();
cmd.Parameters.Clear();
return obj;
}
}
}
catch (Exception ex)
{
Logging.LogError(ex, "MSSqlUtility.GetScalar");
return -1;
}
}
I'd like to have a similar method in EF. Here's what I have so far, but this returns all columns - not just the first.
protected T SelectScalar<T>(string inStoredProcedure, ICollection<SqlParameter> inParameters = null)
{
T result = default(T);
if (inParameters != null && inParameters.Count > 0)
{
string paramNames = string.Join(",", inParameters.Select(parameter => parameter.ParameterName).ToList());
string sqlString = inStoredProcedure + " " + paramNames;
object[] paramValues = inParameters.Cast<object>().ToArray();
result = Database.SqlQuery<T>(sqlString, paramValues).FirstOrDefault();
}
else
{
result = Database.SqlQuery<T>(inStoredProcedure).FirstOrDefault();
}
return result;
}
Example usage. This returns a string object - "John Doe".
public string GetUserName(string employeeID)
{
if (string.IsNullOrWhiteSpace(employeeID))
{
return string.Empty;
}
var parameters = new Collection<SqlParameter>();
parameters.Add(StoredProcedureParameterBuilder.StringParam("#EmployeeID", employeeID, 20));
return this.SelectScalar<string>("dbo.GetUserName", parameters).Trim();
}
The SQL query looks something like this:
SELECT
FirstName + ' ' + Last Name
,EmployeeID
FROM Users
Changing the stored procedure isn't an option - we need both columns in other contexts. The current code returns both the name and the ID. I'd like to fetch just the first column, first row, of whatever my query may spit back.
Ended up having to do a bit more manual work. If anyone has a better solution, I'm all ears, but this works for my use case:
/// <summary>
/// Selects the first item in the query's result.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="inStoredProcedure">The in stored procedure.</param>
/// <param name="inParameters">The in parameters.</param>
/// <returns>The first object in the specified type T.</returns>
protected T SelectScalar<T>(string inStoredProcedure, ICollection<SqlParameter> inParameters = null)
{
using (System.Data.IDbCommand command = Database.Connection.CreateCommand())
{
try
{
command.CommandText = inStoredProcedure;
command.CommandTimeout = command.Connection.ConnectionTimeout;
if(inParameters != null && inParameters.Count > 0)
{
string paramNames = string.Join(",", inParameters.Select(parameter => parameter.ParameterName).ToList());
command.CommandText += " " + paramNames;
foreach (var param in inParameters)
{
command.Parameters.Add(param);
}
}
Database.Connection.Open();
return (T)command.ExecuteScalar();
}
finally
{
Database.Connection.Close();
command.Parameters.Clear();
}
}
}

CRM - Workflow Activity - Error: The given key was not present in the dictionary.

I'm doing a Workflow Activity that comes from a CRM process .
I have two inputs that are EntityReference and are being filled in the process.
Don't printing the trace that is after the try . Just enter the catch. And i donĂ½ know why.
My code is:
public class WK_DecorrerObjetivo : CodeActivity
{
//inputs dialog --- // input alvo
[Input("Alvo")]
[ReferenceTarget("xpto_alvo")]
public InArgument<EntityReference> alvo { get; set; }
//input actividade xpto_atividadeobjetivoid
[Input("Actividade Objetivo")]
[ReferenceTarget("xpto_atividadedeobjetivo")]
public InArgument<EntityReference> atividadeObjetivo { get; set; }
protected override void Execute(CodeActivityContext Execontext)
{
ITracingService _tracing;
IWorkflowContext context = null;
IOrganizationServiceFactory serviceFactory = null;
IOrganizationService service = null;
OrganizationServiceContext serviceContext = null;
try
{
#region Get Work Flow Context
context = Execontext.GetExtension<IWorkflowContext>();
serviceFactory = Execontext.GetExtension<IOrganizationServiceFactory>();
service = serviceFactory.CreateOrganizationService(context.InitiatingUserId);
serviceContext = new OrganizationServiceContext(service);
_tracing = Execontext.GetExtension<ITracingService>();
_tracing.Trace("inicio do try");
FetchExpression query = new FetchExpression(string.Format(Resources.GetTemplateAtividade, context.PrimaryEntityId));
// Obtain result from the query expression.
Entity new_alvo = (Entity)context.InputParameters["Target"];
var alvoGUID = ((EntityReference)new_alvo["xpto_alvo"]).Id;
Entity retrieveTemp = service.Retrieve("xpto_alvo", ((EntityReference)new_alvo["xpto_alvo"]).Id, new ColumnSet("xpto_utilizador", "xpto_conta", "xpto_contacto", "xpto_alvoid", "xpto_name", "createdon", "xpto_estado", "xpto_resultadoimportacao", "xpto_objetivoassociadoid", "xpto_alvo"));
OptionSetValue tipoAtividade = (OptionSetValue)retrieveTemp.Attributes["xpto_tipoatividade"];
switch (tipoAtividade.Value)
{
case 0:
_tracing.Trace("entrou no case 0 - compromisso");
break;
case 1:
_tracing.Trace("entrou no case 1 - phonecall");
break;
case 2:
_tracing.Trace("entrou no case 2 - task");
break;
default:
break;
}
//serviceContext.SaveChanges(); _tracing.Trace("savechanges");
}
catch (Exception ex)
{
string msgErro;
if (ex.InnerException != null)
{
msgErro = ex.InnerException.Message;
}
else
{
msgErro = ex.Message;
}
throw new InvalidPluginExecutionException(string.Format("Erro ao decorrer objetivo: {0}", msgErro));
}
}
}
Thank's.
The option set value you are trying to access "xpto_tipoatividade" isn't included in the list of columns to fetch in the retrieve request. Also always check if the attribute exists in the returned entity attribute collection and safe cast it or use an extension method to get a default value.
var retrieveTemp = service.Retrieve("xpto_alvo",
((EntityReference) new_alvo["xpto_alvo"]).Id,
new ColumnSet(
"xpto_tipoatividade", //<-- add xpto_tipoatividade to the list of attributes to fetch
"xpto_utilizador",
"xpto_conta",
"xpto_contacto",
"xpto_alvoid",
"xpto_name",
"createdon",
"xpto_estado",
"xpto_resultadoimportacao",
"xpto_objetivoassociadoid",
"xpto_alvo"));
var tipoAtividade = retrieveTemp.GetAttributeValue<OptionSetValue>("xpto_tipoatividade");
if (tipoAtividade == null)
{
_tracing.Trace("tipoAtividade is null, returning");
return;
}
switch (tipoAtividade.Value)
{
....
}

Exceptions in 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();
}
}

Error converting data type varchar to float. Prepare statement

I have been searching for a solution to this and so far found nothing that works despite some others having similar issues. I am running an SQL statement using the following code (sorry about the hideous formatting. That is what we use here):
/// <summary>
/// Executes a prepared statement with the parameters passed to AddParameter(parameterName, parameterValue) and creates a recordset.
/// </summary>
///
/// <param name="sqlQuery">The sql statement to execute.</param>
public DbStandardResponseType ExecutePreparedStatementWithParametersQuery (string sqlQuery)
{
DbStandardResponseType dbFactoryResponse = new DbStandardResponseType();
if (String.IsNullOrEmpty (sqlQuery))
{
dbFactoryResponse.ExceptionMessage = "No query string passed.";
dbFactoryResponse.Success = false;
dbFactoryResponse.UserFriendlyMessage = "No query string passed.";
return dbFactoryResponse;
}
try
{
//attempt to prepare our connection
dbFactoryResponse = PrepareConnection();
if (!dbFactoryResponse.Success)
{
return dbFactoryResponse;
}
m_dbFactoryDatabaseCommand.CommandText = sqlQuery;
m_dbFactoryDatabaseCommand.CommandType = CommandType.Text;
if (m_parameterName.Count != 0)
{
for (int i = 0; i < m_parameterName.Count; i++)
{
//create a new parameter object and assign its values before adding it to the connection object
DbParameter parameter = m_dbFactoryDatabaseCommand.CreateParameter();
parameter.Value = m_parameterValue[i];
parameter.ParameterName = m_parameterName[i];
m_dbFactoryDatabaseCommand.Parameters.Add (parameter);
}
m_parameterName.Clear();
m_parameterValue.Clear();
}
m_hasRecordSet = true;
*****Error appears on this line inside the reader object*****
m_dbFactoryDatabaseDataReader = m_dbFactoryDatabaseCommand.ExecuteReader();
dbFactoryResponse.ExceptionMessage = "";
dbFactoryResponse.Success = true;
dbFactoryResponse.UserFriendlyMessage = "OK";
return dbFactoryResponse;
}
catch (Exception ex)
{
if (m_queueErrors)
{
m_queuedErrorsList.AppendLine (ex.Message);
m_queuedErrorsList.AppendLine ("\r\n\r\nPrepared Statement: " + sqlQuery);
m_queuedErrorsList.AppendLine();
m_queuedErrorCount++;
dbFactoryResponse.ExceptionMessage = m_queuedErrorsList.ToString();
dbFactoryResponse.Success = false;
dbFactoryResponse.UserFriendlyMessage = "Execute failed on the database.";
}
else
{
dbFactoryResponse.ExceptionMessage = ex.Message;
dbFactoryResponse.Success = false;
dbFactoryResponse.UserFriendlyMessage = "Execute failed on the database.";
}
try
{
Close();
}
catch (Exception f)
{
dbFactoryResponse.ExceptionMessage = f.Message + "\r\n\r\nPrepared Statement: " + sqlQuery;
dbFactoryResponse.Success = false;
dbFactoryResponse.UserFriendlyMessage = "Failed to close the connection to the database.";
}
return dbFactoryResponse;
}
}
The query is as follows (with values substituted in):
select CONTRIBUTO, count(*) as CountCol
from HA_WITHOUT_SCOTLAND
where
GEOMETRY.STIntersects(geometry::STGeomFromText('POLYGON((-25.43623984375 44.257784519021, 21.62918984375 44.257784519021, 21.62918984375 60.752403080295, -25.43623984375 60.752403080295, -25.43623984375 44.257784519021))', 4326)) = 1
group by CONTRIBUTO
CONRIBUTO column is a varchar(max)
GEOMETRY column is a geometry data type
When I run the statement directly on the database it runs without error and returns what I expect.
When I run it via the C# however using a prepared statement (obtained from a table earlier in the C# code) the error is produced. The parameters that are passed in are as follows:
#5 = -25.43623984375 double
#6 = 44.257784519021 double
#7 = 21.62918984375 double
#8 = 60.752403080295 double
The query without substituted values is as follows:
select CONTRIBUTO, count(*)
from HA_WITHOUT_SCOTLAND
where STIntersects(GEOMETRY, geometry::STGeomFromText('POLYGON(('+#5+' '+#6+', '+#7+' '+#6+', '+#7+' '+#8+', '+#5+' '+#8+', '+#5+' '+#6+'))', 4326)) = 1
group by CONTRIBUTO
I cannot see anywhere in here that would produce this error given the fact that the columns and parameters are the correct data types.
Can anyone shed any light on this?
The problem is down to the section that reads:
'POLYGON(('+#5+'...
This will attempt to convert the text POLYGON((' into a float so that it matches the #S parameter (so they can be added).
Instead, you could try wrapping each parameter in a CONVERT, for example:
'POLYGON(('+CONVERT(VARCHAR(100),#5)+'...
The everything will be a string for the + operator, which will concatenate them instead.
In your substituted version, you've already handled the conversion to a varchar yourself, so there's no problem.

Categories

Resources