Linq bulk operations

105 views
Skip to first unread message

Gunnar Liljas

unread,
Dec 18, 2014, 6:21:06 PM12/18/14
to nhibernate-development
So, now I have completed and old abandoned NH project, to create a Linq implementation of the bulk functionality. It's complete in the sense that all the unit tests from the HQL implementation passes with the Linq implementation as well, plus a couple more. Before I clean things up, I'd like you input on the syntax.

Currently it works like this:

Delete

session.Query<Animal>()
    .Where(x=>x.Age>3)
    .DeleteAll();


//all methods returns int (affected rows)

Update

session.Query<Animal>()
   .Where(x=>x.Age>3)
   .UpdateAll(x=>x
      .Set(y=>y.Name,"Peter")
      .Set(y=>y.Age,y=>y.Age+1)
    )


Insert (i.e INSERT SELECT)

session.Query<Animal>()
   .Where(x=>x.Age>3)
   .InsertInto(x=>new Human{
      Name=x.Name,
      Age=x.Name+10
    })


The Delete method was first implemented as session.Delete<Animal>(x=>x.Age>3), but I added the IQueryable<T>.DeleteAll extension for consistency.

I welcome any ideas on how this can be improved/changed.

/G

Ricardo Peres

unread,
Dec 18, 2014, 7:04:15 PM12/18/14
to nhibernate-...@googlegroups.com
Very nice! I implemented strongly typed delete in NH-3488, but the syntax was a bit different:

session.Delete<Product>(x => x.Price > 100);

Do you have a pull request waiting to be issued?

RP

Alexander Zaytsev

unread,
Dec 18, 2014, 7:13:57 PM12/18/14
to nhibernate-...@googlegroups.com
Very nice. I would like to remove word "All" from the operations.Also why do we need "Set" in update statement?

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-develo...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Gunnar Liljas

unread,
Dec 18, 2014, 7:19:15 PM12/18/14
to nhibernate-development
Yes, the pull request is coming this weekend. Everything works (I think), but it needs a bit of tidying up.

I can remove the "All"

Why we need the "Set"? Well, some kind of tuple containing the target property and the source expression is needed. Or do you have a different idea?

/G

Craig van Nieuwkerk

unread,
Dec 18, 2014, 7:21:10 PM12/18/14
to nhibernate-...@googlegroups.com
Looks nice. But yes, I think if you can have anonymous object for the parameters this would be better. I have used FluentMigrator and it does this for Update/Insert pretty nicely.

Gunnar Liljas

unread,
Dec 18, 2014, 7:28:43 PM12/18/14
to nhibernate-development
Absolutely possible, but the (big) difference is that

1. An anonymous object will lose the static type relationship with what you're updating/inserting, which kind of defeats the point of Linq bulk operations.
2. You will lose the possibility to express anything but simple value expressions, i.e "SET FullName = FirstName + ' ' + LastName" ( Set(x=>x.FullName,x=>x.FirstName + " " + x.LastName), will not be possible.

/G

Alexander Zaytsev

unread,
Dec 18, 2014, 7:54:52 PM12/18/14
to nhibernate-...@googlegroups.com
What about this:

session.Query<Animal>()
   .Where(x=>x.Age>3)
   .Update(x => new Animal {
y.Name = "Peter",
y.Age = y.Age+1,
});

?

Gunnar Liljas

unread,
Dec 18, 2014, 7:55:34 PM12/18/14
to nhibernate-development
...and while the naming is certainly debatable, I can't think of another way to do it, except possibly:

session.Query<Animal>()
   .Where(x=>x.Age>3)
   .Set(y=>y.Name,"Peter")
   .Set(y=>y.Age,y=>y.Age+1)
   .Execute()

The insert is a bit tricky too, since...


session.Query<Animal>()
   .Where(x=>x.Age>3)
   .InsertInto(x=>new Human{
      Name=x.Name,
      Age=x.Name+10
    })

..requires that Human can be constructed and initialized like that. It is currently possible to use it with a parameterized constructor, but the parameters are completely ignored, which may be confusing.

/G

Gunnar Liljas

unread,
Dec 18, 2014, 8:02:52 PM12/18/14
to nhibernate-development
Yes...

session.Query<Animal>()
   .Where(x=>x.Age>3)
   .Update(x => new Animal {
Name = "Peter",
Age = x.Age+1,
});

would work! Didn't think of that.

I'm not completely sold on the "new Animal" part in an update. It looks linguistically wrong. But it's certainly less verbose. The same problems as for the Insert also applies.

/G

Patrick Earl

unread,
Dec 19, 2014, 11:46:09 AM12/19/14
to nhibernate-development

In our shop we use interfaces for entities exclusively since we have a system that allows for complex inheritance. It would be sad to not have support for interfaces or limited constructor scenarios.

What's at the heart the statement that executes? If it's just a matter of setting individual columns perhaps the syntax should reflect that more than pretending like your C# constructor code is going to be called. Doing it on individual properties allows for interfaces, doesn't incorrectly imply that C# code is going to run on insert, and doesn't require the availability of an empty public constructor on a concrete type.

     Patrick Earl

Gunnar Liljas

unread,
Jan 1, 2015, 11:17:20 AM1/1/15
to nhibernate-development

Thierry Lach

unread,
Jan 8, 2015, 11:07:39 AM1/8/15
to nhibernate-...@googlegroups.com
Sorry about coming late to the party, but if you use


session.Query<Animal>()
   .Where(x=>x.Age>3)
   .Update(x => new Animal {
Name = "Peter",
Age = x.Age+1,
});

how will you be able to tell when a field needs to be set to null, as the default of an uninitialized field would also be null?

I also don't see how you could recognize that a non-nullable numeric field was _not_ being updated, as in:


session.Query<Animal>()
   .Where(x=>x.Age>3)
   .Update(x => new Animal {
Name = "Peter",
});



---
Facts do not cease to exist because they are ignored.

Aldous Huxley

Gunnar Liljas

unread,
Jan 8, 2015, 11:22:53 AM1/8/15
to nhibernate-development
Hi Thierry,

That's not a problem. Only the properties/columns included in the member initialization will be included in the update query, i.e 

session.Query<Animal>()
   .Where(x=>x.Age>3)
   .Update(x => new Animal {
Name = "Peter",
Age = x.Age+1,
});

will result in 

update Animal set Name='Peter',Age=Age+1

Jeffrey Becker

unread,
Jan 8, 2015, 11:35:47 AM1/8/15
to nhibernate-...@googlegroups.com
From an under-the-hood perspective, this works because the Update function probably looks something like:
    int Update<T>(this IQueryable<T> query, Expression<Func<T,T>> updateExpression);

Notice that the 2nd param there is an instance of an Expression class, not a delegate.  Because Expressions are a data structure not executable code its possible to walk over them and use that inspection to generate the update statement.  The update function works because only the members we want to update will be emitted in the expression tree. 
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
---
Facts do not cease to exist because they are ignored.

Aldous Huxley

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

Gunnar Liljas

unread,
Jan 8, 2015, 11:50:32 AM1/8/15
to nhibernate-development

Thierry Lach

unread,
Jan 9, 2015, 12:01:56 PM1/9/15
to nhibernate-...@googlegroups.com
Ah.  So if I understand the responses, Linq doesn't actually create an instance of the Animal class at that point, but something "linq-ish" that _could_ be used later to instantiate the Animal class if needed, or could be interpreted differently (as in this scenario).  That makes sense.  Thanks to all.

On Thu, Jan 8, 2015 at 11:50 AM, Gunnar Liljas <gunnar...@gmail.com> wrote:

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-develo...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages