|
|
Comments and Discussions
|
|
 |
|

|
Thanks. You may want to take a look at version 5.5 just published.
|
|
|
|

|
Bookmarked and +5
|
|
|
|

|
Thanks a lot sir.
Cheers, Moises
|
|
|
|

|
Impressive work!
I am personally not a big fun of dynamic, but this work really shows how powerful it can be.
|
|
|
|

|
Thanks again sir, you're very kind!
|
|
|
|

|
Hi. This library is pretty impressive. But I have one question about AND and OR.
In all where samples, you do one where after the other, but can the Where include the && and || operators?
That is: Instead of
Where(x => x.Name == "Test").Where(x => x.Age < 20)
Can I use:
Where(x => x.Name == "Test" && x.Age < 20)
?
And if I can't do that, how do I wrote something like:
(condition1 OR condition2) AND (condition3 or condition4).
This executes differently than simple doing:
condition1 OR condition2 AND condition3 OR condition4.
(In the first case, if condition4 is true the result will be true only if condition1 or 2 are also true... in the second case, if condition4 is true the result will be true even if all the other conditions are false.
|
|
|
|

|
Hello Paulo, thanks for your nice words.
Chaining of Where() methods is just provided as a convenience. You are completely right if you just prefer to use just one single Where() method - something that, by the way, gives you complete control on the syntax you wish to use.
Chaining was introduced because I had a scenario in which one piece of logic prepared a 'base' command, and another piece is able to modify it afterwards. It was not only handy, it saved me a lot of headaches.
The logic in the Where() method supports two 'virtual extension methods': And() and Or(), that you can use to concatenate expressions. For instance you can write something like:
Where( x => x.Id == "007" ).Where( x => x.Or( x.LastName == "Bond" ) );
resulting in:
Id = '007' OR LastName = 'Bond'
(note: the library does extract the values of the parameters, so the above is just a conceptual example).
Now, I believe it is a handy feature but it is not designed to cope with any possible circumstance, it is only valid when such 'concatenation' is valid for your needs. Otherwise, if you have a convoluted logic with nested ANDs and ORs, then you need to revert to use a single Where() method where inside you can write your complex logic using the standard '&&' and '||' C# operators... and probably a ton of parenthesis !!!
PS: I'm looking at publishing a new version by the end of April. Apart from a number of improvements and simplifications in the overall architecture, I'm testing a couple of new ways to use the 'Maps' or entity-alike feature for POCO objects - the feedback I received so far was that the current version is quite powerful but somehow complex to customize its behavior. Any feedback or ideas will be very welcome!
Thanks. BR, Moisés.
|
|
|
|

|
As all the examples concatenated Wheres I though it was mandatory. But I know that allowing to concatenate wheres is great (and in fact I use that with LINQ... but in many cases I have to build the expressions by hand).
Great work. I am voting 5!
|
|
|
|

|
Appreciate you liked it.
Thanks. Moises.
|
|
|
|

|
Excellent article about a well designed library - Well done
|
|
|
|

|
Thanks. Stay tuned for the next versión - probably after Eastern will be done!
Regards,
Moises
|
|
|
|

|
it's great tool.
but please make a simple sample of databinding in windows application for example binding to a listbox or datagridview , i cant make work that , binding dosent work with dynamicobject.
|
|
|
|

|
Hello, and thanks a lot for your message.
Indeed it helps me to clarify that it was never my intention to build binding support in Kerosene. My reasons are:
- First, I envisioned the records ('
KRecord' instances) to act as mere DTO (Data Transfer Objects) instances, that happens to have an easy and handy members' Access mechanism, so they can be used as well for bulk operations, and/or for data intensive operations - but due their dynamic nature they are not built to support binding in a user interface scenario. I'm not saying it wouldn't be possible, or that I won't build that support in the future, but so far they are what they are.
- Secondly, you have the '
Converter' mechanism that will take any record as returned by the database and convert it to whatever object you will need before the result leaves the enumerator. I know it might involve sometimes a separate class, but as I'm used anyhow to work with different classes, for data operations and for business logic / user interface, I am personally a big fan and I use it extensively for, for instance, convert the 'raw records' into meaninful objects... with binding support.
- Thirdly, you have the '
Maps' mechanism. It has been designed to generate POCO classes, meaning that it doesn't require any, and I mean any support in terms of attributes or interfaces. Another way to look at it is that you can generate whatever business object you need, with or without binding support.
I had an interesting conversation recently with someone that made some complains about the 'Maps' in terms of that they are, in his opinion, somehow complex to deal with and very complex to master them. The best thing is that he suggested some improvements that, along with others from my own, I'm currently implementing. I think I'll be able to ship a beta version before Eastern.
Thanks. Best regards,
Moisés.
|
|
|
|

|
Provide an example of a form with a Grid and some fileds and record navigation please
Nice
|
|
|
|

|
Hello, sorry for the delay answering you, and thanks a lot for your message.
Indeed it helps me to clarify that it was never my intention to build binding support in Kerosene. My reasons are:
- First, I envisioned the records ('
KRecord' instances) to act as mere DTO (Data Transfer Objects) instances, that happens to have an easy and handy members' Access mechanism, so they can be used as well for bulk operations, and/or for data intensive operations - but due their dynamic nature they are not built to support binding in a user interface scenario. I'm not saying it wouldn't be possible, or that I won't build that support in the future, but so far they are what they are.
- Secondly, you have the '
Converter' mechanism that will take any record as returned by the database and convert it to whatever object you will need before the result leaves the enumerator. I know it might involve sometimes a separate class, but as I'm used anyhow to work with different classes, for data operations and for business logic / user interface, I am personally a big fan and I use it extensively for, for instance, convert the 'raw records' into meaninful objects... with binding support.
- Thirdly, you have the '
Maps' mechanism. It has been designed to generate POCO classes, meaning that it doesn't require any, and I mean any support in terms of attributes or interfaces. Another way to look at it is that you can generate whatever business object you need, with or without binding support.
I had an interesting conversation recently with someone that made some complains about the 'Maps' in terms of that they are, in his opinion, somehow complex to deal with and very complex to master them. The best thing is that he suggested some improvements that, along with others from my own, I'm currently implementing. I think I'll be able to ship a beta version before Eastern.
Thanks. Best regards,
Moisés.
|
|
|
|

|
thanks for your consideration
but i have scenario that uses your great tool and transfers the deepobject you have to the UI,
here i need some keyword to how to make such an object to be bindable...
if you dont want to implement it , it's ok but give me some keyword for how to make it bindable.
or how to make a dynamicobject bindable .
thanks u.
|
|
|
|

|
Pardon? The KRecord class is a DynamicObject but not a DeepObject. Do you mind to clarify your question/need a bit more?
|
|
|
|

|
the KRecord is DynamicObject , i need a keyword to how to implement binding support for this type of objects.
i ll be appreciate for helping me for make them bindable.
thanks a lot.
|
|
|
|

|
Hi again,
Binding support for dynamic objects is, in my experience, quite limited. As far as I know there is no an universal mechanism, as the internals are different if your are in a Silverlight scenario, a WPF one, an ASP, and so on. If you add to all of this that the KRecord instances were designed to work either with bulk (data intensive) scenarios or as "carriers"this is why they were not intended to be used with user interfaces.
But, if you really insist/need... my first guess would be, first, to add to the KRecord class support for the INotifyPropertyChanged interface, working with the TryGetMember and TrySetMember methods. If your scenario is not this one, then I have been told you might want to go through implementing the ICustomTypeDescriptor interface... but I have not tried it myself so I can hardly advice here.
A quick question: do you really need to bind KRecord instances? Have you considered instead to convert them to instances of your one of your business domain clases you can easly bind to whatever scenario you need? Or using the Maps mechanism as an alternative for that purpose?
Cheers, Moises
|
|
|
|

|
thanks a lot for your suggestion.
my scenario is based on a truly dynamic data driven application and your tool would be great to achieve this goal.
in my scenario there is not any business object, every thing managed by a dynamic data engine.
thank you Moises Barba , your comment will be very helpful.
|
|
|
|

|
Thanks for coming up with such an orm! I had used nhibernate and it was a hassle to maintain all those configuration. I even had to come up with my own repository helper class.
That said, what's the performance of kerosene? Nhibernate is pretty slow for my case, where we take thousands of records per round trip. Also, does kerosene support lazy loading, nested entities and how does it deal with the n+1 select issue?
This looks like a wonderful orm, I'm going to try it out on a smaller project and see how it works. Hope you can answer my questions above. Thanks!
|
|
|
|

|
Hello, sorry for the delay in answering, but I has been buried for a while...
Very, I mean, very, interesting questions. Let me try to answer them:
- Performance: yeah, I hate those ones for being so slow too. I guess that as they are very complex libraries, they need to support a lot of features and scenarios, and this might be at the root of their light speed (so to speak). If you take a look at
Kerosene's architecture it basically has three phases: a parser, the executor, and an optional filter.
- The parser is the one that parsers your dynamic lambda expressions, extracts the parameters and produces the text of the command to execute. It is invoked once per command, and in any case is very lighter - in my very small test machine (1x 2GHz, 2 GB RAM) I was able to parse 1000 dynamic expressions in 1.77 secs. So I would say its impact is neglictable.
- The executor is where the command gets executed and the records fetched from the database. It is basically a light wrapper around ADO.NET whose impact, based upon my tests, is about 3-5% over pure ADO.NET performance (the complex the records are, the longer it takes).
- The filter phase is optional, and it is implemented by either the '
Converter' mechanism or by the 'Maps' one. Their perfomance depend very much on how complex your transformation logic is, so I cannot adventure any figure here.
- Lazy loading. Well... in the '
KRecord' instances world it has no sense to talk about lazy loading, we are fetching the records as they are returned from the databases. If we use any of the filter mechanisms mentioned aboved is where I have implemented the logic for retrieving related information (as the lines of an invoice, for instance).
- BTW, I'm currently implementing a new and simplified version of the 'Maps' mechanism that does a better job in terms of maintining the "state" of a business entity. The whole explanation would take a full article in itself, but the good news are that I expect to have at least a beta version I can share by after Eastern.
- The same comments apply to your other two questions.
In summary, Kerosene was not built to compete against NHibernate or the others. It was built to provide a completely different approach by simplifying the way you interact with your databases, and by allowing you to do so dynamically and without any external configuration files. Then it evolved to support a "poor's man entity framework" for POCO business objects (which happens to be my favourite approach to address complexity when dealing with big programs) in the form of the 'Maps' mechanism.
I found all of this as a very usefull and powerfull approach,more than suited for my needs. So I decided to make it a library and share it with the community. It might be helpful in some of your projects/scenarios (and I'll be happy) - but, as it happens with any other technology, it might be better not to use it in other ones.
Enjoy. Cheers,
Moisés.
|
|
|
|

|
Odd as it may seems, our company is going thru a web service to talk to an SQL database.
So, we are giving the SQL text as a parameter to a web method and are given back a DataSet as a result.
Is there some extension point I could use to support this scenario?
Thank you very much.
|
|
|
|
|

|
Not quite so unfortunately.
In our case, the web service is not under our control so we cannot have it use the ORM.
|
|
|
|

|
Hi. In your case it might be useful for your scenario to use the parsing capability of dynamic lambda expressions: create a Factory associated with you specific database vendor, instantiate a Parser, and use its Parser() method to return the SQL text along with a Parameters' list.
But, honestly, I'm not sure what would be your gain in such scenario. I'm so sorry.
Regards, Moises
|
|
|
|

|
first i wanted to say that its a very cool and interesting project
i'm trying to implement sqlite support.
just added system.data.sqlite and created KeroseneORM.SQLite.
and there're two troubles:
1st(resolved): there's no InnerConnection in SQLiteConnection so MBTools.TypeHelper.GetElementValue overrided for GetElementValue( object obj, string name, object[] indexes = null, BindingFlags flags = DefaultBindings, bool raise = true )
and some null reference checks in other GetElementValue
2nd: SELECT TOP 1 * FROM in sqlite is SELECT * FROM N LIMIT N so how can i override KCommandQuery class? may be there is a way to implement a visitor pattern to build an sql query? and to send visitor implemented class as a param to some place in KeroseneORM.SQLite project? i think there is more language-specific problems and if there is away to implement an overrided sql builder would be cool. like 'collate nocase' and some usings of custom functions in sql statements like (lower2 - for example custom c# function that binded and runs in sqlite and called from builded sql)
thanks!
best regards. Dmitry.
|
|
|
|

|
Hello... and great! I'd love to see what you have done. Let me know if I can be of any help (apart from your questions below).
1.- I didn't know it, thanks. But I would also love to know how have you solved the need of finding the current transaction (unless you wanted to drop support for IDbTransaction, which could an option!).
2.- Actually Top() is a virtual method... but the problem is that the IKLink's extension methods Query() and From() internally create a new KCommandQuery object, and in the current version there is no such a factory mechanism to instantiate specific query commands for different databases.
Good news is that this is among the things I'm currently working on.
Meanwhile, and I know is not very elegant, you may want to use the scape syntax as a workaround...
Keep in touch. Cheers, Moises
|
|
|
|

|
Hi Moises,
This library rocks!. It is a refreshing change from the status quo of heavy ORMs and I think is part of a trend of returning to the basics when dealing with data. I feel like while Linq to (insert database here) is amazingly simple to use but it can get you into real problems if you don't truly understand how to think in set based SQL. I have seen first hand how some simple looking linq statements can cause your web server to grind to a snails pace once you get some traffic going through it. But anyway, I do have a question about some changes I made to make the library work for me. On the IKCommandEnumerableHelper class there are extension methods for List, First, Last, etc. This class also has the ConvertBy method to project your results to a new object. I was running into issues when I wanted to use the List and ConvertBy methods at the same time because the return from ConvertBy is not a IKCommandEnumerable so chaining did not work. To solve the problem I just overrode the List, First, Last, etc methods allowing a converter function to be passed in and used on the reader. This works fine for me but I can't help but wonder if there is a way to do it better where I could chain the call like this: x.List().ConvertBy() or something like that?
Any guidance will be much appreciated.
Thanks,
Frank
|
|
|
|

|
Hello Frank. Two things: 1) thanks very much for your kind words, very much appreciated; and 2) you are completely right: the scenario you have described is not covered in the current version. Thanks a lot for pointing it out!
Fortunately there are several easy ways to solve it. Overriding the extension methods is one. Another way, if you want to chain the calls, is to write the same First(), Last(), etc, extension methods on the IKEnumerator interface itself.
I have done it for you but with a small change: first thing in the chain is the ConvertBy() method, and afterwards the ToList() one, as it is the one that will produce the list you will end up using in your solution.
So, just add the following code to the IKEnumeratorHelper static class:
public static List<object> ToList( this IKEnumerator reader, bool dispose = true )
{
if( reader == null ) throw new ArgumentNullException( "reader", "IKEnumerator cannot be null." );
List<object> list = new List<object>();
while( reader.MoveNext() ) list.Add( reader.Converter == null ? reader.Current : reader.Converter( reader.CurrentRecord ) );
if( dispose ) reader.Dispose();
return list;
}
public static object[] ToArray( this IKEnumerator reader )
{
List<object> list = reader.ToList();
object[] array = list.ToArray(); list.Clear(); list = null;
return array;
}
public static object First( this IKEnumerator reader, bool dispose = true )
{
if( reader == null ) throw new ArgumentNullException( "reader", "IKEnumerator cannot be null." );
object r = null;
if( reader.MoveNext() ) r = reader.Converter == null ? reader.Current : reader.Converter( reader.CurrentRecord );
if( dispose ) reader.Dispose();
return r;
}
public static object Last( this IKEnumerator reader, bool dispose = true )
{
if( reader == null ) throw new ArgumentNullException( "reader", "IKEnumerator cannot be null." );
object r = null;
while( reader.MoveNext() ) r = reader.Converter == null ? reader.Current : reader.Converter( reader.CurrentRecord );
if( dispose ) reader.Dispose();
return r;
}
public static IEnumerable<object> SkipTake( this IKEnumerator reader, int skip, int take, bool dispose = true )
{
if( reader == null ) throw new ArgumentNullException( "reader", "IKEnumerator cannot be null." );
if( skip < 0 ) throw new ArgumentException( "Skip should be equal or bigger than cero." );
if( take <= 0 ) throw new ArgumentException( "Take should be bigger than cero." );
try {
for( int i = 0; i < skip; i++ ) if( !reader.MoveNext() ) yield break;
for( int i = 0; i < take; i++ ) {
if( reader.MoveNext() ) yield return ( reader.Converter == null ? reader.Current : reader.Converter( reader.CurrentRecord ) );
else yield break;
}
}
finally { if( dispose ) reader.Dispose(); }
}
With it you'll be able to chain ConvertBy() with any of this extension methods as follows:
public class Employee {
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
...
}
public void MyTest() {
IKLink link = ...; var cmd = link.From( x => x.Employees );
var list = cmd.ConvertBy( rec => {
dynamic x = rec; Employee emp = new Employee();
emp.Id = x.Id;
emp.FirstName = x.FirstName;
emp.LastName = x.LastName;
...
return emp;
} )
.ToList();
foreach( var item in list ) Console.WriteLine( "\n> {0}", item );
}
Note that I have included in the new extension methods a default "bool dispose = true" argument in order to automatically dispose the reader created by ConvertBy as soon as it has been used, and so avoiding to maintain in memory references to managed objects.
I will include this code in the next release (5.4) as soon as I'll find some time to publish it. You'll deserve the recognition of having found this scenario.
Hope it helps.
Thanks. Cheers, Moisés.
modified 6 Oct '12 - 3:51.
|
|
|
|

|
Hi Moises,
I have updated my code and it works great! I have to say again how nice this library is! I look forward to the 5.4 release. Have you considered creating Nuget packages for Kerosene? I think this would give the project more exposure to developers.
Thanks,
Frank
|
|
|
|

|
Thanks again and happy you find it useful.
Regarding NuGet... maybe in a near future
Cheers, Moisés.
|
|
|
|
|

|
Hello. I'm afraid I cannot help a lot in this question as I am not using Cristal Reports, and so, I am not familiar with its structure or needs.
But Kerosene is architected to be agnostic: with the appropriate ADO library underneath it can use any SQL-like database as far as you can refer to its tables and columns and connect by virtue of a connection string. So, I don't see any reason that impede you to use Kerosene with it.
I will be interested to know how it goes for you.
Thanks for your message. Best regards, Moisés.
|
|
|
|

|
This looks like a great library! Can't wait to have a deeper read of the code ...
A great tool for those of use who like the general abstraction concepts of an ORM, but don't like the heavyweight and overly complicated way that most of the big ORMs do it (granted, they do have to account for a large number of scenarios and edge cases).
I have built a simple generics-based DAL library myself, and what particularly interests me is your lambda parsing to SQL - I am curious, does this slow down your library considerably? Have you measured this? The way you have done this is really nice, but I imagine the reflection overhead (particularly with rather large queries) can get quite nasty?
|
|
|
|

|
Hello Ryan, and many thanks for your comments.
Actually, your second sentence was able to perfectly summarize, and in a surprisingly small number of words, the objectives and benefits of Kerosene... Have you considered working as a professional journalist? (joking)
Regarding your question: bear in mind that the expressions are parsed only once and then executed, they are not parsed in each iteration of the command, it would have been madness. So in comparison with the time it takes to go to the database, execute the command, and return potentially hundreds or thousands of records... the impact of the parsing of one expression is neglictable in most circumstances.
If you business problem is, otherwise, not extremely database intensive, then yes you may end producing and consuming a higher expressions to execution ratio. I believe it won't ever be a major problem, and this is why I have never done formal performance tests.
If you are interested in my informal ones... well, using my desktop virtualized environment with 1x CPU 2.60 GHz, 2 GB RAM, running Windows 2008 server R2, SQL 2008 server, Visual Studio, and all the development stuff you can imagine:
- Parsing 1000 query commands: 2.5 seconds average.
- Parsing 1000 update commands: 1.77 seconds average.
The reason for the differences, apart of using more complex or easier expressions, is also because the Query commands have a heavier structure, and you pay a small penalty when creating them.
Thanks. Regards, Moisés
|
|
|
|

|
OK awesome, thanks for your reply, you've explained it quite nicely.
I guess you might see a performance hit in a web environment, where you typically have a lot of isolated short-lived database actions across many requests and users, so you'll suffer the overhead every time a lambda-based query is executed.
I would love to see the code that parses the lambdas ... I did have a very quick look around the source but couldn't find what I was looking for. Can you point me in the direction of the source files I should look at?
EDIT: Nevermind, I found KParser and DynamicParser. Nice
So I assume there is a POCO generator in there somewhere? I don't know how Kynetic works, I've never used it before, but I am very keen to check it out, it looks great!
Thanks for sharing
modified 25 Sep '12 - 5:21.
|
|
|
|

|
Sure:
1- Take a look at the DynamicParser class (in the MB.Tools project). It is the core element for the whole parsing mechanism: it is able to take a dynamic lambda expression and transform it into a logic tree regardless what types are involved (well, indeed, it uses dynamics so the mechanism is not dependent on any specific type - cool, uh?).
You may want to take a look at: DynamicParser: How to parse Delegates and Dynamic Lambda Expressions and convert them into Expression Trees[^], because in this article I have provided some more explanations and details.
2- Take a look at the KParser class in the MB.KeroseneORM project. Its first mission is to parse an object given, potentially a dynamic lambda expression, and in this case with the aid of DynamicParser, and its second mission is to translate the tree into the actual SQL syntax understood by the database this specific parser was created for.
It uses a modified version of the Visitors pattern, so that each specific node type is treated by a specific virtual method called recursevely until all terminal nodes are parsed. Along the way, some of those nodes are going to be considered "constants" and then extracted from the string and converted into parameters.
Hope it helps.
Enjoy, Moisés
|
|
|
|

|
You sir, are awesome.
Thank you!
|
|
|
|

|
Well, I have just pointed you to both KParser and DynamicParser...
No, there is not any POCO generator. Indeed the overall idea is that you can use your own POCO classes straight without any, I'm serious, any modifications. Not creating wrappers, or decorating your classes with attributes, and not even needing to write external mapping files.
Obviously magic does not exist. When your application, at run-time, uses a given business object with Kerosene, it will become a managed "entity". This process basically involves attaching dynamically to your instance a package of metadata information - it is the analogous of dynamically extending your classes at run-time, but instance by instance. This package along with an internal temporary cache will do the magic.
I used a similar mechanims to build a solution that provides support for "Extension Properties", the analogous of the "Extension Methods" we are all very happy with... C# Easy Extension Properties[^].
Cheers, Moisés
|
|
|
|

|
OK, wow thats really cool! Does this mean that instead of attributes you're relying on a big chunk of configuration code in App_Start or something similar to map all your objects to tables and columns etc?
|
|
|
|

|
You are partially right.
75% of the time by using reflection Kerosene is able to match the columns in the database with fields or properties in your business class. If this is your scenario you don't have to do anything else.
If, otherwise, there is no such one-2-one match, or if you want to deal explicitly with dependency properties, then yes, you need to customize the map associated with that type. The good news is that it is done by basically injecting a few delegates that will take care only for those border cases, while letting Kerosene to deal with the standard "managed" columns. And yes, I use to create and inject those delegates at start-up time.
Have you taken a look at: Kerosene Maps: A dynamic and configuration-less Entity Framework designed for your POCO objects[^]? It explains how to treat those cases.
|
|
|
|

|
If this is 1/2 as good on further study as it looks at first glance, it's fab!
|
|
|
|

|
Thanks sir. Enjoy it ... and don't forget to take a look at the Maps (Entity Framwork) and WCF versions as well.
- Moisés
|
|
|
|

|
It was great. It's even better now! Thank you so much for sharing this.
|
|
|
|

|
You are very gentile...
Thanks. Moises
|
|
|
|
|

|
Thanks / Gracias.
Moisés.
|
|
|
|

|
You're welcome. / Por nada.
Christian Amado
MCITP | MCTS | MOS | MTA
Olimpia ☆ ★★★
Please mark as answer, if it helps.
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
The fifth incarnation of a dynamic and self-adaptive ORM library with full support for POCO objects, that doesn’t require any mapping or configuration files, and with support for natural SQL-like syntax in plain C#
| Type | Article |
| Licence | CPOL |
| First Posted | 12 Oct 2010 |
| Views | 72,095 |
| Downloads | 1,938 |
| Bookmarked | 143 times |
|
|