Dapper multilevel nesting - c#

I am trying to use Dapper in my project to speed up data loading (currently using EF6)
Here is my SQL
String SQL = #"select vwArtikli_Grid_V2.ArtikalID
,vwArtikli_Grid_V2.ArtikalNaziv
,Artikli_TagLista.ArtikalTagListaID
,Artikli_TagLista.ArtikalTagID
,Artikli_Stanje.ArtikalStanjeID
,Artikli_Stanje.ObjekatID
,Artikli_Stanje.Stanje
,Artikli_Tagovi.GrupaID
,Artikli_Tagovi.ArtikalTagGrupaID
,Artikli_Tagovi.ArtikalTagNaziv
,Artikli_Tagovi.ArtikalTagPrint
,Artikli_Tagovi.ArtikalTagSlika
,Artikli_Tagovi.ArtikalTagID
,vwArtikli_Grid_V2.ArtikalID
from Artikli_Tagovi
inner join Artikli_TagLista on Artikli_Tagovi.ArtikalTagID = Artikli_TagLista.ArtikalTagID
right outer join vwArtikli_Grid_V2 on Artikli_TagLista.ArtikalID = vwArtikli_Grid_V2.ArtikalID
left outer join Artikli_Stanje on vwArtikli_Grid_V2.ArtikalID = Artikli_Stanje.ArtikalID;
I am using my Entity Framework Entities as POCOs and they are
VwArtikliGridV2, Artikli_TagLista, Artikli_Tagovi, Artikli_Stanje
VwArtikliGridV2 has two properties
public virtual ICollection<Artikli_TagLista> Artikli_TagLista { get; set; }
public virtual ICollection<Artikli_Stanje> Artikli_Stanje { get; set; }
and Artikli_TagLista has
public virtual Artikli_Tagovi Artikli_Tagovi { get; set; }
What is the easiest way to execute the query and map my data to the POCOs or Entities ?
I tried
Dapper.Mapper
var Artikli = cn.Query<VwArtikliGridV2, Artikli_TagLista, Artikli_Stanje, Artikli_Tagovi> (SQL);
but it didnot work
I also tried Slapper.AutoMapper
List<dynamic> ArtikliUM = cn.Query<dynamic>(SQL).ToList();
Slapper.AutoMapper.Configuration.AddIdentifiers(typeof(Artikli_Tagovi), new List<string> { "ArtikalTagID" });
Slapper.AutoMapper.Configuration.AddIdentifiers(typeof(Artikli_TagLista), new List<string> { "ArtikalTagListaID" });
Slapper.AutoMapper.Configuration.AddIdentifiers(typeof(Artikli_Stanje), new List<string> { "ArtikalStanjeID" });
Artikli = (Slapper.AutoMapper.MapDynamic<VwArtikliGridV2>(ArtikliUM) as IEnumerable<VwArtikliGridV2>).ToList();
But it also did not work.
I can map VwArtikliGridV2 but i cant map any of the nested objects. They are always null.
What can I try ?

Dapper maps things flat. At first this feels like a big pain because EF nests things so easily. But once you get over the initial pain you realize how simple, predictable, and performance-oriented it is. Maybe an extra 15 min. of query-crafting for untouchable performance.
I answered a similar question here return a list of data via stored proc to dapper.
You should be able to return multiple datasets (one for the parent item, one for the underlying tags, etc.) and combine them in your app tier.

As BlackjacketMack said in his answer, I think multiple data sets are the way to go.
You might want to look in the Dapper documentation at the features Multi Mapping to split single rows into multiple objects, and QueryMultiple to read multiple result sets from a single query.
Obviously, it would mean modifying your query to return multiple result sets, but could achieve what you are looking for.
Examples can be found here: https://github.com/StackExchange/Dapper/blob/master/Dapper.Tests/MultiMapTests.cs
https://github.com/StackExchange/Dapper/blob/master/Dapper.Tests/QueryMultipleTests.cs
In particular, you might want to take a look at method public void TestMultiMapThreeTypesWithGridReader() in MultiMapTests.cs. I found that very useful to help me understand a similar problem of parent with child collections, where the child contains different object types returned in a single row.

