I have an object in my SQL Server database that is called dbo.stringlist
CREATE TYPE [dbo].[StringList] AS TABLE
(
[Item1] [varchar](2000) NULL
)
I have a stored procedure that has syntax in SSMS like
exec [dbo].[Getdata] #Fields
(which is the dbo.stringlist) which effectively just joins on a table in the stored procedure and acts as a long list of criteria. It's how I want to deal with the request on the database side, so I won't be changing that.
All I want to do is execute this in C# (hopefully) using Dapper, because the rest of the goal has been built out using nothing but Dapper. I can scrap it if need be. The goal is to effectively write an ETL of sorts, but instead of endpoint-to-endpoint SSIS-like packages, I am just automating deploy scripts for new builds.
If there is a way to do this outside of Dapper that is easier, then I am all for scrapping this at this point. Like I said, the documentation for this is sparse, at least the updated documentation for this specific task is sparse.
I tried this method - https://gist.github.com/taylorkj/9012616
And also tried this method - https://bornsql.ca/blog/table-valued-parameters-and-dapper-in-net-core/
The second one didn't work, because I just want to pass the list in, but that's not an option. It needs to be an IEnumerable, which I don't feel like I can pass values into like a list. But maybe I just don't know what I am doing when it comes to that interface.
So if anyone has an UP-TO-Date link to help me out that would be stellar. I have a feeling I am going to have to scrap everything I have done, which is fine, I guess. I just want the tool to work. I know this post doesn't have a lot of information, that's why I am just asking for ideas to accomplish what I am trying to do in C# and hopefully Dapper, but like I said, I am not married to it.
I got it to work with the second link.
private static IEnumerable<SqlDataRecord> CreateSqlDataRecord(IEnumerable<string> list)
{
var metaData = new SqlMetaData("Item1", SqlDbType.VarChar, 2000);
var record = new SqlDataRecord(metaData);
foreach (var item in list)
{
record.SetSqlString(0, item);
yield return record;
}
}
public static SqlMapper.ICustomQueryParameter GetTableValuedParameter()
{
string[] list = new string[] { };
return CreateSqlDataRecord(list).AsTableValuedParameter("dbo.StringList");
}
I wasn't updating the parameter nor was I including a varchar data length of 255.
Related
This is a contrived example however I have simplified it for ease of explanation.
Please see my update at the bottom before investing too much of your
time!
Background
I have some (a lot of) code that ordinarily queries my DB as follows:
SELECT name FROM sites where IsLive=1;
My challenge is to, under certain conditions, return the full list of sites, essentially
SELECT name from sites;
I do not wish to modify the actual C# code issuing the SQL (although I can do if I have to in order to achieve my goal which is purely for demonstration purposes).
Therefore in order to leave as much untouched as possible my thoughts are to insert a database-proxy-view called site that returns the data dependent on a control variable
Method
Rename existing site table to site_table
Create a new view named site that the C# code now unknowingly targets and which returns the (possibly filtered) details from site_table according to the control variable value (Note a limitation on variables in views meant I had to create a function in order to demonstrate this - see http://dev.mysql.com/doc/refman/5.7/en/create-view.html wrt error 1351)
Changes made
ALTER TABLE site RENAME TO site_table;
CREATE FUNCTION controlVariableFn() RETURNS VARCHAR(16) RETURN #controlVariable;
CREATE OR REPLACE VIEW site AS SELECT * from site_table WHERE (IsLive = 1 OR controlVariableFn() = 'SHOWALL');
The above statements are ugly but achieve the result I want, however my problem is to dynamically pass through controlVariable without changing my main SQL queries being sent.
My Question
Is there a way to (ideally as I am creating my connection object) define the controlVariable outside the actual SQL to be executed but which the View can still access similar to the above as though it had been supplied as a regular user variable parameter to the query?
so the code would look something like
var connectionString = "Server=localhost;User ID=un;Password=pw;Database=dbname;....";
DbConnection db = new MySql.Data.MySqlClient.MySqlConnection
(connectionString, "controlVariable=SHOWALL");
var results = db.Query<Site>("SELECT * FROM site;");
(I understand that this would not be a smart permanent solution)
Update
My preferred solution as outlined above will not work for me as once I get into my data access layer as the results set will
essentially be filtered again back to the original set. There are some circumstances where it
could work; it would depend on the SQL issued (e.g. when collapsing a
results set down instead of trying to expand a results set as I was
trying to do here).
In that regard I am no longer looking for an answer here but will leave it for posterity as a preferred option and as per the guidelines - thanks anyway.
If you do not want to edit the c# code then the variable will have to be stored in the database although i am not sure how you will not edit the code.
If you are willing to edit the code then you can access a secondary configuration table which will have the settings that you would like the user to pass to the view. take this and allow the user to select which they want and then pass it to the view through the application.
I'm new to using C# in Visual Studio 2010, so my knowledge is fairly limited.
I am looking to make a program that runs and exports multiple Stored Procedures in SQL Server 2008 - also with a nice interface.
The thing I am unsure about is how this is best done?
I was thinking that I want a form with maybe a treeview and a datagridview and then execute the stored procedures. This works fine with just one query - but my question is how this is done best with multiple querys? I don't want different datagridviews for each stored procedure (I have a lot). I want to be able to select a different stored procedure in my treeview and have the data in the datagridview to change without having to run the Stored Procedure every time. Some of them are pretty timeconsuming (> 1 min). So I quess what I am asking is, if I can somehow load all the data into my program at once?
Can I get a dataset to hold more than one table - or will I somehow have to create different datasets for all my stored procedures?
Rather than load all the data into the program at once, what you want is to cache the results of a query the first time you run it. This stops people having to wait for everything to load just so they can look at one data set.
Your scenario as you describe it at the moment is that when you click a particular item in the tree view, the relevant SQL is run and the collection of results is bound to your datagridview, wiping out any previous data.
The practice of caching inserts a get-if-I-don't-already-have-it layer and a method of storing. Something like this:
public class MyResultClass
{
public string Column1 { get; set; }
public string Column2 { get; set; }
}
private static readonly Dictionary<string, IEnumerable<MyResultClass>>
CachedResults = new Dictionary<string, IEnumerable<MyResultClass>>();
protected void OnTreeViewSelectionChanged(object sender, EventArgs e)
{
// I'm not overly familar with the treeview off the top of my head,
// so get selectedValue however you would normally.
var selectedValue = ????;
IEnumerable<MyResultClass> results;
// Try to find the already cached results and if false, load them
if (!CachedResults.TryGet(selectedValue, out results))
{
// GetResults should use your current methodology for getting the results
results = GetResults(selectedValue);
CachedResults.Add(selectedValue, results);
}
myGridView.DataSource = results;
}
Note: If this is a web application rather than a winforms one, you'll need to use a ConcurrentDictionary instead of a Dictionary to make this pattern thread safe.
OK, so I understand all about the importance of using SqlParameters, and this question may seem a little dumb, but I'm not entirely sure on the answer and want to make sure I make the right decision.
Consider this simple c# method:
public static void MakeLinks (int tableOneId, List<int> tableTwoIds, string storedProcedureName)
{
using (SqlConnection conn = new SqlConnection(<connection string>))
{
// Code Omitted
using (SqlCommand cmd = new SqlCommand(storedProcedureName, conn)
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
// SqlParameterCollection code omitted
cmd.ExecuteNonQuery();
}
}
}
Basically, I have a large number of methods that link rows in various combinations of tables. In each one, there is a single Id for 'TableOne' and a collection of Ids for 'TableTwo'. Each combination has a relevant StoredProcedure in the database and the .NET code is extremely repetitive. I'm therefore considering using the above approach, updating all StoredProcedure parameter names to be the same (e.g. #Id rather than "#tableOneId", "#tableTwoId" etc.) and then simply passing the name of the StoredProcedure to the method and creating the command string as such:
string commandString = String.Format("[DatabaseName].[dbo].{0}", storedProcedureName);
My question is therefore, Is this still safe, or is there a better way to handle / do this?
Your thoughts and advice are much appreciated.
Changing the names of your stored-procedure parameters makes it no more, or less safe. One might conceivably argue that someone who has gained access to your database might notice the pattern - but then the astute will realise that if an attacker has got that kind of access to your database then you're screwed anyway.
If you can make your code more re-usable by making the parameter names generic - then go for it, I say. It also semi-documents that these stored procedures perform the same kind of function and I wish more stored-procedures that I have had the misfortune of coming across had some kind of naming convention that could be relied upon!
Is it safe to use one method for running similar Stored Procedures,
passing the StoredProcedure name as a method parameter
Yes
Looks like you have multiple procedures with similar working only differing in their names. There shouldn't be any problem (like SQL Injection) by passing procedure name to C# method.
I'm just looking to write a generic function in C# .NET 4.0 where I can send it a database string, a query string, and get my results back.
Not being that well versed in C# and it's various different objects, I'm not really sure what the best options might be for returning this information.
I know there is DataTable, DataSet, and that's really about it. What I'm looking for is an object that is fairly efficient and something where I can easily access it's data members. I'm sure it wouldn't be a problem writing my own, but I know there has to be some other object in .NET 4.0 that I can access.
The problem with such a design is that it's very difficult to use best practices for security and preventing SQL Injection. Passing in a pure SQL statement to a function pretty much eliminates the ability to defend against SQL Injection unless you do it carefully.
As a general rule, if you're taking in any user input, you want to be sure that you're either using parameterized stored procedures, or parameterized queries. The recommended pattern would be to pass in a statement and an array of type object that contains the correct number of parameter values, and build the parameter collection in your function.
We use the Microsoft data Application Blocks from their patterns and Practices library, which contain functionality that really makes this a lot easier.
In using this, the ConnectionString is stored in the app.config or web.config, and you pass in the NAME of the connection string, The Data Application Blocks code takes care of looking it up, creating the command, connection, and performing the query.
Our code looks like this when using this pattern:
public static DataSet ExecuteDataset(string CallingApp, string ConnectionStringName, string StoredProcName, object[] ParameterValues)
{
DataSet ReturnValue = new DataSet();
Microsoft.Practices.EnterpriseLibrary.Data.Database db = Microsoft.Practices.EnterpriseLibrary.Data.DatabaseFactory.CreateDatabase(ConnectionStringName);
try
{
ReturnValue = db.ExecuteDataSet(StoredProcName, ParameterValues);
}
catch (Exception ex)
{
// a bunch of code for exception handling removed
}
return ReturnValue;
}
This is the code for getting a DataSet. There are plenty of other functions available, and this code might not work against the newest version of the Data Access Library. We're using a slightly older version, but this should give you an idea of how easy it is to use.
New and improved method is not to do that, but to havea method to get the data you want.
E.g define a CustomerOrder class. Have a database module with a function called say:
GetOpenOrdersForCustomer(int argCustomerID)
that returns you a collection of them.
Look up Entity Framework POCO etc.
DataSet and DataTable are earlier incarnations of this sort of thing, they are pretty hefty, with a lot of baggage.
If you want/need to do a bit more handcoding. Interfaces like IDataReader, IDbCommand IDbConnection, will isolate you from a number of implementation details in your code, for instance whether your backend is SQL Server, MySql etc.
Even then you are still better off not passing things like DataTable, SqlCommand about about in your application code.
Too many chances to to plaster the database schema all over the code leaving you with a huge and soon to be realised potential for technical debt.
Hide it all. In Code First POCO it's practically invisible.
Have you thought about using an ORM like NHibernate : http://nhforge.org/Default.aspx
It will do everything related to database and you will be working with nice object classes.
In other case, your function can return Datatable and you can write another function to get the DataTable and extract data into an object class. eg. This new function would be like,
public object PopulateData(DataTable table, Enum ClassType); //Or something similar
You can then use reflection to map DataTable columns and class object.
But I would recommend using an ORM.
It depends the scenario you want to cover.
For example, in an MVC application its fairly maintainable to deploy a data layer based on Entity Framework, because its easily testeable and the communication with the database is very straightforward through simple objects.
Another approach could be use an already developed component, such is Data Application Block from Enterprise library or perhaps develop your custom database factory... It just depends the purpose of your development.
Could you give more details of what kind of application we are talking about?. It's an existing database or a new one?.
Another question. What exactly you want to expect to pass over the query string?. If you mind pass sql sentences i will tell you that its a very bad and dangerous practice that you should avoid.
Best Regards.
You may want something like:
public static DbDataReader Query(
string connectionString, string selectCommand,
params KeyValuePair<string, object>[] parameters)
{
var connection = new OleDbConnection(connectionString);
var command = new OleDbCommand(selectCommand, connection);
foreach (var p in parameters)
command.Parameters.Add(new OleDbParameter(p.Key, p.Value));
var result = command.ExecuteReader();
return result;
}
OleDb used as example.
What would be the best approach to allow users to define a WHERE-like constraints on objects which are defined like this:
Collection<object[]> data
Collection<string> columnNames
where object[] is a single row.
I was thinking about dynamically creating a strong-typed wrapper and just using Dynamic LINQ but maybe there is a simpler solution?
DataSet's are not really an option since the collections are rather huge (40,000+ records) and I don't want to create DataTable and populate it every time I run a query.
What kind of queries do you need to run? If it's just equality, that's relatively easy:
public static IEnumerable<object[]> WhereEqual(
this IEnumerable<object[]> source,
Collection<string> columnNames,
string column,
object value)
{
int columnIndex = columnNames.IndexOf(column);
if (columnIndex == -1)
{
throw new ArgumentException();
}
return source.Where(row => Object.Equals(row[columnIndex], value);
}
If you need something more complicated, please give us an example of what you'd like to be able to write.
If I get your point : you'd like to support users writting the where clause externally - I mean users are real users and not developers so you seek solution for the uicontrol, code where condition bridge. I just though this because you mentioned dlinq.
So if I'm correct what you want to do is really :
give the user the ability to use column names
give the ability to describe a bool function (which will serve as where criteria)
compose the query dynamically and run
For this task let me propose : Rules from the System.Workflow.Activities.Rules namespace. For rules there're several designers available not to mention the ones shipped with Visual Studio (for the web that's another question, but there're several ones for that too).I'd start with Rules without workflow then examine examples from msdn. It's a very flexible and customizable engine.
One other thing: LINQ has connection to this problem as a function returning IQueryable can defer query execution, you can previously define a query and in another part of the code one can extend the returned queryable based on the user's condition (which then can be sticked with extension methods).
When just using object, LINQ isn't really going to help you very much... is it worth the pain? And Dynamic LINQ is certainly overkill. What is the expected way of using this? I can think of a few ways of adding basic Where operations.... but I'm not sure how helpful it would be.
How about embedding something like IronPython in your project? We use that to allow users to define their own expressions (filters and otherwise) inside a sandbox.
I'm thinking about something like this:
((col1 = "abc") or (col2 = "xyz")) and (col3 = "123")
Ultimately it would be nice to have support for LIKE operator with % wildcard.
Thank you all guys - I've finally found it. It's called NQuery and it's available from CodePlex. In its documentation there is even an example which contains a binding to my very structure - list of column names + list of object[]. Plus fully functional SQL query engine.
Just perfect.