Open NickAb opened 8 years ago
@NickAb I'm not sure Merge is even applicable to ORMs. Can you write a little snippet showing how you would have use it?
There isn't anything at the moment, though we could potentially do something nice (but not for 1.0.0).
@gdoron Actually, I am only interested in merge-as-upsert analog (upsert as in mongo upsert), not full-blown MERGE
syntax, so it might look like:
var newFeatureForRegionSetting = new FeatureForRegionSetting();
entity.Region = region;
entity.Feature = feature;
entity.Settings = settings;
dbCtx. FeatureForRegionSettings.Upsert(x => x.region == entity.region && x.feature == entity.feature, newFeatureForRegionSetting);
which will insert newFeatureForRegionSetting
if condition is not matched, or update entity if condition is matched.
I can not provide usage example for full-blown merge, as I am not that familiar with other MERGE
uses.
No, I don't think so. As I understand, the issue you are referring to is about "merging disconnected graphs", so it is about interworking of EF change tracking, etc. What I am referring to is SQL MERGE statement, see docs here https://msdn.microsoft.com/en-us/library/bb510625.aspx
Ah, I've done this with a home brewed extension method but it needs to get the entity before doing the upsert. Needed to define the key value and didn't support multiple keys. Would be useful to have since it would be more performant.
There's a pattern described https://msdn.microsoft.com/en-us/data/jj592676.aspx Which is essentially what I wrapped in the extension, would be much better if the framework did this instead. Especially if it can be made to determine if the keys are default or not - without necessarily loading the entity from the data store.
Perhaps it could leverage the MERGE statement as you alluded to. Match on entity keys. I could see folks wanting the results of the MERGE returned which would complicate the operation. The simple case would be useful enough to warrant only Insert or Update with no additional complexity. Also, depends on whether or not the data store supports MERGE if its to be used to implement the feature.
There is a very handy document in PostgreSQL wiki about UPSERT in various SQL dialects. It may come handy if someone is trying to implement it in the EF.
(In practice it seems there is no agreed way how it should work, especially with unique fields, and would be a messy thing to do in EF because of it)
This is an implementation of MERGE to MSSQL than can give ideas. It is an extension method to EF6.
I know there are lots of things it would be nice to support, and limited developer time, but it's a shame this one is missing. Apart from this, my code is entirely database-independent, but it's hard to implement upsert without database support.
The usual pattern is that you try to insert, and whether it succeeds or fails, you know the relevant row is in the database. You can therefore pull it out and work with it. There are problems with this approach, though:
Just pinging this to make sure it's not totally forgotten. With the seemingly wide support of UPSERT/MERGE across databases, this could be a pretty valuable feature (although of course the specific implementations/variations would have to be investigated (see this comparative doc cited above by @Ciantic)
@roji Thanks--there's definitely a lot of value in doing this. the comparative doc is very useful--we would have to figure out what to do for SQL Server.
@ajcvickers https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql it seems to be "upsert"
I liked the idea of an Upsert command for EF Core, so I thought to make a simple extension that could be used in some of the more simple scenarios.
Considering the example above, it could be expressed like this:
dbCtx.Upsert(new FeatureForRegionSetting
{
Region = region,
Feature = feature,
Settings = settings
})
.On(x => new { x.Region, x.Feature })
.RunAsync();
But it can also handle more interesting scenarios:
DataContext.Upsert(new DailyVisits
{
UserID = userID,
Date = DateTime.UtcNow.Date,
Visits = 1,
})
.On(v => new { v.UserID, v.Date })
.UpdateColumns(v => new DailyVisits
{
Visits = v.Visits + 1,
})
.RunAsync();
I've posted the project here: https://github.com/artiomchi/FlexLabs.Upsert
I also described a bit more about it in a blog post. It's a simple extension, and can't be directly merged into EF, but I think the syntax is pretty good, and it might be useful enough for some people :)
I have situation where Update very needed too. I ask this situation on StackOverflow, link here.
Apparently, the update method in Entity functions as an Add or Update by default. https://docs.microsoft.com/en-us/ef/core/saving/disconnected-entities#saving-single-entities
So this might be able to be closed now?
@Nonary Add/Update <> Upsert
@vovikdrg Care to define what's different? https://en.wiktionary.org/wiki/upsert defines it as a way to update or insert in databases.
@Nonary I did few comments before. Link you provided has nothing to do with technical stuff if you are asking about words definition and differences probably its wrong thread. If you really care about technical differences of upsert in sql world please check here https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-2017
@vovikdrg are you saying that EF doesn't generate the database-specific upsert command? That is important because as I said above, trying to emulate upsert with insert and update tends to lead to odd effects.
@PeteX EF will generate only update or insert commands which are different from upsert. Also what do you mean about emulate? Upsert is supported almost in all engines.At some point due to complexity maybe it make sense even to be as extension EF.Core.Extension.SqlServer, EF.Core.Extension.MySql
You don't need to emulate upsert, I was just pointing out that it doesn't work very well if you try!
@Nonary in the MS docs you linked to, it never tells you that it does an upsert:
The Update method normally marks the entity for update, not insert. However, if the entity has a auto-generated key, and no key value has been set, then the entity is instead automatically marked for insert.
So, it'll either generate an INSERT
or an UPDATE
sql statement, depending on the state of the entity passed to the Update()
method.
@Nonary @vovikdrg @PeteX
At this point in time, there is no native Upsert functionality in Microsoft's EF Core package. If you need upsert support in EF Core, you can either manually construct the correct SQL statement for the DB engine you're using, use an SP.
Another alternative is use the FlexLabs.EntityFrameworkCore.Upsert package that I created. :smile: At the moment, it will generate the SQL statement for SQL Server, MySQL, Postgres and Sqlite, and run it immediately.
I'm continuously working on improving it, and am planning to extend it to use the EF's object state in the future, but even without that - it works quite well, and has been tested in several projects currently in production.
@PeteX any examples or arguments when it is not working? I did use it many times work like a charm. Maybe my cases were simple or I was doing something "wrong"
@vovikdrg when what didn't work sorry? I'm not sure if you're talking about EF now, EF as it was when I wrote my first comment, or emulating upsert with INSERT and UPDATE.
In general the problems are races that occur when access to data is contended, so most of the time things will work correctly.
@ajcvickers Any update about this feature please?
@TanvirArjel This issue is in the Backlog milestone. This means that it is not going to happen for the 3.0 release. We will re-assess the backlog following the 3.0 release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.
@TanvirArjel check this https://github.com/aspnet/EntityFrameworkCore/issues/4526#issuecomment-366818031
Just a small update, tried today to implement repository InsertOrUpdate method, and it is real pain now.
Referencing the new issue that basically describes the pain you have when you need to insert or update:
@yahorsi have you tried the library from https://github.com/dotnet/efcore/issues/4526#issuecomment-466083476?
@yahorsi have you tried the library from #4526 (comment)?
There are definitely libraries that add important but unfortunately missing in the EF things (Bulk, Merge) but IMHO, this should be implemented out of the box in the EF itself.
I'm continuously working on improving it, and am planning to extend it to use the EF's object state in the future, but even without that - it works quite well, and has been tested in several projects currently in production.
@artiomchi Can you elaborate on a timeline for that?
A vote for this feature. Could this be implemented in core EF via an additional (preview) NuGet package (Microsoft.EntityFrameworkCore.Upsert) providing functionality for each supported database incrementally if need be. For example, bring support for most popular / most value add databases e.g. SQL Server, PostgreSQL and SQLite.
@nilmas there are several community packages that provide this functionality already
@ErikEJ Thanks for replying. I echo the sentiment others have voiced here and elsewhere for upsert to be a good candidate for a core feature of EF. Personally would prefer Microsoft support this core feature rather than a third party.
@ErikEJ not very effectively. Typically you would want to queue up several changes then call 'db.SaveChanges()', but the change tracking doesn't handle upserts
For those who is still waiting. Install linq2db.EntityFrameworkCore and follow this documentation link for Merge Other Insert, Upsert operations are also supported.
I opened a similar issue, providing (hopefully) specific scenario where this is crucial. https://github.com/dotnet/efcore/issues/23044
Beware of many common implementations for this. I haven't looked into SQL Server's MERGE
, but I do know that MySQL's ON DUPLICATE KEY UPDATE
honors any key, not just the primary key.
In the vast majority of cases (but not all of them), the intent is to perform an update if the primary key exists, not if any unique key exists. Imagine accidentally using an existing name, and instead of getting a rejection, overwriting a completely unrelated record that happened to have the same name - without knowing you did! The original record will have mysteriously vanished, and everything that pointed to it now points to the new record. The horror...
The takeway is this: There should be a clear distinction between determining existence by the primary key
vs. by any primary or unique key
. Both have a place, although the former is a more common requirement, and the latter seems to be a more commonly supported feature.
Postgres gets this right by the way. You choose the columns where conflicts are to trigger updates. It's pretty bad that MySQL triggers updates for any conflict, I didn't know that. If you have a table of users for example, and the email addresses are constrained to be unique, you could trigger an update just because someone registered with an existing email address? Ouch.
@PeteX Kudos to PostgreSQL for getting this right!
When I first encountered the issue, I was surprised to find nobody mentioning it anywhere. Ubiquitous as it is, it seems to be extremely unknown.
I've raised a PR to add this at #24956
@dan-giddins note that this issue is about supporting database upsert (in SQL), and not to the EF Core change tracker.
@roji I see. So my PR which uses the existing Add and Update architecture (which presumably maps to INSERT and UPDATE at the database level) does not technically solve this, as it does not do a direct upsert, however it should provide the requested functionality.
Note "AddOrIgnore" in #16949 which is closely related to this. See also notes in https://github.com/dotnet/efcore/issues/16949#issuecomment-911346041 about non-tracking inserts and conflict actions.
Postgres gets this right by the way.
As does SQLite, which bases its upsert syntax and semantics on Postgres.
Is this the reason this has been neglected for so long? Varying behavior? I know SQL Server doesn't have anything other than maybe merge, if that counts.
This is such a needed feature. We would appreciate an update on when and if they maybe supported.
@ChiefInnovator This issue is in the Backlog milestone. This means that it is not planned for the next release (EF Core 7.0). We will re-assess the backlog following the this release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources. Make sure to vote (👍) for this issue if it is important to you.
Note that SQL Server MERGE is apparently not atomic - see here for various resources discussing this, especially this and this.
tl;dr without HOLDLOCK, MERGE is vulnerable to concurrency issues, so if two upserts are raising, one of them may fail with an error (since the row didn't exist when it checked, but by the time it attempts to insert it, it did). With HOLDLOCK, deadlocks may occur.
This doesn't in itself mean that MERGE can't be use to support UPSERT, but think about exactly what we want to do here.
if two upserts are raising, one of them may fail with an error (since the row didn't exist when it checked, but by the time it attempts to insert it, it did).
Yep, I'm really resigned to key conflict exceptions causing by this problem when I use MERGE(with Linq2db) in our production.
but think about exactly what we want to do here
I want atomic
to be a must. It should be designed for concurrent environment. But I'm not sure whether we can implement it on SQLServer.
In my project I need to insert entity if it does not exist yet, or update it otherwise (somewhat like
UPSERT
in mongodb). As I understand in SQL it can be accomplished usingMERGE
, but I was not able to findMERGE
in EF Core.