Filling sqlCommand Parameters with an object - Potential Issues - c#

I have written a method does the following.
What it does
Parses an SqlCommand object string for parameters in a parameterized SQL query.
Excludes any parameters declared and parameters starting with ##.
Finds properties in an object using reflection that share the parameter name
Uses the object's property value and adds a parameter to the
parameter collection
Reflection
This method is designed to be used in a web environment that is highly customizable and get parameters from a variety or deserialized JSON objects. I am very aware that reflection is slow but the idea is only to reflect a single object for a return set and not to be used in data processing loops, the reflection issue is not something I care about.
Questions
Aside from listing downsides of reflection, I would like to know what issues anyone sees with this code that I might not have considered. Please make the assumption that the object has the correct properties. I am having a hard time testing this because I wrote it to parse the SQL that I know. I cannot write tests for SQL I don't know. It seems to pass every test of SQL I have written.
Is there any issue with the code
Are there potential queries that would break this?
Can the code be written more efficiently ?
Are there any other issues that you might see with this idea ?
Code
public static void LoadParametersByObject(SqlCommand command, Object obj)
{
var DeclareREG = new Regex("(?<=Declare\\s*)#\\w{1,}");// finds all Declare #name
var ParameterREG = new Regex("(#{1,2}\\w{1,})");//finds all #name and all ##name
List<String> Exclude = (from Match x in DeclareREG.Matches(command.CommandText) select x.Value.Replace("#", "").ToUpper()).ToList();
List<String> Include = (from Match x in ParameterREG.Matches(command.CommandText)
where !x.Value.StartsWith("##") && !Exclude.Contains(x.Value.Replace("#", "").ToUpper())
select x.Value.Replace("#", "").ToUpper()).Distinct().ToList();
foreach (PropertyInfo prop in (from x in obj.GetType().GetProperties() where Include.Contains(x.Name.ToUpper()) select x).ToArray())
{
command.Parameters.AddWithValue("#" + prop.Name, prop.GetValue(obj));
}
}
Code Breakdown
Use regex to find Declare(whitespace)#parameter and put it in the
exclude list
Use regex to find any parameters starting with # or ##.
throw out the ## as they are internal SQL objects and check the #
parameters to make sure they are not in the exclude list as they
were declared in the text of the query and add the results to the
include list
Iterate the include list and search for the object property with the
same name
Add the property value to the SqlCommand's SqlParameter collection
Thanks in advance for your help

Related

How to generate nHibernate raw sql for Insert/Update without execution?

Is it possible to generate a raw SQL statement for insert/update operations using nHibernate without actually executing them? Assuming of course that everything (mappings, connectionStrings, etc.) is properly configured?
The closest thing I've found is to call:
Session.SessionFactory.GetClassMetadata(typeof(Client))
Which returns an object of type SingleTableEntityPersister containing SQLIdentityInsertString, that looks like this:
INSERT INTO Client (FirstName, LastName) values (?, ?)
But it would still require me to bind all of the properties manually, and on top of that SQLIdentityInsertString is a protected property. Are there any proper ways of doing that?
Okay, the closest thing I've found is to construct your own sql query with a string builder. First you need to extract your class metadata:
var metaData = Session.SessionFactory.GetClassMetadata(typeof(Client)) as SingleTableEntityPersister;
Then you can retrieve other information, such as:
var propertyNames = metaData.PropertyNames;
var tableName = metaData.TableName;
var firstPropertyValue = metaData.GetPropertyValue(client, propertyNames[0], EntityMode.Poco);
Once you have that information you can construct your own query manually. Not exactly the solution I wanted, but it's as close as it gets, I think.
However, one thing to note is that Session.CreateSQLQuery(string) method is currently bugged, and as a result SetParameter method doesn't work with more than 10 named parameters. There already seems to be a bug report created for this on NHbiernate's Jira.

C#/ODP.NET: large IN clause workaround