Related

LINQ to Entity Any() with related Object Collection

First, let me say that I've researched this problem and read the following stack overflow articles, but none of them really address this situation.
How can I use Linq to join between objects and entities?
inner join in linq to entities
Situation
I have two classes
public class Section{
public string SchoolId{get;set;}
public string CourseId{get;set;}
public string SectionId{get;set;}
}
public class RelatedItem{
public string SchoolId{get;set;}
public string CourseId{get;set;}
public string SectionId{get;set;}
//..
}
I have an array of Section coming from one source and is an actual collection of Objects.
RelatedItem I'm getting via a LINQ to Entities call against a DbContext.
My goal is to get all of the RelatedItems based on the Sections I have from the other source.
I'm writing a query like this
Section[] mySections = GetSections(); //Third Party Source
IQueryable<RelatedItem> relatedItems = DbContext.RelatedItems
.Where(r=>
mySections.Any(s=> s.SchoolId == r.SchoolId &&
s.CourseId == r.CourseId &&
s.SectionId == r.SectionId)
);
Problem
At runtime, I receive the following error
Unable to create a constant value of type
'ProjectNamespace.Section'. Only primitive types or
enumeration types are supported in this context.
I found a work around, but it involves doing the following, but it doesn't take advantage of any of my table indexes.
var sectionIds = sections.Select(s=>string.Concat(s.SchoolId, "|",s.CourseId, "|",s.SectionId));
IQueryable<RelatedItem> relatedItems = DbContext.RelatedItems
.Where(r=>
sectionIds.Contains(string.Concat(r.SchoolId, "|",r.CourseId, "|",r.SectionId))
);
This block of code works, and currently is pretty fast (but this is dev, and my record count is small). Aside from converting my related items to a collection in memory, does anyone have any other suggestions?
Try using Contains instead:
Section[] mySections = GetSections(); //Third Party Source
IQueryable<RelatedItem> relatedItems = DbContext.RelatedItems.Where(r=>
mySections.Select(s => s.SchoolId).Contains(r.SchoolId) &&
mySections.Select(s => s.CourseId).Contains(r.CourseId) &&
mySections.Select(s => s.SectionId).Contains(r.SectionId)
);
Contains should translate to WHERE IN clauses in SQL.
This won't work if using .NET 3.5 and LINQ to Entities, as it wasn't implemented in that version.
Proper way to solve this is to implement IEquitable. Here is an example on how to do it Does LINQ to Entities support IEquatable in a 'where' clause predicate?
One tip when implementing Equals() and GetHashCode() do not call any .NET methods (like getType()) only compare primitives SchoolId, CourseId, SectionId, it should get converted to expression tree and work just fine.

c# Linq to Sql dynamic Data Context assignment

