-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Join support in POCO mode #355
Comments
Thank you for this. We are on top of it already. There will be detailed stories for this one soon. |
we need that, join and load related data. thanks a lot. |
You can do it in a different way. That is a basic scenario for RepoDb. See this link: https://repodb.readthedocs.io/en/latest/pages/connection.html#querymultiple It has recently been asked on our Gitter Chat. Or, here is the code for you. Querying Single Parent and its Children
Querying Multiple Parents and there Children
|
Tuple has limit. we need multiple join. we are developing banking solutions. we are working on huge data in our organization. we need join without limitation. |
it have performance issue see following commands exec sp_executesql N'SELECT [Id], [Name] FROM [Author] WHERE ([Id] = @T0_Id) ; its run 2 select command in sql server but we want just a select command with join. we can write 2 select command in our codes. your excellent micro orm needs load related data with hints, like left join, right join, inner join. if you do that your repoDb in perfect and awesome and amazing too. |
i found a solution! but it dont create join command in sql server. following classes are poco(s):
following lines are method used in a class, this class inherits from blow class:
and following methods are working like join not really joins in sql.
|
Thank you for your commendation in the first place. In anyway, the queries you made in your last comment seems to be not performant when compared to PS: I would assume that what you are looking for is really a "JOIN (ie: INNER JOIN, LEFT JOIN, RIGHT JOIN)" issued at the DB Server itself (and not just on the fly). This "JOIN" feature is "purposely" not being supported up until the current version. Yes, it is in purpose :) - You seems to be looking the "Include" features of EF Core which was previously supported by RepoDb via There is already a talk to re-enable this feature through this issue #263 . Anyway, it is fair that you would like this feature and I am planning to add the For now, since the feature is not available, would you be able to use |
hello dear mike i hope you are doing greate! |
Thank you for your interest in RepoDb. We are glad that you're helping us on our journey by sharing it to your colleagues. Please be reminded that your inputs are valuable to us and that it will be considered soon. However, we have a lined-up things-to-do. See below.
Right after the mentioned items above, I as an author will make sure that I will keep this ticket open until the feature is delivered. |
Honestly, I really do not like to put JOINs in RepoDb due to the fact that in order to make it more performant, it must be handled via SQL Scripts by the developer. Howerver, my decision has changed as the communities are requesting it. I will listen to all of you. I am considering this feature and as early as now, I would like to think the actual architecture of the actual implementation. I would like to gather some inputs from some of you about JOIN support. My intention as an author is to make it much more simpler (maybe not too close with IQueryable for now). But, I prefer to use the Linq.Expression on that actually for compilations. I am asking some of you to help me analyze the "ways" on how to do JOINs here. Think of it as an approach that you would like. If you are the developer, what/how would it be? You can share the way of coding you'd like. FYI: @myabdolmaleki @eMTeTeam @rdagumampan @calumchisholm Feel free to share to your network as well if they would like to contribute here. Thank you in advance. |
I'm not sure yet what the most sensible approach would be for the actual implementation, but JOINs are one of those areas where feature support seems to vary considerably across the different RDBMS platforms. Limiting any implementation to INNER JOINs would probably be a good idea while evaluating different approaches.
You might also consider implementing UNION / INTERSECT / EXCEPT as a first step - they should be more straightforward to develop than JOIN, but could provide you some insight into the direction you want to take with this. |
@calumchisholm - thank you for the valuable feedback (as always). I have received a bunch of questions and requests on JOIN operations (approximately 10-15 times). I can see that it is much more practical to prioritize this one. I would like to make a very simplistic approach of doing JOINs and definitely not taking the same path as EF. I need a collective response from the community before doing an architectural visits on this. It will affect the core implementation of RepoDb, that is why! :) |
are you considering the syntax or the actual code implementation, or both at this point? |
@fake-fur - thanks for digging into this. On this story, I am trying to ask the community the best way to handle the Your inputs to the My intention as an author is to create a |
hi mr mike does repodb supports sql join? |
Hey, it is in purpose that we did not supported the JOIN query (and/or SplitQuery) in RepoDb. We highly suggest to you to use the ExecuteQuery method instead and write your own JOIN SQL itself. |
Hi Mike! We're reviewing your ORM. About JOINs, at least in our case, it wouldn't be a problem to write our own joins in plain SQL (actually, that would be our preffered solution). My question is related to one-to-one JOINS, and how to map the results back to multiple objects... similar to what Dapper calls "multiple mapping", with the "splitOn" feature (which we kind of dislike, because we need to use literal strings). Is there (or will be) support on RepoDB for such kind of queries? In other words, let's say you need to retrieve an Order record, along with the Customer of each order. Is there a way to retrieve this with RepoDb in one shot?
And then, something like:
Then, the order object would be filled in with the Customer property as well? Thanks! |
@mbirnios - had you visited the QueryMultiple already? |
You can do it with QueryMultiple, but you have to handle the assignment of the child rows into the parent rows. FYI: it is executing multiple SQL statements in the DB, it is not a real JOIN. Then, it simply maps the result based on the order of generic types you passed in the QueryMultiple method. Max of 7 generic types; it is a Tuple object. |
Please, please support at least 1:1 joins. QueryMultiple makes sense for other associations but not having this for 1:1 is just too painful. If I have reference tables and I want to include those like this: public class ParentTable
{
public int Id { get;set;}
public string Name { get; set; }
public ReferenceType ReferenceType { get; set; }
}
public class ReferenceType
{
public int Id { get;set;}
public string Name { get; set; }
} Writing complex queries for a simple join seems like something this library would want to join. |
@cleverguy25, would you be able to share your thoughts on how would you like to do this as an extension of IDbConnection? I am aiming to the actual syntax. Let us say, you have these classes. public class Supplier
{
public int Id { get;set;}
public string Name { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class Product
{
public int Id { get;set;}
public string Name { get; set; }
} Would this work for you? var address = "New York";
var suppliers = connection.Query<Supplier, Product, Supplier>(supplier => supplier.Address = address).FirstOrDefault();
// Process the 'suppliers' and each of the 'supplier.Products' |
I would love to help, either with code or just on requirements. 👍 Btw your example references address which is not part of your model. I am shopping for a new ORM for a new product, but hesitant to pick RepoDB even though I immediately mostly love it. I need 1:1 joins. Please consider basic support for that and solve the harder 1:many, many:many problems in another way. I have written some homegrown ORMs in the past for previous companies, so have some experience here. But first, I think that this is jumping again to a 1:many or a many:many relationships, and those are more complicated. I would love to focus on 1:1 for some basic support. So an updated model: public class Supplier
{
public int Id { get;set;}
public string Name { get; set; }
public string Address { get; set; }
public SupplierType SupplierType { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class SupplierType
{
public int Id { get;set;}
public string Name { get; set; }
}
public class Product
{
public int Id { get;set;}
public string Name { get; set; }
} So if I didn't care about products for time being, I could just do Query, either always as it would be a simple join or a flag to include associations. var address = "New York";
var suppliers = connection.Query<Supplier>(supplier => supplier.Address = address).FirstOrDefault(); I expect in my FluentMap or Attributes I will need to inform you about the join, and whether it could be null or not (or do you infer that with schema binding?) So you can decide on what JOIN type to use. For 1:many or many to many, I do not mind just doing QueryMultiple and either hand-coding some SQL, or the way you have it today. I do like I am not a fan of the |
Btw, if we can find a way to direct message, we can sync a time to do a zoom or teams call and talk through this, if it helps. I want to point out that even if I hand tune the SQL, ExecuteQuery or ExecuteQueryMultiple do not help as I cannot extract from the result set if I got the extra columns via Join. So the only way to do 1:1 is to do dynamic and write a separate mapper to my POCO. That is what TinyORM advocates anyways. |
For some context. Joint Clause can get complex and it might cause circular dependency in POCO if not handled properly. In one of the projects I worked on, there were 6 tables related to each other. Off topic: I asked about similar issue here dotnet/efcore#14525 (comment) |
@cleverguy25 - of course we can. I am also open for a WhatsApp call. A lot in the community are requesting this feature TBH. Can you share me your Teams (account) or WhatsApp (mobile number) so we can plan and schedule a meeting? EDIT: Please do send it on my email address, can be found on the documentation contact page. |
@tola , that's also my point on my side, that's why JOIN is not supported in this library. Though, I have solve this cyclomatic complexity problem in the early days of RepoDb via |
@tola I agree, but usually there are some signs of either bad ORM model or bad SQL table design. For hydrating deep hierarchies, you should be writing your own SQL. I want to simplify for 1:1 relationships that can map to 1:1 JOINS. I rarely see 1:1 that are circular, and if that exists then it could be detected and not generate SQL and handle in mapping. That is what I have done with past ORMs. But also even if I have to hand-write some SQL, right now I cannot easily map it using RepoDbs mapper. I do not mind writing my SQL, but if hand-write mapping code as well, I can just do that via editor macros and use raw ADO.Net. But I think |
RepoDb mapper is not limited if you are returning the resultsets into single model, specially if that does not have a relationship (like Dapper). Do you mean, relationship mapping? If not, can you elaborate this with some samples? |
It is not a single model though. I know how to do this with custom SQL: public class Supplier
{
public int Id { get;set;}
public string Name { get; set; }
public string Address { get; set; }
public int SupplierTypeId { get;set;}
public string SupplierTypeName { get; set; }
} But I want this: public class Supplier
{
public int Id { get;set;}
public string Name { get; set; }
public string Address { get; set; }
public SupplierType SupplierType { get; set; }
}
public class SupplierType
{
public int Id { get;set;}
public string Name { get; set; }
} My understanding is I would have to do ExecuteQuery to dynamic object, then map that dynamic to my POCO model. If I have to do that I can just write ADO.Net. |
@cleverguy25 , that make sense. The same scenario as above, you shared. |
Hi @mikependon ! Thanks for your support. I didn't try the QueryMultiple, since I believe that's not what I would need. My use case is exactly the one that @cleverguy25 recently posted, where he was trying to retrieve Supplier along with SupplierType. In other words, a 1:1 relationship, a very simple join. I'm not trying to retrieve and order with a collection of products, I think that's clear on your docs. The way 1:1 relationships are solved in Dapper (which is what we're using now), is to use their "Multiple Mapping" feature (either automatically if you have "Id"s properties, or via the "Split On" param). Basically, you supply a SQL with your JOINs, and 4 or 5 tables comes into the same row. In the Supplier example:
Because by convention they use the Id property, they can execute:
And that would fullfill your supplier object, with the https://medium.com/dapper-net/multiple-mapping-d36c637d14fa BTW, our approach to ORMs here in the company is to write our own SQL for complex queries, to take the most out of the engine. We're not looking for a complex C# to emulate the JOIN. I believe that's where EF goes out of whack, and there's never going to be a perfect solution. Then, map that to our object to model using Dapper. And then yes, we do persistance with EF, to take advantage of the change tracking features. |
@mbirnios - thanks for the insights. What you and @cleverguy25 is requesting is doable, it is a minor tweak in the compiler. We will support this soon! I have a scheduled call with @cleverguy25 to know more about their use-case, after that, I will share the updates on this User Story and will line-it-up for the development. |
@myabdolmaleki @mbirnios @fake-fur @tola @calumchisholm - hi gents, I have a very productive talks with @cleverguy25 today and we are both planning to introduce the feature in RepoDB in which you would be able to make the 1:1 mapping. We are also looking the 1:Many mapping and/or Many:Many mapping, but the intention is first to support the simple case possible. Please remember that this is the initial support of RepoDB in relation to JOIN features, we are doing it in a different way. Let us say you have the following models. public class Teacher
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Address
{
public int Id { get; set; }
public string ZipCode { get; set; } // some has strings, AZ5800
public string Street { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Teacher Teacher { get; set; }
} The model properties will be filled properly if you are to execute the using (var connection = new SqlConnection(connectionString))
{
var sql = @"SELECT S.Id
, S.Name
, A.Id AS AddressId
, A.Street AS AdressStreet
, A.City AS AddressCity
, A.Country AS AddressCountry
, T.Id AS TeacherId
, T.Name AS TeacherName
FROM [dbo].[Student] S
INNER JOIN [dbo].[Address] A ON A.Id = S.AddressId
LEFT JOIN [dbo].[Teacher] T ON T.Id = S.TeacherId
WHERE (S.Age >= @Age);";
var student = connection.ExecuteQuery<Student>(sql, new { Age = 18 });
// The 'student.Teacher' will automatically be filled with the information
} Notice the aliases, it should always be prefixed by the table name. So this could also be related to multiple 1:1 relationship, not just On the other hand, if you are to use the fluent approach like FluentMapper
.Entity<Student>()
.Association<Address>(t => a.Id == s.AddressId, AssociationType.Join);
.Association<Teacher>(t => t.Id == s.TeacherId, AssociationType.Left); Or via literal string. FluentMapper
.Entity<Student>()
.Association<Address>(new RelationDefinition("Id", "AddressId"), AssociationType.Join);
.Association<Teacher>(new RelationDefinition("Id", "TeacherId"), AssociationType.Left); And by doing the normal using (var connection = new SqlConnection(connectionString))
{
var student = connection.Query<Student>(10045);
// The 'student.Teacher' and 'student.Address' will automatically be filled with the information
} You can as well associate using the attribute [Association(new RelationDefinition("Id", "AddressId"), AssociationType.Join)]
public Address Address { get; set; } Or [Association(new RelationDefinition("Id", "TeacherId"), AssociationType.Left)]
public Teacher Teacher { get; set; } We hope it is okay and make sense to you guys! The depth of the relationship will only be limited to 2, beyond than that, we will throw an exception. I hope that is not a complex configuration to you as a user. |
Good progress. Looking forward for more depth of relationship. I usually have a query or stored procedure with more related tables. |
@mikependon looks like a nice start, but i agree with @tola more depth would be nice |
@tola @travbeamo , thank you for the replies. Please also try to drag friends, colleagues or somebody from the community if they can share/give us more feedback before we dive-down into this solution. We would like to mediate our solution on all possible cases, but we will not deeply support what the deep-object-graph of EF can. But I will make sure to align it with @cleverguy25 , but we will do it in a partial manner. |
Any updates on the Join feature in POCO? I think this is becoming an urgent thing to be achieved since it will spare the devs lot of complexity and give us better performance. |
Currently missing this feature to move 100% to RepoDb from Servicestack Ormlite. |
@stefandevo the same here as I'm planning to move from Dapper. Thank you so much for the great ORM |
@moalamri Can you point me to an example that shows how the way SmartSql joins tables? |
@tb-mtg the problem with SmartSql and SqlSugar is the documentation. They are written in Chinese.. |
Here's an example in Sqlsugar repository of table joining in POCO. var newDynamic = db.Queryable<Student, School, DataTestInfo>((st, sc, di) => new JoinQueryInfos(
JoinType.Left, st.SchoolId == sc.Id,
JoinType.Left, st.Name == di.String
)) .Select((st,sc,di)=>new { name=st.Name,scid=sc.Id }).ToList(); It says in the variable name that it returns dynamic but in fact it doesn't unless you cast it by adding (dynamic) to the select method as below var newDynamic = db.Queryable<Student, School, DataTestInfo>((st, sc, di) => new JoinQueryInfos(
JoinType.Left, st.SchoolId == sc.Id,
JoinType.Left, st.Name == di.String
)) .Select((st,sc,di)=>new (dynamic){ name=st.Name,scid=sc.Id }).ToList(); this is one of the problems i faced where i had to google translate the documents so i can understand. I still use Repodb tho for other non rational queries |
Hi @mikependon, are you planning to add Join (simple 1:1) support in RepoDb? |
Yes, we will revisit the architecture and will include this in the near future. |
Glad to hear join support is coming! |
Any news regarding 1:1, 1:N, N:M? |
there is no method with join name in SqlConnection Class and BaseRepository and no any join support in SqlServerTableHints Class.
one of the best functionality of orms is support rdbms functionalities in native languages not just in raw sql methods.
please add joins like other perfect methods!
The text was updated successfully, but these errors were encountered: