I have a SQL Server Data Tools (SSDT) project that has a number of stored procedures for which I would like to generate the C# code to call them using T4. Are there any existing examples for doing this?
So far, I can create a function per proc, but I'd really like to be able to tap into the meta data SSDT creates so I can get the parameters, data types, and return values from it rather than doing string parsing.
COOL WITH A CAPITAL C! (but don't tell anyone who uses an ORM!)
To get the datatypes etc make sure you grab the latest DacExtensions from the MS DacFx team:
https://github.com/Microsoft/DACExtensions
The new api (which incidentally is written using T4 templates) makes finding the info you need many many times simpler.
There should be enough information you need in this blog to get you going:
https://the.agilesql.club/Blogs/Ed-Elliott/DacFx-Create-tSQLt-Tests-From-A-Dacpac
The only difference is that you are creating C# and not T-SQL so you won't have to deal with the ScriptDom.
When you do this, please dump it on github it sounds like a really useful project.
To answer this question in the comments:
I can create methods with the correct parameters, but I'm struggling
to find where the objects are in the model are that represent the
content of a stored procedure. I need to know the columns returned by
a SELECT statement in order to generate the return objects. Any ideas?
The referenced objects are provided by the TSqlProcedure.BodyDependencies relationship. That will return objects referenced in the stored proc body, but won't tell you how they are used. The relational model doesn't try to embed this info as it doesn't help in deployment, but you can get it by querying the SQLDOM AST for the procedure.
The AST is a syntax tree defining the actual structure of the Procedure statement, including the structure of the procedur body. What you need to do is:
Create a Visitor that visits SelectStatement nodes (or their children)
Find the column names used in the select
Map these names to names of objects returned by TSqlProcedure.BodyDependencies. Now you have a rich object that can state the table the column is contained in, the column's data type, etc.
Do whatever you need to based on this (for example define a return type with the correct properties matching the column data types?)
A few notes / resources:
Ed's DacpacExplorer will help you view and understand the code.
Dave Ballantyne just added SQLDOM support to DacpacExplorer. Not only will this help you see what statements you need to match in the visitor, you should also look at how they use loadAsScriptBackedModel (see this commit) to ensure you have the full AST for the procedure body. Without this you would just get the body as one SqlScript object which isn't much use to you.
Further examples of the visitor pattern are Dave's TSqlSmells and the DacExtensions project
Twitter is an easy way to contact Ed, Dave and me if you are blocked :-)
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 realize that handcrafting SQL statements is evil. I also realize that the spontaneous answer of everyone seeing this post will be "Use parameters". I have found several occurences of similar questions around here, but I can't find out how they apply to my specific problem.
I use System.Data.SQLite with C#. I want to dynamically create a VIEW, say listview_asdf which I then can access inside a library with simple SELECT * FROM listview_asdf statements. That library uses loads of automatically generated statements for accessing my view.
Problem is, at one point I need to create a view like this:
CREATE VIEW listview_asdf AS SELECT * FROM tbl_asdf WHERE id IN (1,2,3)
not knowing how many and which parameters I have in the IN() list. Actually, the list will be different each time I use it (I delete the views in their Dispose() methods). Creating the view does - as far as I understand the relevant google result - not allow to use parameters.
So, I need to take an arbitrary-length array of parameters (easy) and write the CREATE VIEW statement with an IN (...) clause. Here, I need to insert the parameter values. This is no problem in case of integers, as shown above, but for strings or other types (System.DateTime comes to mind) I need some kind of escaping.
Else, I need some way of getting around the necessity of escaping. I don't know of any solution to either except the ominous
sql = sql.Replace("'","''");
which, as User Aur Saraf points out here, is a sure way of losing my job (which I do like).
Any ideas for a way out?
Hello fellow developers.
First of all I apologize beforehand for the wall of text that follows, but after a day going crazy on this, I need to call for help.
I've stumbled across a problem I cannot seem to solve. I'll try to describe the scenario in the best possible way.
Task at hand: in an existing Asp.Net Mvc application, create a lookup table for an integer field, and use the textual value from the lookup in the editing view. When saving, we must first check if the lookup already has a corresponding text value for the same Root ID. If there is, use that. Otherwise, create it and then use it.
The structure:
The data model is a graph of objects where we have the root object, a collection of level A child objects, and every level A child object has a collection of level B child objects, so something like this:
Root (with fields)
Level A child (with fields) x n
Level B child (with fields) x n
The field we have to handle is on the LevelB objects.
There is a single Mvc view that handles the whole data. For collection objects, all fields are named like levelA1levelB1MyField, levelA1levelB2MyField, etc so every single field has unique name during the post. When the post happens, all values are read through a formCollection parameter which has average 120/130 keys. The keys are isolated by splitting them and looping on the numerical part of the names, values are read and parsed to the expected types and assigned to the object graph.
The datalayer part backing the object graph is all stored procedures, and all the mapping (both object to sproc and sproc to object) is hand written. There's a single stored procedure for the read part, which gets multiple datasets, and the method calling it reads the datasets and creates the object graph.
For the saving, there are multiple sprocs, mainly a "CreateRoot" and "UpdateRoot". When the code has to perform such tasks, the following happens:
For create scenario, "CreateRoot" is called, then the sprocs "CreateLevelA" and "CreateLevelB" are called in loop for each element in the graph;
For update scenario, "UpdateRoot" is called, which internally deletes all "LevelA" and "LevelB" items, then the code recreates them calling the aforementioned sprocs in loop.
Last useful piece of information is that the "business objects graph" is used directly as a viewmodel in the view, instead of being mapped to a plain "html friendly" viewmodel. This is maybe what is causing me the most trouble.
So now the textbox on the view handles an "integer" field. That field must now accept a string. The field on LevelB must remain an integer, only with a lookup table (with FK of course) and the text field from the lookup must be used.
The approaches I tried with no success:
My first thought was to change the datatype on the property MyField from integer to string on the object, then change the sprocs accordingly and handle the join at sproc level: I'd have a consistent object for my view, and the read/write sprocs could translate from string to integer and viceversa, but I can't do that because the join keys to retrieve the integer when writing are part of the Root item (as I stated in the first lines of this wall of text), which I don't know in the CreateLevelB sproc, and changing the whole chain of calls to pass those parameters would have a huge impact on the rest of the application, so no good.
My next try was to keep things "as they are" and call some "translation methods": when reading, pass the integer to the view, and there call the translation method to display the text value. When saving, use the posted text to retrieve the integer. The save part would work, I'd have all the parameters I need, but for the read part, I'd have to instantiate the "data access layer" and call its method at View level, and there's no need to explain why that is a very bad choice, so I ruled this out too.
Now I'm out of options (or ideas anyway). Any suggestion to solve this is very welcome, and also if something is not clear enough just point it out and I will edit my post with more accurate information.
Thanks.
This is not a real answer but you could rip out all sprocs and use the updating facilities of an OR mapper. This will resolve all the layering issues. You just update data how you see fit and submit at the end.
I guess this would also make the questions around "should I use an int or a string" go away.
Edit: After reading your comment I thought of the following: Do not implement alternative 1. You rather want to sacrifice code quality in the view than in the data storage model. The last one is more important and more centrally used.
I would not be too concerned with messing up the view by calling the DAL from it or the like. Changes in a view are localized and do not mess up the application's architecture. They just degrade the view.
Maybe you could create a view model in your controller and do the translations between DAL-model and view model? Or is that pattern not allowed?
I am writing a program that needs to read a set of records that describe the register map of a device I need to communicate with. Each record will have a handfull of fields that describe the properties of each register.
I don't really need to edit or modify the data in my VB or C# program, though I would like to be able to display the data on a grid. I would like to store the data in a CSV file, or perhaps an XML file. I need to enable users to edit the data off-line, preferably in excel.
I am considering using a DataTable or a Collection of "Register" objects (which I would define).
I prototyped a DataTable, and found I can read/write XML easily using the built in methods and I can easily bind to a DataGridView. I was not able to find a way to retreive info on a single register without using a query that returns a collection of rows, even though I defined a unique primaty key column. The syntax to get a value from a column is also complex, though I could be missing something on both counts.
I'm tempted to use a collection of "Register" objects that I can access via a unique key. It would be a little more coding up front, but seems like a cleaner solution overall. I should still be able to use LINQ to dataset to query subsets of registers when I need them, but would also be able to grab a single field using a the key value, something like this: Registers(keyValue).fieldName).
Which would be a cleaner approach to the problem?
Is there a way to read/write XML into a Collection without needing custom code?
Could this be accomplished using String for a key?
UPDATE: Sounds like the consensus is towards the Collection of register Objects. Makes sense to me. I was leaning that way, and since nobody pointed out any DataTable features that would simplify acessing a single row, it looks like the Collection is clearly the way to go. Thanks to those who weighed in.
I would be inclined not to use data sets. It would be better to work with objects and collections. Your code will be more maintainable/readable, composable, testable & reusable.
Given that you can do queries on the data set to return particular row, you might find that a LINQ query to turn the rows into objects may be all the custom code that you need.
Using a Dictionary<string, Register> for look ups is a good idea if you have a large number of items (say greater than 1000). Otherwise a simple LINQ query should be fine.
It depends on how you define 'clean'.
A generic collection is potentially MUCH more lightweight than a DataTable. But on the other hand that doesn't seem to be too much of an issue for you. And unless you go into heavy reflection you'll have to write some code to read/write xml.
If you use a key I'd also recommend (in the case of the collection) to use a Dictionary. That way you have a Collection of the raw data and still can identify each entry through the key in the Dictionary.
I usually use datatables if its something quick and unlikely to be used in any other way. If it's something I can see evolving into an object that has its own use within the app (like your Register Object you mentioned).
It might be a little extra code up front - but it saves converting from a datatable to the collection in the future if you come up with something you would like to do based on an individual row, or if you want/need to add some sort of extra functionality to that element down the road.
I would go with the collection of objects so you can swap out the data access later if you need to.
You can serialize classes with an xml serializer and defining a Serialize attribute or something like that (it has been a while since I done that, sorry for the vagueness). A DataSet or DataTable works great with XML.
Both DS and DT have ReadXml and WriteXml methods. XML must be predefined format, but it works seamlessly.
Otherwise, I personally like collections or dictionaries; DS/DT are OK, but I like custom objects, and LINQ adds in some power.
HTH.
i´m trying to query a DataTable object without specifying the fields, like this :
var linqdata = from ItemA in ItemData.AsEnumerable()
select ItemA
but the returning type is
System.Data.EnumerableRowCollection<System.Data.DataRow>
and I need the following returning type
System.Data.EnumerableRowCollection<<object,object>>
(like the standard anonymous type)
Any idea?
Thanks
If I understand you correctly, you'd like to get a collection of objects that you don't need to define in your code but that are usable in a strongly typed fashion. Sadly, no you can't.
An anonymous type seems like some kind of variant or dynamic object, but it is in fact a strongly typed class that is defined at compile time. .NET defines the type for you automatically behind the scenes. In order for .net to be able to do this, it has to have some clue from the code with which to infer the type definition. It has to have something like:
from ItemA in ItemData.AsEnumerable()
select ItemA.Item("Name"), ItemA.Item("Email")
so it knows what members to define. There's no way to get around it, the information has to logically be there for the anonymous type to be defined.
Depending on why exactly your are trying to do this, there are some options.
If you want intellisense while still encapsulating your data access, you can return xml instead of a datatable from your encapsulated data access class. (You can convert data tables to xml very easily. You'll want to use the new System.Xml.Linq classes like the XElement. They're great!) Then you can use VS2008's ability to create an xsd schema from xml. Then use/import that schema at the top of your code page, and you have intellisense.
If you have to have an object an with properties for your data, but don't want to define a class/structure for them, you'll love the new dynamic objects coming in C#4.0/VB10. You have object properties based on what the sql returns, but you won't have intellisense. There is also a performance cost to this, but (a) that might not matter for your situation and (b) it actually is not so bad in some situations.
If you're just trying to avoid making a lot of classes, consider defining structs/structures on the same code file, beneath your class definition. When you add more columns to your result set, it's easy to adjust a struct with more public fields.
In short you can have any two of the following three: (a) dynamic, (b) strontly-typed objects, (3) intellisense. But not all three.
There is one way to accomplish what you want, but it required knowledge of dynamic linq. You would build the query during run-time and then use it. I am no expert and have never really played around with it, but here is a link to Scott Guthrie's blog about it - Dynamic Linq. Hope that helps.
Wade