`Hi,
Can somebody please give me a pointer on this? I have 8 servers each with 8 databases which look identical exept server/database name. We are talking thousands of tables.
I create my data contexts with sqlmetal.exe
After creating my data contexts, I import them into the application and then I run comparison scripts over the databases to compare results.
My problem is dynamically switching between data contexts.
Datacontext.DAL.DUK1 duk1sdi = new Datacontext.DAL.DUK1(connectionString);
Datacontext.DAL.DUK3 duk3sdi = new Datacontext.DAL.DUK3(connectionString);
string fromOne = runQuery(duk1sdi);
string fromThree = runQuery(duk3sdi);
public static string runQuery(DataContext duk)
{
var query =
from result in duk.TableA
select result.Total;
string returnString = query;
return returnString;
}
I have no problem with the query running when the duk is predefined, however how do I define and pass the datacontext to the function?
The error I get is:
Error 1 'System.Data.Linq.DataContext' does not contain a definition
for 'TableA' and no extension method 'TableA' accepting a first
argument of type 'System.Data.Linq.DataContext' could be found (are
you missing a using directive or an assembly reference?)
You could use the GetTable<T> method, where T is the type of the table, e.g. TableA.
public static string runQuery(DataContext duk) {
var table = duk.GetTable<TableA>();
var query = from result in table select result.Total;
...
}
However, all types of TableA will need to be the same type, strictly (I'm pretty sure).
Otherwise you would need to literally branch the logic for the handling of each context. Since you can extend your DataContext instances (in general, maybe not in your specific case) then you could have them share an interface that exposes a collection property of TableA, but you would need a higher level context wrapper to pass around then - unless you pass around the collection by altering the method signature.
You can use interfaces. Check this answer, but be sure to script the interfaces using a .tt file with the amount of tables you have.
Edit:
If you have generated contexts which you want to use interchangeably in a reusable method, you have the problem that the generated TableA classes are not reusable, since they are different types (even though the names may match, but that doesn't make them equal). Therefore you need to abstract the actual types, and one way to do this, is to use interfaces. You build your reusable method around an interface which abstracts the specific context-type and table-type. The downside is that you have to implement the interfaces on the generated contexts and tabletypes. This though is something you can solve using a .tt script.
Pseudo code:
// Define interface for table
public interface ITableA {
// ... properties
}
// Define interface for context
public interface IMyContext {
IQueryable<ITableA> TableA { get; }
}
// Extend TableA from DUK1
public partial class TableA: ITableA {
}
// Extend DUK1
public partial class Datacontext.DAL.DUK1: IMyContext {
IQueryable<ITableA> IMyContext.TableA {
get { return TableA; }
}
}
// Same for DUK3 and TableA FROM DUK3
// Finally, your code
Datacontext.DAL.DUK1 duk1sdi = new Datacontext.DAL.DUK1(connectionString);
Datacontext.DAL.DUK3 duk3sdi = new Datacontext.DAL.DUK3(connectionString);
string fromOne = runQuery(duk1sdi);
string fromThree = runQuery(duk3sdi);
public static string runQuery(IMyContext duk) {
// Note: method accepts interface, not specific context type
var query = from result in duk.TableA
select result.Total;
string returnString = query;
return returnString;
}
If your schema is identical between databases, why script the dbml for all of them? Just create one context with it's associated classes and dynamically switch out the connection string when instantiating the context.
var duk1sdi = new Datacontext.DAL.DUK1(connectionString1);
var duk3sdi = new Datacontext.DAL.DUK1(connectionString2);
Thanks, guys, I think I found the simplist solution for me based a bit of both your answers and by RTFM (Programming Microsoft Linq in Microsoft .NET Framework 4 by Paulo Pialorsi and Marco Russo)
In this way I don't have to use the large DBML files. It is a shame because I'm going to have to create hundreds of tables in this way, but I can now switch between connection strings on the fly.
First I create the table structure. (outside the program code block)
[Table(Name = "TableA")]
public class TableA
{
[Column] public int result;
}
Then I define the table for use:
Table<TableA> TableA = dc.GetTable<TableA>();
And then I can query from it:
var query =
from result in TableA
select TableA.result;

PetaPOCO and more than 4 joins

Is petapoco capable of achieving the following :
1.Unlimited joins in one query
2.Unlimited One to Many relations in one query
I have looked at PetaPOCO and it seems like it is not capable of doing more than 4 joins, the longest signature looks like :
db.Query<T1, T2, T3 , T4>
Also seems like it supports a one to many relation , but only for one composite object such as below :
db.FetchOneToMany<T1, T2> where T2 is a foreign key of T1
I'm testing some of the micro ORMs out there to stick to the best one. Do you know of any of them that can handle these situations and if none of the micro ORMs are supporting this feauture, how do you deal with an object that is like the following :
class A
{
List<B> member1;
List<C> member2;
Z member3; //Composit object
Z1 member4; //Composit object
Z2 member5; //Composit object
Z3 member6; //Composit object
Z4 member7; //Composit object
}
And then even more complicated is , what if member one (type B) has some composite object within itself ? What if we have :
class B
{
G member0;
}
Please don't propose a solution to hit database multiple times, coz it's going to be way too many calls when the objects become just a little bit complex.
Oh and i also know that one other way of tackling the case of unlimited joins is creating a very flat object that hols all fields combined. It's not an elegant solution at all.
The T1..T$ Query() overloads all pass through to the main Query(..Type[]..) method. You can either add more Query() overloads yourself to handle more T parameters, or pass in all the types you need in a Type array (which is what the T1-T4 functions do) :
Query<TRet>( new Type[]{typeof(Poco1), typeof(Poco2), typeof(Poco3), typeof(Poco4), typeof(Poco5)}, null, sql, args);
You can have multiple one to many relationships but Schotime is right, you need to be very careful of swathes of duplicate data coming back in your result set. Write the sql query and look at the result set, is the amount of duplication acceptable to you? If so then in Petapoco there is a concept of relator callbacks where you write a small class that handles the different pocos in a single result row and add each poco to the list properties on the parent poco.
http://www.toptensoftware.com/Articles/115/PetaPoco-Mapping-One-to-Many-and-Many-to-One-Relationships
I've never had to do this with multiple one to many but quoted from the above
"If you're joining more than two tables you'll need something more
complex but it's really just extensions of the above."
Another option is to have a stored procedure that does all the work in a single database request and have it return multiple result sets which I believe Schotime has achieved in his branch of petapoco but I've not used it myself yet so I can't really comment on if it will help here :
http://schotime.net/blog/index.php/2011/11/20/petapoco-multiple-result-sets/
If I absolutely had to wire up all the data in one go for objects as complex and nested as you are suggesting then I would use a stored procedure (a single db call) and stitch it all together with code. Only then would I figure out how to do this in Petapoco. However if your UI doesn't show all the nested data until the user clicks on an expander button (or similar) I'd use an AJAX call at that point rather than get all the data initially.
The answer is correct, but I came to this page from another forum and no one there could make this work, so I thought I would chip in what I did to make things clearer. Basically, I had code like the following:
var sql = "select * from someTable where tableId = #0";
var listOfStuff = _petapoco.Fetch<FirstType, SecondType, ThirdType, FourthType, FirstType>(new RelatorClass().MapIt, sql, idVar);
Since I needed to add in a fifth poco, and all the Fetch methods eventually lead to the master Query method listed above in the accepted answer, I had to do this:
var sql = "select * from someTable where tableId = #0";
Func<FirstType, SecondType, ThirdType, FourthType, FifthType, FirstType> mapIt = new RelatorClass().MapIt;
var listOfStuff = _petapoco.Query<FirstType>(new[] { typeof (FirstType), typeof (SecondType), typeof (ThirdType), typeof (FourthType), typeof(FifthType)}, mapIt, sql, idVar).ToList();
Now I can query with 5 pocos and I didn't have to modify the PetaPoco code. The only other thing to do would be to add to your relator class so you can tell PetaPoco where to map the new data and you're good to go.
Delegate:
Note: you don't have to return the (UserActivity,int) anonymous type like this, you can return a single type without the parenthesis! I'm just lazy and don't want to create a new model for the return type.
private delegate (UserActivity, int) GetIt(int fk_AccountTypeValue, UserActivityModel ua, User u, Client c, Client_Account ca);
SQL Operation:
public List<(UserActivity,int)> SomeMethodName(int orgnizationID)
{
var sql = Sql.Builder
.Select("TOP(200) at.FK_AccountTypeValue, ua.*, u.*, c.*, ca.*")
.From("UserActivity ua")
.LeftJoin("Users u").On("u.PK_UserID = ua.FK_UserID")
.LeftJoin("Client c").On("c.FK_UserID = u.PK_UserID")
.LeftJoin("Client_Account ca").On("ca.FK_ClientID = c.PK_ClientID")
.LeftJoin("AccountType at").On("at.PK_AccountType = c.FK_AccountTypeID")
.Where("u.FK_OrganizationID =#0", orgnizationID)
.OrderBy("ua.Timestamp desc");
GetIt obj = new GetIt(youKnowIt);
var typs = new Type[]{typeof(int), typeof(UserActivityModel), typeof(User), typeof(Client), typeof(Client_Account)};
var uaList = _database.Query<(UserActivity, int)>(typs, obj, sql.SQL, sql.Arguments).ToList();
return uaList;
}
Method pointed to by the delegate:
private (UserActivity,int) youKnowIt(int fk_AccountTypeValue, UserActivityModel ua, CurrentDesk.Models.User u, CurrentDesk.Models.Client c, CurrentDesk.Models.Client_Account ca)
{
// do stuff
var uam = new UserActivity()
{
// assign stuff
};
return (uam, fk_AccountTypeValue);
}

pulling a non-trivial object from a MySQL database

I have a C# .NET 3.5 project using a MySQL database.
I have an object Task which I would like to be able to create by pulling it from a series of database tables.
public class Task
{
public Task()
{
Values = new List<string>();
OtherValues = new List<string>();
Requirement = string.Empty;
Minimum = 1;
Children = new List<Foo>();
}
public IList<string> Values { get; set; }
public IList<string> OtherValues { get; set; }
public string Requirement { get; set; }
public int Minimum { get; set; }
public int Maximum { get; set; }
public IList<Foo> Children { get; set; }
}
I'd like to be able to get the tasks from a TaskList which would lazily read elements of the task as they were accessed by an enumerator.
public class TaskList : IEnumerable<Task>
{
/* ... */
public IEnumerator<Task> GetEnumerator()
{
string query = #"SELECT my_task.*, `Order` FROM my_task ORDER BY `Order` DESC";
using (MySqlConnection connection = new MySqlConnection(connection_string_))
using (MySqlCommand command = connection.CreateCommand())
{
command.CommandText = query;
connection.Open();
using (MySqlDataReader reader = command.ExecuteReader())
{
yeild /* ??? */
}
}
}
}
How is this done?
You can serialize it to XML and store it as a string. Add the following function to Task:
public XElement Serialize()
{
return new XElement("Task",
new XElement("Values",from val in Values select new XElement("Item",val)),
new XElement("OtherValues",from val in OtherValues select new XElement("Item",val)),
new XElement("Requirement",Requirement),
new XElement("Minimum",Minimum),
new XElement("Maximum",Maximum)
);
}
You will need to put using System.Linq; and using System.Xml.Linq; in the top of the .cs file.
I didn't write the code to serialize Children because I don't know what the data type Foo looks like, but you should serialize it in a similar manner. After you've done that, you can easily write the XML to the database, and read it back(write a constructor that parses the Xml into a Task object)
EDIT(addition):
Here is an example to a constructors that receives XML(or parse a string as XML):
public Task(string xmlSourceAsString):
this(XElement.Parse(xmlSourceAsString))
{
}
public Task(XElement xmlSource)
{
Values=(from itm in xmlSource.Element("Values").Elements("Item") select itm.Value).ToList();
OtherValues=(from itm in xmlSource.Element("OtherValues").Elements("Item") select itm.Value).ToList();
Requirement=xmlSource.Element("Requirement").Value;
Minimum=int.Parse(xmlSource.Element("Minimum").Value);
Maximum=int.Parse(xmlSource.Element("Maximum").Value);
}
EDIT(explanation):
You can't store your object as is in the database "as is", because it refers to other objects. For example - the list Values doesn't sit in the same place in memory as the rest of the object, befause it's a ref type - it refers to another object that sits in a different place in the memory. In matter of fact, the only parts of your object that are stored in the same place as the main object are the Minimum and Maximum, which are ref types, so if you could somehow store the object as is(laziest solution possible, if it worked), you would get your Minimum and Maximum fields right, but all other fields will point to the memory addresses where those objects where placed when you stored the Task object, which are now most likely invalid pointers(and I say "most likely" because it is also possible(though rare) that they will point to legitimate objects, maybe event of the same type - but they still won't have your data.
If you want the object with all it's data stored in a database(or in a file. or passed to a proccess that runs on another computer via network) you have to serialize it. Performance-wise the best way is to serialize it to binary(C# have some tools for that, but it's still more complex than XML).
Xml also have the adventage of being easily readable from most modern programming languages and database engines. MySQL has some functions to read and write XML, so you can update the object in the database and access it's fields from MySQL queries.
Conclusion
You asked for a solution that is easy(lazy), efficient, and sql-compatible(access to the object's fields from MySQL queries). I say you can only have two of your three requirements, but you can choose which two:
If you want something easy and efficient, even at the price of loosing compatibility, serialize your objects to binary. True, it's not as easy as XML, but .NET has some tools to help you with that.
If you want something efficient and compatible, and willing to do some work for that, you can put your object in MySQL the way databases are meant to be used - use separate tables for the lists that refers to the objects via OIDs, etc. This will require some work, but after you add the tables and code the MySQL functions and the C# functions that handle everything, you should be able to store, retrieve, and access your objects with ease.
If you want something easy and compatible, and you can afford loosing some efficiency, use my solution and serialize your objects to XML. This is the laziest solution - unless someone knows a library that can automatically serialize any object, LINQ to XML is the easiest way to do it, and requires much less code than any other solution.

How to create and populate a nested ViewModel well

I have a View Model that has some serious nesting. I need to populate it from Entity Framework 4. I tried creating one big linq statement to populate it, but it says it doesn't recognize the .ToList() methods. It compiles fine. Runtime error is
LINQ to Entities does not recognize the method
'System.Collections.Generic.List`1[ProductDepartment] ToList[ProductDepartment]
(System.Collections.Generic.IEnumerable`1[ProductDepartment])' method,
and this method cannot be translated into a store expression.
What is a more efficient way to populate something like this without doing several thousand database calls?
List<Product> Products {
int ID
string Name
...
List<Department> Departments {
int ID
string Name
}
List<Image> Images {
int ID
string Name
}
List<Price> Prices {
int ID
string Name
List<Version> Versions {
int ID
string Name
List<Pages> Pages {
int ID
string Name
} } } }
The horrible Linq code looks something like this
var myProducts = (from myProduct in DC.MyProducts
where p => p.productGroup == 1
select new Product {
ID = myProduct.ID,
Name = myProduct.Name,
Departments = (from myDept in DC.MyDepartments
where q => q.fkey = myProduct.pkey
select new Department {
ID = myDept.ID,
Name = myDept.Name
}).ToList(),
...
//Same field assignment with each nesting
}).ToList();
Update:
The fix was to remove all the .ToLists(), which worked better anyway.
Now I have to do filtering and sorting on the end product.
Well for starters, that is one crazy model, but i'm assuming you already know this.
Do you really need all that info at once?
I'll play devil's advocate here and assume you do, in which case you have a couple of logical choices:
1) As #xandy mentioned - use .Include to eager load your associations in the one call. This is assuming you have setup navigational properties for your entites in your EDMX.
2) Use a View. Put all that crazy joining logic inside the database, making your EF work a very simple select from the view. The downside of this is your queries to the view basically become read only, as i don't believe you can perform updates to an entity which is mapped to a view.
So it's your choice - if this is a readonly collection for displaying data, use a View, otherwise eager-load your associations in the one hit.
Also, be careful when writing your LINQ queries - i see you have multiple .ToList statements, which will cause the query to be executed.
Build up your query, then perform the .ToList once at the end.
why do you require all this informataion at one go? You can use lazy loading when a nested property is accessed?

Categories

Resources