Convert Dictionary to anonymous object in c#? - c#

I have a web API I'm building in C#. This web API is used as a front end to query a SQL database. I am using SqlKata as the engine to perform the querying. When generating a WHERE clause, the SqlKata documentation states the following:
Multiple fields
If you want to filter your query against multiple fields, pass an object that represents col/values.
var query = new Query("Posts").Where(new {
Year = 2017 ,
CategoryId = 198 ,
IsPublished = true,
});
I want to be able to avoid hardcoding the WHERE clause, but rather base it on passed in querystrings. My idea was to add each querystring name and value to a Dictionary then somehow use the values of that Dictionary within the SqlKata .Where() clause. I thought maybe I could convert the Dictionary to the required anonymous object, but I can't get it to work. Any ideas folks on how to do this? So my url might be:
https://webapi.com.au/api?store=120&name=james
Dictionary:
store=120
name=james
Query:
var query = new query("Posts").Where(anonObject)
( anonObject would be {store=120,name=james} )

You don't need an anonymous object. Looking at the SqlKata docs, you can just build up the query by looping over the dictionary, something like this:
//Assuming the dictionary is something like Dictionary<string, object>
var query = new Query("Posts");
foreach(var element in dictionary)
{
query = query.Where(element.Key, element.Value);
}
var result = query.Get();
Note: Never even heard of SqlKata before so this is just from looking at the docs.

Related

Multiple where clause using C# and SqlKata query engine

I am working on a small Web API which will use SqlKata in the back end as the query engine to talk to SQL Server. In the documentation, SqlKata says the following in relation to multiple where clauses:
Multiple fields
If you want to filter your query against multiple fields, pass an object that represents col/values.
var query = new Query("Posts").Where(new {
Year = 2017 ,
CategoryId = 198 ,
IsPublished = true,
});
My intention is to use query strings to create the WHERE clauses in the back end, but I'm a bit stuck as to how to convert the Key/Value pairs of the query strings into properties of an object to use in the SqlKata query. The request will be highly dynamic in nature, so I can't really use a static object. I am wondering if anyone has any tips on how to construct an object to satisfy these requirements, being that the properties - both the property name and/or value - can be dynamic, as could the number of properties within the object. In my head I could imagine somehow converting the key / value pairs of the query strings into an object at run-time, but I'm stuck on how to achieve that.
I did try the following, but it seems each iteration through the loop the last key/value pair is replaced, so you end up with only the most recent WHERE clause being considered:
if (request.QueryStringParameters != null)
{
foreach (var element in request.QueryStringParameters)
{
CreateCloudWatchLog($"query string {element.Key} value {element.Value}", context, LogLevel.Trace, environmentLogLevel);
if (element.Key != "limit")
{
query = query.Where(element.Key, element.Value);
}
if (element.Key == "limit")
{
query = query.Limit(Convert.ToInt32(element.Value));
}
}
}
I have also tried this approach (query strings => json => dynamic object) but I get the error 'parameter count mismatch' (I am testing with one parameter passed in called 'storenumber' with a value of 399)
var json = JsonConvert.SerializeObject(request.QueryStringParameters, Formatting.Indented);
CreateCloudWatchLog($"Serialised query strings = {json}", context, LogLevel.Trace, environmentLogLevel);
var myobject = JsonConvert.DeserializeObject<dynamic>(json);
query = query.Where(myobject);
Debug logs:
[Trace] Serialised query strings =
{
"storenumber": "399"
}
[Trace] Finished converting JSON to object
[Error] Parameter count mismatch.
Looking at the documentation, it looks like you can use the basic where method which takes a property and a value. For example, assuming you have a dictionary of key value pairs, you could do this:
var filters = new Dictionary<string, object>
{
{ "Year", 2017 },
{ "CategoryId", 198 },
{ "IsPublished", true },
}
var query = new Query("Posts");
foreach(var filter in filters)
{
query = query.Where(filter.Key, filter.Value);
}

ORMLite SqlList with Tuples