We have a C# component that handles attaching arbitrary-sized element lists into IN clauses for semi-arbitrary SQL SELECT queries. Essentially this boils down to receiving something like:
SELECT COUNT(*) FROM a WHERE b IN (...)
...where the "..." is the only portion of the query the component is allowed to modify.
Currently the component will insert a comma-separated set of named bind parameters, then attach the corresponding IDbDataParameter objects to the command and execute; the component is made aware of the types for the parameters it has to bind. This works well, until the calling code supplies a parameter set larger than the database is willing to accept. The objective here is to get such large sets working with queries against Oracle 11gR2 via ODP.NET.
This task is complicated somewhat by the following approaches being deemed unacceptable by those setting the requirements:
Global Temporary Tables
Stored procedures
Anything requiring CREATE TYPE to have been executed
The solution to this is not required to execute only one query.
I'm trying to make this work by binding the clause as an array, using code sourced from elsewhere:
IList<string> values;
//...
OracleParameter parameter = new OracleParameter();
parameter.ParameterName = "parm";
parameter.DbType = DbType.String;
parameter.Value = values.ToArray();
int[] sizes = new int[values.Count];
for (int index = 0; index < values.Count; index++)
{
sizes[index] = values[index].Length;
}
parameter.ArrayBindSize = sizes;
//...
The command subsequently executes without throwing an exception, but the value returned for COUNT is zero (compared to the expected value, from running the query in SQLDeveloper with a nested SELECT returning the same parameter set). Going through the ODP.NET docs hasn't brought any joy thus far.
The questions for this are:
Is there a way to make the above parameter attachment work as expected?
Is there another viable way to achieve this without using one of the vetoed approaches?
(I'm aware this is similar to this (unanswered) question, but that scenario does not mention having the same restrictions on approaches.)
Well, since you are not allowed to use Global Temporary Tables, are you at least allowed to create normal tables? If so, here is a way:
Create an OracleCommand object with the following command text:
#"BEGIN
CREATE TABLE {inListTableName}
(
inValue {dbDataType}
)
INSERT INTO {inListTableName}(inValue) VALUES(:inValue);
END"
Set the ArrayBindCount on the command object to the number of items you need in your in list.
Replace {inListTableName} with the Guid.NewGuid().ToString().
Replace the {dbDataType} with the correct oracle data type for the list of values that you want to use in your in clause.
Add an OracleParameter to the OracleCommand named "inValue" and set the value of the parameter to an array containing the values that you want in your in clause. If you have a Hashset (which I recommend using to avoid sending unnecessary duplicates), use the .ToArray() on it to get an array.
Execute this command. This is your prep command.
Then use the following sql snippet as the value portion of the in clause in your select sql statement:
(SELECT {inListTableName}.inValue FROM {inListTableName})
For example:
SELECT FirstName, LastName FROM Users WHERE UserId IN (SELECT {inListTableName}.inValue FROM {inListTableName});
Execute this command to get a reader.
Lastly, one more command with the following command text:
DROP TABLE {inListTableName};
This is your cleanup command. Execute this command.
You might want to create an alternate schema/user to create the inListTable so that you can grant appropriate permissions to your user to only create tables in that schema.
All of this can be encapsulated in a reusable class with the following interface:
public interface IInListOperation
{
void TransmitValueList(OracleConnection connection);
string GetInListSQLSnippet();
void RemoveValueList();
}
TransmitValueList would create your prep command, add the parameter and execute the prep command.
GetInListSQLSnippet would simply return (SELECT {inListTableName}.inValue FROM {inListTableName});
RemoveValueList cleans up.
The constructor for this class would take the value list and oracle db data type, and generate the inListTableName.
If you can use a Global Temporary Table, I would recommend that over creating and dropping tables.
Edit:
I'd like to add that this approach works well if you have clauses involving NOT IN lists or other inequality operators. Take the following for example:
SELECT FirstName, LastName FROM Users WHERE Status == 'ACTIVE' OR UserID NOT IN (1,2,3,4,5,6,7,8,9,10);
If you use the approach of splitting the NOT IN part up, you will end up getting invalid results. The following example of dividing the previous example will return all users instead of all but those with UserIds 1-10.
SELECT FirstName, LastName FROM Users WHERE UserID NOT IN (1,2,3,4,5)
UNION
SELECT FirstName, LastName FROM Users WHERE UserID NOT IN (6,7,8,9,10);
Maybe this is too simplistic for the kind of query you're doing, but is there any reason why you couldn't split this into several queries and combine the results together in code?
i.e. Let's imagine 5 elements are too many for the query...
select COUNT(*) from A where B in (1,2,3,4,5)
you'd separately perform
select COUNT(*) from A where B in (1,2,3)
select COUNT(*) from A where B in (4,5)
and then add those results together. Of course, you'd have to make sure that in-clause list is distinct so you don't double up on your counts.
If you can do it this way, there is an added opportunity for parallelism if you're allowed more than one connection.

Dynamic Linq "OrderBy" on runtime values

I've been looking at Dynamic Linq today (installed into VS via NuGet)...but all the examples I have found so far assume OrderBy is to be done on a known property or column name; however I am trying to OrderBy a field which is not strongly typed; but actually a key value of a row object which is derived from a Dictionary; e.g.
class RowValues : Dictionary<string, string>
{
...
}
So the list to be ordered is specifically a list of RowValues objects, filled with Name,Value pairs. For a given list of RowValues, the OrderBy field could by any of keys of the named value pairs entries (fyi: I want the orderby field to be specified in an xml config file ultimately so the ordering can be changed without re-deployment of binaries etc).
I've got a hunch the solution lies in writing a custom ordering function passed to the OrderBy??? This function would obviously know how to get a specific value from the RowValues object given a field name from the xml config....?? The answers I have seen so far show passing a string which contains a custom order by clause into the OrderBy, which is close to where I want to be, but how in my case would the runtime know where to find the fields referred to in the OrderBy string??
Input will be very much appreciated, or have I completely misunderstand the Dynamic Linq functions?
If you're using dynamic LINQ, it would just be:
var sortColumn = GetConfigValue(...);
var sorted = RowValues.OrderBy(sortColumn);
You could of course use a concatenated string to create a multiple sort ("column1, column2 DESC"). As far as I'm aware, there's no custom sort function unless you're using regular LINQ.
Also, I would make sure you know the performance characteristics of Dynamic LINQ.
Edit:
Is this what you're looking for? This will order it based on the value of the "Key" entry in the dictionary. If you need multiple sort by-s, you can use it in a loop with .ThenBy()
void Main()
{
List<RowValues> v = new List<RowValues>();
var key = "Key"; //GetFromConfig();
var v1 = new RowValues();
v1.Add("Key", "1");
v1.Add("3", "5");
var v2 = new RowValues();
v2.Add("Key", "3");
v2.Add("2", "2");
var v3 = new RowValues();
v3.Add("Key", "2");
v3.Add("2", "2");
v.Add(v1);
v.Add(v2);
v.Add(v3);
v.OrderBy(r => r[key]).Dump();
}
class RowValues : Dictionary<string, string>
{
}
Kyle, thanks again. Apologies for late reply, I have moved on from this issue now but out of interest and courtesy I wanted to come back and agree your code is much closer to where I wanted to get to, but we have lost the dynamic linq aspect. So, where you are calling the OrderBy and ordering on the key, I would want to pass a string containing the order command e.g "r[key] desc". The reason being I would want to leave the determination as to which direction to order until runtime. I suspect ths would be accomplished using an expression tree possibly? e.g: here

LINQ to Entities does not recognize the method 'Int32 IndexOf(System.String, System.StringComparison)' method

I have executed a linq query by using Entityframework like below
GroupMaster getGroup = null;
getGroup = DataContext.Groups.FirstOrDefault(item => keyword.IndexOf(item.Keywords,StringComparison.OrdinalIgnoreCase)>=0 && item.IsEnabled)
when executing this method I got exception like below
LINQ to Entities does not recognize the method 'Int32 IndexOf(System.String, System.StringComparison)' method, and this
method cannot be translated into a store expression.
Contains() method by default case sensitive so again I need to convert to lower.Is there any method for checking a string match other than the contains method and is there any method to solve the indexOf method issue?
The IndexOf method Of string class will not recognized by Entity Framework, Please replace this function with SQLfunction or Canonical functions
You can also take help from here or maybe here
You can use below code sample:
DataContext.Groups.FirstOrDefault(item =>
System.Data.Objects.SqlClient.SqlFunctions.CharIndex(item.Keywords, keyword).Value >=0 && item.IsEnabled)
You really only have four options here.
Change the collation of the database globally. This can be done in several ways, a simple google search should reveal them.
Change the collation of individual tables or columns.
Use a stored procedure and specify the COLATE statement on your query
perform a query and return a large set of results, then filter in memory using Linq to Objects.
number 4 is not a good option unless your result set is pretty small. #3 is good if you can't change the database (but you can't use Linq with it).
numbers 1 and 2 are choices you need to make about your data model as a whole, or if you only want to do it on specific fields.
Changing the Servers collation:
http://technet.microsoft.com/en-us/library/ms179254.aspx
Changing the Database Collation:
http://technet.microsoft.com/en-us/library/ms179254.aspx
Changing the Columns Collation:
http://technet.microsoft.com/en-us/library/ms190920(v=sql.105).aspx
Using the Collate statement in a stored proc:
http://technet.microsoft.com/en-us/library/ms184391.aspx
Instead you can use this method below for lowering the cases:
var lowerCaseItem = item.ToLower();
If your item is of type string. Then this might get you through that exception.
Erik Funkenbush' answer is perfectly valid when looking at it like a database problem. But I get the feeling that you need a better structure for keeping data regarding keywords if you want to traverse them efficiently.
Note that this answer isn't intended to be better, it is intended to fix the problem in your data model rather than making the environment adapt to the current (apparently flawed, since there is an issue) data model you have.
My main suggestion, regardless of time constraint (I realize this isn't the easiest fix) would be to add a separate table for the keywords (with a many-to-many relationship with its related classes).
[GROUPS] * ------- * [KEYWORD]
This should allow for you to search for the keyword, and only then retrieve the items that have that keyword related to it (based on ID rather than a compound string).
int? keywordID = DataContext.Keywords.Where(x => x.Name == keywordFilter).Select(x => x.Id).FirstOrDefault();
if(keywordID != null)
{
getGroup = DataContext.Groups.FirstOrDefault(group => group.Keywords.Any(kw => kw.Id == keywordID));
}
But I can understand completely if this type of fix is not possible anymore in the current project. I wanted to mention it though, in case anyone in the future stumbles on this question and still has the option for improving the data structure.

Dynamic Object - Tidy property names?

I'm using some code to execute a SQL and return a IEnumerable of dynamic objects. The code is here if you want to see it.
I have a table with a column name such as APPLICATION_NAME;
In my object I have to reference it like so:
var results = ReturnResults("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=rawdb;Data Source=mypc", "SELECT * FROM APPLICATIONS");
string name = results.First().APPLICATION_NAME;
Is there a way to make property names resolve to something tidier? ie.
string name = results.First().ApplicationName;
Thanks
There are some ToCamelCase() extensions out there (just google it). But if you would implement it into your dynamic object. How do you know which shiny name you have to take for an ugly column name?
Instead you should rewrite your select statement to return nice names, instead of implementing some algorithm into the dynamic object. How about this statement:
SELECT APPLICATION_NAME as 'ApplicationName' FROM APPLICATIONS
So it is clear to everyone who reads the sql statement how to access the columns through the dynamic object.

Categories