there can be variable number of params being sent through a query string. in my form the query sends a few params that may not always be filled i.e. they will be sent as id="". this means that the function being used in my TableAdapter will bring the wrong result if one param is not coming in. i.e. it recieved 7 params whereas it was expecting 8 (or the 8th will be NULL).
The only workaround i can think is to make overloaded functions. but that means i will have to make 64 overloaded functions (for 8 params). Too much work, which makes me think that there maybe some other way i could get the job done without making 64 functions.
Is there any?
Working on ASP.NET with MSSQL
There is one work around we are using and It works perfectly for us.
You don't need to make any overload functions. You can pass all the parameter's values and If there is an empty value you can simply pass '%' and you don't need to do anything with the query either. Here is an example of an SQL Query:
Select * from Student where ID = 5 AND RollNo = '%' AND CourseID = '%'
If you check the above query in SQL server it will give you correct result.
Note: I have not tested this with TableAdapter, But I am sure it will work.
64 overloaded functions clearly isn't a workable solution. It's probably time to look at your WorkTable adapter code as it sounds like it isn't handling exceptional values very gracefully.
I would go with ashelvey's suggestion of one method with 8 parameters and make sure the handling code is robust enough to deal with null values or empty strings.
Introduce an object that is specifically for the parameters.
public void DoWork(params OperationArgs args[]) { /* code */ }
class OperationArgs {
public int? Id { get; set; }
public string Keywords { get; set; }
// etc
}
Related
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.
Under ASP.NET MVC are you supposed to pick up QueryString params the same way you do in ASP.NET WebForms? or does the [AcceptVerbs(HttpVerbs.Get)] declaration get used somehow?
Query string parameters can be accepted simply by using an argument on the action - i.e.
public ActionResult Foo(string someValue, int someOtherValue) {...}
which will accept a query like .../someroute?someValue=abc&someOtherValue=123
Other than that, you can look at the request directly for more control.
I think what you are looking for is
Request.QueryString["QueryStringName"]
and you can access it on views by adding #
now look at my example,,, I generated a Url with QueryString
var listURL = '#Url.RouteUrl(new { controller = "Sector", action = "List" , name = Request.QueryString["name"]})';
the listURL value is /Sector/List?name=value'
and when queryString is empty
listURL value is /Sector/List
You can always use Request.QueryString collection like Web forms, but you can also make MVC handle them and pass them as parameters. This is the suggested way as it's easier and it will validate input data type automatically.
I recommend using the ValueProvider property of the controller, much in the way that UpdateModel/TryUpdateModel do to extract the route, query, and form parameters required. This will keep your method signatures from potentially growing very large and being subject to frequent change. It also makes it a little easier to test since you can supply a ValueProvider to the controller during unit tests.
Actually you can capture Query strings in MVC in two ways.....
public ActionResult CrazyMVC(string knownQuerystring)
{
// This is the known query string captured by the Controller Action Method parameter above
string myKnownQuerystring = knownQuerystring;
// This is what I call the mysterious "unknown" query string
// It is not known because the Controller isn't capturing it
string myUnknownQuerystring = Request.QueryString["unknownQuerystring"];
return Content(myKnownQuerystring + " - " + myUnknownQuerystring);
}
This would capture both query strings...for example:
/CrazyMVC?knownQuerystring=123&unknownQuerystring=456
Output: 123 - 456
Don't ask me why they designed it that way. Would make more sense if they threw out the whole Controller action system for individual query strings and just returned a captured dynamic list of all strings/encoded file objects for the URL by url-form-encoding so you can easily access them all in one call. Maybe someone here can demonstrate that if its possible?
Makes no sense to me how Controllers capture query strings, but it does mean you have more flexibility to capture query strings than they teach you out of the box. So pick your poison....both work fine.
This is the correct way in .NET 6 (and other netcore)
var Param = Request.Query["IndexString"];
If need to be string
string Param = Request.Query["IndexString"].ToString();
#Context.Request.Query["yourId"]
I've created a SQL query that I execute with the following command, and it returns the correct number of entries but these contains all 0:
If I run the same SQL command in my Management Studio, it works correctly.
I also tried it with a Linq statement and it works also correctly:
I hope you guys can help me to solve the problem.
You shouldn't be projecting into a List<T> - the ToList() does that. Basically, simplify:
var data = dbContext.Database.SqlQuery<Tuple<DateTime, string, string>>(...).ToList();
It might also work with value-tuples:
var data = dbContext.Database.SqlQuery<(DateTime, string, string)>(...).ToList();
which would also allow you to conceptually name them:
var data = dbContext.Database.SqlQuery<(DateTime Datum, string Text, string Bemerkung)>
(...).ToList();
Note: concatenating filter is almost certainly a SQL injection vulnerability; if looks like you should be using a SQL parameter there instead.
#Evk notes that EF might not support column-wise binding of tuples. If that is the case, then your best bet would be to create a POCO that matches the column definitions:
class Foo // rename me to something meaningful
{
// note: there may be custom attributes you can use
// to make these names less ugly, i.e.
// [Column("TEXT")] on a property called Text
public DateTime RMA_DATUM {get;set;}
public string TEXT {get;set;}
public string BEMERKUNG {get;set;}
}
and use SqlQuery<Foo>.
I have a scripting functoid with the following code:
public string MyConcat(string product)
{
string retStr= "01";
product = product.ToUpper();
if(product.Contains("CONDITION")){
retStr= "02";
}
return retStr;
}
This works perfect when I run it in LinqPad, but when I test the map it returns the product string instead of the retStr, which I find really weird. Any help is much appreciated.
You probably have another Scripting functoid that has the same signature, i.e. is called MyConcat, returns a string, has a single string input. In that case it will execute the first version created with the input linked to it.
Please make sure you give your function names a unique and descriptive name to avoid this.
If you do need to use the same function multiple times in your map, this feature of it re-using the the function is quite useful, but I usually make sure to add a comment to all the subsequent copies stating that only the first version has the code.
I have a large number of PL/SQL stored procs that return columns with single character strings representing some kind of status value from a fixed range. In the project I'm working on, these columns have been mapped by Dapper to string properties on the domain objects, which are awkward and unreliable to manage, so I'd like to switch to enums.
If I used enums with single character names like enum Foo {A, P} I'm pretty sure Dapper would map them correctly but I don't want that, I want enums with descriptive labels like so:
enum Foo {
[StringValue("A")]
Active,
[StringValue("P")]
Proposed
}
In the above example, StringValueAttribute is a custom attribute and I can use reflection to convert the "A" to Foo.Active, which works fine - except I need Dapper to perform that conversion logic for me. I wrote a custom type handler to do this:
public class EnumTypeHandler<T> : SqlMapper.TypeHandler<T>
{
public override T Parse(object value)
{
if (value == null || value is DBNull) { return default(T); }
return EnumHelper.FromStringValue<T>(value.ToString());
}
public override void SetValue(IDbDataParameter parameter, T value)
{
parameter.DbType = DbType.String;
parameter.Value = EnumHelper.GetStringValue(value as Enum);
}
}
//Usage:
SqlMapper.AddTypeHandler(typeof(Foo),
(SqlMapper.ITypeHandler)Activator.CreateInstance(typeof(EnumTypeHandler<>).MakeGenericType(typeof(Foo)));
The registration with SqlMapper.AddTypeHandler() seems to work fine, but when my DbConnection.Query() code runs, I get an error saying that the value 'A' could not be converted - the error is thrown from Enum.Parse, suggesting that Dapper isn't actually calling my type handler at all despite it being registered. Does anyone know a way around this?
Another user has reported this as an issue on Dapper's github site. Seems like it's a deliberate optimisation specifically around enums in Dapper, so I've changed my database model rather than trying to change the mapping code. I looked at trying to modify Dapper itself, but the source code of Dapper is optimised like nothing I've ever seen, emitting opcodes to perform conversions in the most performant way possible - no way I want to start trying to work out how to make changes there.