I'm having some troubles selecting a Tuple of Objects from my custom SQL query with ORMLite.
I have the following code:
var query = "select definition.*, timeslot.*, type.* from <blah blah>";
var defs = dbConnection.SqlList<Tuple<Definition, Timeslot, Type>>(query, new
{
/* query parameters */
});
The query itself is fine (I've tested it in SQL Management Studio).
The code above sets attributes only for the first item of the Tuple, leaving to the default state the others.
I've selected singularly each object and the result is correct (so no trouble during the conversion to POCO I guess).
Same thing goes if I use Select<Tuple<Definition, Timeslot, Type>> instead of SqlList.
I couldn't manage to try with MultiSelect since it appears to not take a string.
What is the correct way to select a Tuple in this manner?
I am working in C#.
Thanks in advance!
SelectMulti seems to be what you're looking for here.
From the documentation under the Selecting multiple columns across joined tables heading:
// Note: I'm making an assumption on your query here.
// Build the `q` object however it needs to be.
var q = db.From<Definition>()
.Join<Definition, Timeslot>()
.Join<Definition, Type>();
var results = db.SelectMulti<Definition, Timeslot, Type>(q);
foreach (var tuple in results)
{
var definition = tuple.Item1;
var timeslot = tuple.Item2;
var type = tuple.Item3;
}

Using NHibernate function to filter results with QueryOver

I am trying to use a SQL Function to filter results of a query. I have a function in SQL called SplitKeys that takes a csv string and returns a table of integers. I want to produce the following SQL (or close to it):
select * from Members where MemberKey in (select * from SplitKeys('1,2,3'))
I''m using QueryOver and can't seem to get something to produce that where clause. Since I couldn't get that to work, I created another function that took a string and id and returned the id if it was in the list, or -1 if not.
session.QueryOver<Member>().Where(
Restrictions.Ge(
Projections.SqlFunction(
"dbo.IsKeyInList",
NHibernateUtil.StringClob,
new[]
{
Projections.Constant(
keyList,
NHibernateUtil.StringClob),
Projections.Constant(',', NHibernateUtil.Character),
Projections.Property<Member>(x => x.MemberKey)
}),
0));
This works great, except it's slow. with the first query, the function is called once and it can then filter based on the table. In the second it calls the function for every row.
I originally had this as a list of ints and passed that in. But, the problem with that is each element of the list is a parameter in the SQL and the max parameters is 2100.
session.QueryOver().WhereRestrictionOn(x => x.MemberKey).IsInG(intKeyList);
I've looked and looked and can't seem to find someone who has done that or get NHibernate and the C# compiler to like it. I tried this and it doesn't work. This and other variations either throw an exception with No Mapping for System.In32 or it returns nothing.
var keys =
session.QueryOver<MemberKey>()
.Select(
Projections.SqlFunction(
"dbo.SplitKeysCSV",
NHibernateUtil.StringClob,
new[] {
Projections.Constant(keyList),
Projections.Constant(delimiter),
}));
I tried creating some sort of mapping for the results, but I can't do that because there is no table. I tried using the .In() function, but that doesn't accept a Projects.SqlFunction.
You can do this using a SqlCriterion:
var sqlCrit = new SQLCriterion("{alias}.MemberKey in (SELECT * FROM dbo.SplitKeys(?))",
new[]{ keyList }, new IType[]{ NHibernateUtil.String })
var keys = session.QueryOver<Member>()
.Where(sqlCrit)

How to create a fully dynamic linq query?

I need to build about 30 different administration pages to add/edit/delete records from 30 different tables. I could obviously spend the time creating 30 unique pages, to query each table, but I'm curious if there's a way to simply create a single, dynamic page that queries a single, dynamic linq query. This linq query then returns all fields & records from a specified table.
I've seen examples of dynamic linq similar to this one (http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx), but that still requires hardcoding the table name into the query. I'd like to do a select all similar to this, where I pass in the name of the table (i.e. "Products", "Orders", etc), and then somehow query that table:
private List<tableName> MyDynamicQuery(string tableName)
{
IEnumerable<tableName> dynamicList;
using (MyEntities db = _conn.GetContext())
{
dynamicList = (from q in db.<tableName>
select q).ToList();
}
return dynamicList;
}
Is something like this even possible to do?
Thanks
Instead of using table names, why don't you pass in a selector? It would look something like this:
private List<T> GetData<T>(Func<MyEntities, IEnumerable<T>> selector)
{
using (MyEntities db = _conn.GetContext())
{
return selector(db).ToList();
}
}
You'd use it like so:
var orders = GetData(db => db.Orders);
You could use entity framework and do this:
dynamiclist = this.datacontext.Set<T>().ToList(); // where T is the Type, represents the table in EF

Using Dapper to map more than 5 types

I am currently building a SELECT query that joins 12 tables together. I've been using Dapper for all my other queries and it works great. Problem is, the generic methods only have to five generic parameters.
I've previously modified the code to support up to 6 for another query, but now I really don't think I should be hacking 6 more levels of generics.
Is there a way to pass dapper an array of types, and it returns the results as an array of objects, which I can cast manually if I have to?
I also might be approaching the problem the wrong way! Any help will be appreciated!
In a project I worked on I saw something like this to get more than 7 types mapped. We used Dapper 1.38:
connection.Query<TypeOfYourResult>
(
queryString,
new[]
{
typeof(TypeOfArgument1),
typeof(TypeOfArgument2),
...,
typeof(TypeOfArgumentN)
},
objects =>
{
TypeOfArgument1 arg1 = objects[0] as TypeOfArgument1;
TypeOfArgument2 arg2 = objects[1] as TypeOfArgument2;
...
TypeOfArgumentN argN = objects[N] as TypeOfArgumentN;
// do your processing here, e.g. arg1.SomeField = arg2, etc.
// also initialize your result
var result = new TypeOfYourResult(...)
return result;
},
parameters,
splitOn: "arg1_ID,arg2_ID, ... ,argN_ID"
);
The queryString is self-explanatory. The splitOn parameter says how Dapper should split the columns from the SELECT statement so that everything can be mapped properly to the objects,
you can read about it here.
You could use a dynamic query and map it afterwards. Something like this
var result = conn.Query<dynamic>(query).Select(x => new Tuple<Type1, Type2, Type3, Type4, Type5>(
// type initialization here
new Type1(x.Property1,x.Property2),
new Type2(x.Property3,x.Property4),
new Type3(x.Property5,x.Property6) etc....));
Edit: With a rather huge result set, another option might be to use multiple querys and then use a Grid Reader. That might work for you.
There's the example taken from the dapper age:
var sql =
#"
select * from Customers where CustomerId = #id
select * from Orders where CustomerId = #id
select * from Returns where CustomerId = #id";
using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
}
This has been answered long time ago, but I would like to add my two cents here. Instead of manually modify Dapper's source code, why don't you just create a poco class with those fields and use your query like a table?
The mapping would work fine, I know it is a pain also to do that class definition, but seems easier than dealing with later Dapper's updates.

Categories