Short Circuit in SMO-created stored procedure - c#

Due to a huge and hateful database that I'm not allowed to rationalise, I am forced to create a C# Datalayer generator for my app. I'm pretty much done, using T4 to generate Model classes and Insight.Database repos but I needed to create my User Defined Table Types and Stored Procedures via Sql Management Objects.
What I'm interested in is, can I use a short-circuit parameter when creating via SMO? What I want to replicate is something like this:
CREATE PROCEDURE [dbo].[cip_GetLicenses]
#fldIndex int = null
AS
SELECT [fldIndex]
,[fldLicenceData]
FROM [dbo].[tblLicences]
WHERE (#fldIndex is NULL OR (fldIndex = #fldIndex))
I can construct the body of the sproc relatively easily with a string builder and some column iteration, but creating a parameter is done separately.
The StoredProcedureParameter Type does actually have a DefaultValue property but it's a string, and sadly setting it to " = null" simply throws exceptions at run time.
Can anyone advise?

Related

WF 4.5: Is it possible to add variables dynamically at runtime?

We are writing a custom activity. In this activity it is possible to set a database connection string and a name for a stored procedure. At runtime the stored procedure is executing. Now we have some stored procedures which has input parameters.
Is it possible to generate variables dynamically in WF 4.5 for each input parameter in the stored procedure? Reading the parameters from the stored procedure is not the problem, but I dont have any idea how to generate the variables.
Example:
The user enters a name for the stored procedure to be executed (2 input params #Variable1 and #Variable2). Now in the variables tab should be 2 variables: #Variable1 and #Variable2. If the user changes the name in the stored procedure then in the variables tab should be the new params (for example only #Variable2)...
We spent a lot of time on this issue. But the only thing we have learned is that the activity has to be a NativeActivity and the variables should be added in the CacheMetadata method. But if I add a variable with AddVariable() method nothing happens :(
If you are open to including third-party libraries, you could try using an ORM tool like Dapper to accomplish this. A Dapper query generally takes an anonymous type to supply its parameters. Typical code for creating a custom object from fields in a database would look something like this:
IDbConnection db = New IDbConnection(...);
int id = 527; // normally passed in - using a hard coded value would defeat the purpose...
string myQuery = "SELECT Engine, Transmission, Make, Model, BodyStyle FROM Table WHERE ID = #ID";
Car result = db.Query<Car>(myQuery, new { ID = id }).First();
So I believe that you could use reflection to pass in the type ("Car") using reflection and create an anonymous object or pass in an actual object with an "ID" property at runtime. It will automatically create a custom Car object with the resulting data, assuming that the Car object has properties of Engine, Transmission, Make, Model, BodyStyle, etc.
Note that if you don't supply the type you expect to get back, you get an ExpandoObject: Creating an anonymous type dynamically? - you may also be able to pass one of these in for your input parameters, which would mean that you could create it at runtime.
This guy came up with a generic method for Dapper that may help you: http://www.bradoncode.com/blog/2012/12/creating-data-repository-using-dapper.html
What he came up with is something like this:
IEnumerable<T> items = null;
// extract the dynamic sql query and parameters from predicate
QueryResult result = DynamicQuery.GetDynamicQuery(_tableName, predicate);
using (IDbConnection cn = Connection)
{
cn.Open();
items = cn.Query<T>(result.Sql, (object)result.Param);
}
return items;

Function import that returns object instead of complex type

I'm building dynamic table with stored procedure, so when adding function import, I want it to return object instead of specific type. Is it possible? Thanks
UDPATE
I've tested this and it doesn't work - the call to SqlQuery returns a System.Object. I'll not mark for deletion as it is useful to know that this technique doesn't work.
it should be possible but as #BorisB has said, I'm not sure why you would want to ...
List<dynamic> result = context
.Database
.SqlQuery<dynamic>("select * from ......")
.ToList();
If it were possible what would be the type of that returned object and how would you access its properties?
You'd somehow had to build an EF complex type and a function import at runtime or build a model using ModelBuilder (but you still don't have a data class), which may be possible in theory but it in practice it is way above the problem you are solving.
Maybe it would be better to use ADO.Net directly or modify those SPs to return results as XML which is then consumed by the client (but in XML case you still don't have compiled type of returned objects, so no deserialization, only XPath).

Specifying SqlString length for a CLR stored procedure

I'm writing stored procedures in C# at the moment and I've run into a problem with the size of a parameter.
So I've created a project in VS 2008 and created several stored procedures which all look a bit like this:
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void SaveProgress(... SqlString logpart, ...)
{
...stuff...
}
}
Now because I've not specified anything else, when I deploy this to a database, the CREATE DATABASE statement (apparently) gets created with a nvarchar(4000) as the definition for the input parameter.
However, I regularly have to flush log parts larger than 4000 chars, so I'd like that to be nvarchar(MAX).
Now I think I can do some jiggery-pokery and use Management Studio to re-define the CREATE DATABASE statment, but I'd actually like to define the fact that I want it to be MAX in the project/solution, so the deployment gets done correctly and I don't have to start adding large wads of comments and/or documentation for anyone who needs to maintain this code after me.
Is there any way to specify this in the code or maybe in the AssemblyInfo or something like that?
Revisiting this years later, I tried to use SqlChars in a function that read data from the database and returned a formatted string with data in it. Using SqlChars actually made the function bomb, stating that it could not find linked server System - an error message that seems to have nothing to do with the problem, as I was never referencing a linked server in the first place.
Changing the return type and parameters back to SqlString, adding [return:SqlFacet(MaxSize = -1)] attribute to the function, and adding [SqlFacet(MaxSize = -1)] to each parameter made my function work properly.
Try using SqlChars. SqlChars automatically maps to NVARCHAR(MAX)

Type safe binding to an Oracle Stored Procedure in C#?

We are deploying multiple projects in C# with Oracle databases behind. I would like to implement all of the database logic in Oracle stored procedures as this
keeps all of the database logic in the database
makes it easier to maintain when database structures change
allows re-use of the stored procedures more easily across programming languages
I have test code running where I return rows using a SYS_REFCURSOR and I manually do the data bind on the results as SYS_REFCURSOR could be returning anything - i.e. its not type safe
My question is - is there any way I can define correct types in the stored procedure return type and correctly bind to that type safely in my C# code?
e.g. my PL/SQL procedure looks like this - the return part is not type safe - it could be anything. If I wanted to re-use it from another Oracle package then it will not have the correct type checking
PROCEDURE get_risk (p_process_id IN NUMBER, p_risk OUT sys_refcursor);
and my C# code looks something like the following. I have cludged this together from several classes so hopefully it makes sense. When I extract the data from the DB call I am manually defining the data types - I need to know in the C# code what the Oracle data types are
// setup procedure call
_oracleCommand = new OracleCommand("risk_pkg.get_risk", _conn.OracleConnection);
_oracleCommand.Parameters.Add(new OracleParameter("p_process_id", OracleDbType.Int64, processId, ParameterDirection.Input));
_oracleCommand.Parameters.Add(new OracleParameter("p_risk", OracleDbType.RefCursor, null, ParameterDirection.Output));
_oracleDataAdapter = new OracleDataAdapter(_oracleCommand);
_dataSet = new DataSet();
// call Oracle
_oracleDataAdapter.Fill(_dataSet);
// extract data - hand coded binding
Int64 dbRiskId = (Int64)_dataSet.Tables[0].Rows[0][_dataSet.Tables[0].Columns["risk_id"]];
Int64 dbClientId = (Int64)_dataSet.Tables[0].Rows[0][_dataSet.Tables[0].Columns["client_id"]];
return new Risk(dbRiskId, dbClientId);
This isn't necessarily a problem - I just want to know if there is a better way of doing this to make my PL/SQL more obvious in what it is returning, and making my C# code not have to know the Oracle data types - encapsulating me from database structure changes
Accepted solution : this seems to be the practical solution. I'm still slightly unsatisfied that my Oracle procedure isn't defining its return type explicitly, but that life
(You should post a sample of your to test code. Because I'm not sure, if I understand your question correctly.)
The returned type is Object to serve any possible return value. You have to convert it manually. But you could generate the code for the conversions. Define a table or a file with this meta information: which stored procedure returns which types and in which .Net types they shall be converted. Use this meta information to create the C# code.
We fill our RefCursors into a DataTable. The code to assign the table fields to their appropriate member variables is generated out of our meta tables.
I have used T4 text templating to do this with SQL server. Works incredibly well.

how to unit test following Method, returning value using "ref"

I have similar methods in the business layer. I am new to unit testing and sometimes get confused. For an idea, can you suggest, what will be a better approach to test this method
behaviour? I am using C# NUnit and Moq
public int? AddNewCatRetID(string categoryName)
{
int? categoryID = 0;
Adapter.AddNewBlogCategoryReturnID(categoryName, ref categoryID);
if (categoryID.HasValue)
return categoryID;
return 0;
}
where
Adapter = Visual Studio 2008, Data Set Designer generated TableAdater
AddDeveloperCategoryReturnID() = Name of a function which utilises a Stored procedure in DB
It adds a new record, "Category" and returns its auto generated ID. If it is non zero, we take that result for further processing.
I know should not be interested in talking to Database, below is the procedure, just to give an idea about what is going on in DB
PROCEDURE [dbo].[AddDeveloperCategoryReturnID]
#NAME NVARCHAR(MAX),
#CATEGORY_ID INT OUTPUT
AS
BEGIN
INSERT INTO [AllTimeGreatProgrammersDateBase].dbo.CATEGORIES(NAME )
VALUES (#NAME );
SET #CATEGORY_ID = SCOPE_IDENTITY();
SELECT #CATEGORY_ID;
END
some issues
how to check the values returned using "ref" from the method
what will you prefer to test and not to test? will be great if can list
There are several options depending on the characteristics of the Adapter type. If AddDeveloperCategoryReturnID is virtual or an interface member, you can most likely use a Test Double (either a hand-rolled one or a dynamic mock) to replace its behavior with some test-specific behavior.
If is a non-virtual method, you have two options:
Refactor the method to make it more testable.
Write an automated test that involves a database round-trip.
Automated tests that involve the database are orders of magnitudes more difficult to write and maintain than pure unit tests, so I would tend to shoot for the refactoring option.
On the other hand, if you think that the stored procedure represents a valuable code asset that should be protected by an automated test, you have no recourse but to write the database test.
I'd first convert Adapter.AddNewBlogCategoryReturnID(categoryName, ref categoryID) so that instead of returning a variable by reference, it simply returned the value.
Then, I would extract that into a virtual method.
To test AddNewCatRetID, I would extend the class to make a testable version, and override that virtual method to return an int? stored in a public variable.
That way, when you test to see what happens when you call AddNewCatRetID in a situation where there's a 0 in the database, you don't need to actually put a 0 in the database - you just set that parameter on the testable version of your class, and when your test calls AddNewCatRetID, instead if hitting the database, it just returns the value you set. Your test is guaranteed to be faster if you can avoid hitting the database, and since it's MS's generated adapter, there's not really a need to test it - you only care about what your method does with what the adapter returns.

Categories