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

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;

Related

Short Circuit in SMO-created stored procedure

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?

interface on specific parameters C#

I have a dynamic method(dynamicQuery) that receives an Enum (which stored procedure) and a Dictionary<string,string>, which will populate SQLParameters based on the content of the dictionary.
Let's say I have 3 stored procedures in SQL server that takes different parameters
named sp_insert_product, sp_insert_person, sp_insert_house.
How will I force the dictionary to accept the specific strings to pass to the stored procedure based on what the stored procedure accepts?
Another view of comparison. An overloaded dictionary, as you would do with methods.
EDIT
POSSIBLE SOLUTION
I can probably create a check inside the method, dynamicQuery, to check that if the content of the dictionary, does align with the parameters of the specified stored procedure.
But I want to do the error checking before compile-time, as opposed to run-time checking which would decrease performance
I resorted to ADO.NET
Thank you
If I understand your question correctly, you can use an enum as key for the dictionary.
enum Procedures
{
insert_prod,
insert_person
};
var dic = new Dictionary<Procedures, string[]>();
If I understand what you're trying to do - then that's very hard to do if there is no rigid contract between the sites. But as long as the code that deals with both is close together, it usually isn't an issue, for example:
string DoSomething(string username, string value) {
var dict = new Dictionary<string,string> {
{ nameof(username), username },
{ nameof(value), value },
};
SomeHelper.ExecuteSP("sp_somesp", dict);
}
Note that the same thing can also be done via anonymous types and reflection, for example:
string DoSomething(string username, string value) {
SomeHelper.ExecuteSP("sp_somesp", new { username, value });
}
which is pretty-much the approach that "Dapper" takes (except Dapper has a lot of optimization built in to make the reflection not hurt).

Generic wrapper for stored procedure execution

How can I execute stored procedures generically? I tried:
_context.Set<TEntity>();
I want to avoid embedded SQL such as:
context.Database.SqlQuery<TEntity>(storedProcedureName);
However, because the stored procedure is accessed via a Function rather than a Type this does not work. I also tried creating/accessing via a function mapping. The complex type for the return result exists, but this can't be used to obtain the result itself.
Do I need Delegates or is there support for this somewhere?
To be clear, I hope to create something like....
public IEnumerable<TEntityResult> ReadSpAll<TEntity,TEntityResult>(IEnumerable<SqlParameter> sqlParameters)
where TEntity is the stored proc and TEntityResult is the complex return type
See "Using Import Functions to Map Stored Procedures"
# http://msdn.microsoft.com/en-us/data/gg699321.aspx
for code like
var context = new AWEntities();
ObjectResult<OrderDetail> orderDetailEnumerable = context.GetDetailsForOrder(71796);
List<OrderDetail> details = orderDetailEnumerable.ToList();

Access a property of a DbSet by name

I have a DbSet<T>
I want to access one of the properties on it by name.
Is this possible?
I'm basically making a generic function, so that for any DbSet I have, I can specify a column and have it list the values of that column.
You can get the DbSet itself from the context by doing context.Set(T) but I am not sure about the fields.
I did something similar a while back using reflection.
T item = context.Set(T).First();
string propName = "MyProperty";
object value = item.GetType().GetProperty(propName).GetValue(item, null);
Of course note that you'll either need to cast the values to a specific type manually, or use ToString, which should work quite well on all basic types.
This assumes you already have the data from the server, and now need to process it.
EDIT:
If you want to create a query, then I found this!
Apparently, what you're looking for is available as part of Entity Framework these days.
An extension method is available which allows you to use .Select("propertyName") which returns IQueriable. Remember to add System.Linq.Dynamic to your using section.
You can then create select queries by specifying the name of the parameter.
List<object> data = (db.Set<SetType>()
.Where("propertyName == #0 && someOtherProperty == #1", propertyValue, someOtherPropertyValue)
.Select("propertyName") as IEnumerable<object>).ToList();
Check out this article on Dynamic LINQ.
Using the provided code, I was able to write a LINQ to Entities query like this:
var query = context.Set(Type.GetType("Person")).Select("Name");

Is this an appropriate use of generics and C#'s dynamic data type?

The problem I'm having is thus, we're building a data access layer using our existing ORM (it's an old one called Gentle) with the idea of moving to something like Fluent NHibernate. There are a few queries where we have to add custom clauses to the SqlBuilder in our existing setup, so for instance when retrieving some person objects we might be adding a clause like:
"PersonId in (SELECT PersonId from Orders where OrderValue > " + orderValue + " and OrderName = " + orderName
The point being that the parameters are being added directly in a string rather than as a parameterised query, it is possible in Gentle to add it as a parameterised query and this is what I've been working on. All our DALs inherit from a base GentleDAL, this is the class that actually constructs the Gentle query, adds the clauses and parameters etc. To add a parameterised clause in Gentle you have to do two things with your SqlBuilder object, you have to call sb.AddConstraint(string clause) to add your clause, and then for each parameter you have to call sb.AddParameter(string name, Type type), you can then construct your SqlStatement object from this, and only after that can you set the value for your parameter where you call stmt.SetParameter(string name, object value).
The way I have represented these parameters/clauses is I have created a class called GentleClauseCollection, this contains the clauses and parameters and has Add and Get methods for both of these things. Clauses are just strings and are stored internally in a List, the parameters are stored in a GentleParameter class which uses generics. The full code for GentleParameter is as follows.
public class GentleParameter<TParamType>
{
public string Name { get; private set; }
public TParamType Value { get; private set; }
public Type ParameterType {get { return typeof (TParamType); }}
public GentleParameter(string parameterName, TParamType parameterValue)
{
Name = parameterName;
Value = parameterValue;
}
}
There is no collection in .NET that I'm aware of that would let me store GentleParameter for different values of TParamType in the same collection, however it can be done using the DLR. In my GentleCollection class I store the parameters in a List and I get the parameters from this class as an IEnumerable. The Add method in my class is able to only allow GentleParameter's to be added so I know that my parameters will always have a Name, Value and ParameterType field which I can access.
My questions are: Given I could sacrifice the generics and change my parameter class Value property to be 'object' instead of T, have I overcomplicated things by using dynamic, what are the pros and cons of both approaches? Is there a third way to do this that I haven't thought of and how significant a performance impact am I likely to see by using dynamic given that all the method calls using the dynamic objects will be compiled at run time?
Thanks in advance for your help.
As sb.SetParameter is not generic and awaits an object, I would not make GentleParameter generic and hence I would not use the DLR.
Using dynamic doesn't seem over complicated to me. Method calls are resolved at runtime and it's cached the resulting invocation probably averages about 10x slower (we are talking nanoseconds). So it depends on how you are going to use it if it makes sense.
If you are always going to use it as type Object than yes you don't need to be using type dynamic not that it would have hurt anything.
If you want to be able to access properties than yes you should use dynamic, the result code will look cleaner than anything else you could do.
But even using dynamic you don't necessarily have to call the properties themselves dynamically, if you want to have as much static typing is possible you can have dynamic resolve a helper method that takes a generic form of your GentleParameter and do your work inside that.
...
private void HelperDoStuffWithGenericParam<T>(GentleParameter<T>param){
//Do stuff you have the static typing
}

Categories