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;
}
Related
My Xamarin app pulls data from an API and inserts that data into a SQLite table. The API currently has 9 tables defined, and as such there are 9 classes in my app that match those tables. I used the code snippet from this question's accepted answer:
Getting all types in a namespace via reflection
Below is my code, using the snippet from the answer and the foreach loop I'm trying to build that'll insert the data.
string nspace = "App.Tables";
var q = from t in Assembly.GetExecutingAssembly().GetTypes()
where t.IsClass && t.Namespace == nspace
select t.Name; // Getting list of classes as IEnumerable
var L = q.ToList(); // Converting to List
foreach (var name in L) // Inserts data for every class found
{
var response = await httpClient.Value.GetStringAsync("http://website.com/api/" + name + "s"); // Substitutes each class name into API url
var data = JsonConvert.DeserializeObject<List<TableName>>(response); // Deserializes into List
using (SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation)) // Opens database connection
{
conn.CreateTable<TableName>(); // Create table
conn.DeleteAll<TableName>(); // Delete all old data
conn.InsertAll(data); // Inserts new data
}
}
I don't know what TableName should be in order to get the correct class for each item on the list. For example: say the list contained the strings Table1, Table2, and Table3 - it got those strings from the App.Tables namespace which contains three separate classes called Table1, Table2, and Table3. When the first part of the code gets the list and stores it as the variable L, it should get the data for each "name" variable in L and then insert it into the matching table. How do I refer it to the table?
Before I would give my answer, I would like to tell you that I do not
recommend updating tables via reflection - tables should contain
logically distinct entities, so batch deleting and updating them is
kinda weird. This is one of those few occurrences where I would never
work around typing the updates of the tables one by one. But those are
just my two cents... Of course if I had a thousand tables my opinion
would not stand.
This kind of reflection also makes your code hard to follow and trace - think about how you will search for usages of the CreateTable<ExampleClass>() method? You will never trace it back - or only via great efforts - that you called it in this piece of code.
So to answer your question...
You first get the method group, then create a generic version of it based on the type. I think converting from Type to string is unnecessary for the part you're looking for, since you need to convert back to Type
using (SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation)) {
MethodInfo nonGenMethodCreate = typeof(SQLiteConnection).GetMethod("CreateTable");
MethodInfo nonGenMethodDeleteAll = typeof(SQLiteConnection).GetMethod("DeleteAll");
MethodInfo nonGenMethodInsertAll = typeof(SQLiteConnection).GetMethod("InsertAll");
foreach(Type t in Assembly.GetExecutingAssembly.GetTypes()) {
MethodInfo genMethodCreate = nonGenMethodCreate.MakeGenericMethod(t);
MethodInfo genMethodDeleteAll = nonGenMethodDeleteAll.MakeGenericMethod(t);
MethodInfo genMethodInsertAll = nonGenMethodInsertAll.MakeGenericMethod(t);
genMethodCreate.Invoke(conn, null);
genMethodDeleteAll.Invoke(conn, null);
genMethodInsertAll.Invoke(conn, new[] { data });
}
}
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);
}
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.
I am currently building reports and have a need to Group columns dynamically, depending on user's choice. Now, assuming that the situation is fixed on all columns, the query would be as follows:
var groupedInvoiceItems = invoiceItems.GroupBy(x => new { x.SalesInvoice.name, x.SalesInvoice.currencyISO, x.CatalogProduct });
Doing so would return results as desired, IGrouping. I would then run a loop to process the necessary data as below:
foreach (var groupedInvoiceItem in groupedInvoiceItems)
{
// Perform work here
}
Now, the headache comes in when I try to make the Grouping dynamic by using Dynamic Linq. The query is as follows:
var groupedInvoiceItems = invoiceItems.GroupBy("new (SalesInvoice.name, SalesInvoice.currencyISO, CatalogProduct)", "it");
The problem with this is that it does not return IGrouping anymore. Hence, my foreach loop no longer works. Is there any solution to the matter? I tried casting IGrouping to the Dynamic query but to no avail. Help is needed urgently.
The result of the GroupBy is an IEnumerable<IGrouping<DynamicClass,InvoiceItem>>, so you can proceed by something like:
foreach (IGrouping<DynamicClass,InvoiceItem> invoiceItemGroup in groupedInvoiceItems)
{
}
You should do the grouping then select the specified attributes to iterate throw them in the foreach loop. Try this out:
var groupedInvoiceItems = invoiceItems.GroupBy("SalesInvoice.name","it").GroupBy("SalesInvoice.currencyISO","it").GroupBy("CatalogProduct","it").Select("new (it.SalesInvoice.name,it.SalesInvoice.currencyISO,it.CatalogProduct)");
Hope this works.
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.