EF4.1 DBContext:在一个 Save() 函数中插入/更新,无需身份 PK

发布于 2024-12-05 08:21:35 字数 1093 浏览 0 评论 0原文

我有一个街道表,其中有两个字符串列的组合作为 PK,即邮政编码和街道代码。

使用 EF4.1 和 DBContext,我想编写一个“保存”方法,而不是占用一条街道(以未附加状态进入),检查它是否已存在于数据库中。如果是,则发出 UPDATE,如果不是,则发出 INSERT。

仅供参考,保存这些街道的应用程序正在从文本文件中读取它们并保存它们(该文件中有数万条这样的“街道线”)。

我现在想到的是:

    public void Save(Street street)
    {
        var existingStreet = (
                                from s in streetContext.Streets
                                where s.PostalCode.Equals(street.PostalCode)
                                && s.StreetCode.Equals(street.StreetCode)
                                select s
                             ).FirstOrDefault();

        if (existingStreet != null)
            this.streetContext.Entry(street).State = System.Data.EntityState.Modified;
        else
            this.streetContext.Entry(street).State = System.Data.EntityState.Added;

        this.streetContext.SaveChanges();
    }

这是好的做法吗?这里的表现怎么样?因为对于每条街道,它首先会往返数据库以查看它是否存在。

尝试插入街道(状态 = 添加)并捕获任何 PK 违规,这不是更好的性能吗?在 catch 块中,我可以将状态更改为已修改并再次调用 SaveChanges()。或者这不是一个好的做法?

有什么建议吗?

谢谢

I have a streets table, which has a combo of two string columns acting as the PK, being postalcode and streetcode.

With EF4.1 and DBContext, I'd like to write a single "Save" method than takes a street (coming in in an unattached state), checks if it already exists in the database. If it does, it issues an UPDATE, and if it doesn't, it issues an INSERT.

FYI, the application that saves these streets, is reading them from a textfile and saves them (there a few tens of thousands of these "streetlines" in that file).

What I've come up with for now is:

    public void Save(Street street)
    {
        var existingStreet = (
                                from s in streetContext.Streets
                                where s.PostalCode.Equals(street.PostalCode)
                                && s.StreetCode.Equals(street.StreetCode)
                                select s
                             ).FirstOrDefault();

        if (existingStreet != null)
            this.streetContext.Entry(street).State = System.Data.EntityState.Modified;
        else
            this.streetContext.Entry(street).State = System.Data.EntityState.Added;

        this.streetContext.SaveChanges();
    }

Is this good practice ? How about performance here ? Cause for every street it first does a roundtrip to the db to see if it exists.

Wouldn't it be better performance-wise to try to insert the street (state = added), and catch any PK violations ? In the catch block, I can then change the state to modified and call SaveChanges() again. Or would that not be a good practice ?

Any suggestions ?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

盛夏已如深秋| 2024-12-12 08:21:35

选择所有街道,然后为每个循环创建一个比较和更改状态的循环。循环完成后调用 saveChanges。这样您只需对数据库进行几次调用,而不是数千次

Select all the streets then make a for each loop that compares and change states. After the loop is done call saveChanges. This way you only make a few calls to the db instead of several thousends

小耗子 2024-12-12 08:21:35

感谢您的回复,但没有一个真正令人满意,所以我做了更多研究并重写了这样的方法,这满足了我的需求。

ps:将方法重命名为 Import() 因为我发现对于用于(批量)从外部源(如我的例子中的文本文件)导入实体的方法来说,有一个更合适的名称

ps2:我知道这不是真正的最佳实践捕获异常并通过不对其执行任何操作来让它静静地死去,但在我的特定情况下,我不需要对其执行任何操作。它只是作为一种查明该行已存在于数据库中的方法。

public void Import(Street street)
{
        try
        {
            this.streetContext.Entry(street).State = System.Data.EntityState.Added;

            this.streetContext.SaveChanges();
        }
        catch (System.Data.Entity.Infrastructure.DbUpdateException dbUex)
        {
            this.streetContext.Entry(street).State = System.Data.EntityState.Modified;

            this.streetContext.SaveChanges();
        }
        finally
        {
            ((IObjectContextAdapter)this.streetContext).ObjectContext.Detach(street);
        }
} 

Thanks for the replies but none were really satisfying, so I did some more research and rewrote the method like this, which satisfies my need.

ps: renamed the method to Import() because I find that a more appropriate name for a method that is used for (bulk) importing entities from an outside source (like a textfile in my case)

ps2: I know it's not really best practice to catch an exception and let it die silently by not doing anything with it, but I don't have the need to do anything with it in my particular case. It just serves as a method to find out that the row already exists in the database.

public void Import(Street street)
{
        try
        {
            this.streetContext.Entry(street).State = System.Data.EntityState.Added;

            this.streetContext.SaveChanges();
        }
        catch (System.Data.Entity.Infrastructure.DbUpdateException dbUex)
        {
            this.streetContext.Entry(street).State = System.Data.EntityState.Modified;

            this.streetContext.SaveChanges();
        }
        finally
        {
            ((IObjectContextAdapter)this.streetContext).ObjectContext.Detach(street);
        }
} 
哎呦我呸! 2024-12-12 08:21:35

如果 street 已存在于数据库中,您的代码必须导致异常,因为您将从上下文加载它,之后您将尝试将具有相同主键的另一个实例附加到同一上下文实例。

如果您确实必须这样做,请改用此代码:

public void Save(Street street)
{
    string postalCode = street.PostalCode;
    string streetCode = steet.StreetCode;
    bool existingStreet = streetContext.Streets.Any(s =>
                            s.PostalCode == postalCode
                            && s.StreetCode = steetCode);

    if (existingStreet)
        streetContext.Entry(street).State = System.Data.EntityState.Modified;
    else
        streetContext.Entry(street).State = System.Data.EntityState.Added;

    streetContext.SaveChanges();
}

无论如何,在高并发系统中它仍然不是可靠的解决方案,因为其他线程可以在您检查和后续插入之间插入同一条街道。

Your code must result in exception if street already exists in the database because you will load it from the context and after that you will try to attach another instance with the same primary key to the same context instance.

If you really have to do this use this code instead:

public void Save(Street street)
{
    string postalCode = street.PostalCode;
    string streetCode = steet.StreetCode;
    bool existingStreet = streetContext.Streets.Any(s =>
                            s.PostalCode == postalCode
                            && s.StreetCode = steetCode);

    if (existingStreet)
        streetContext.Entry(street).State = System.Data.EntityState.Modified;
    else
        streetContext.Entry(street).State = System.Data.EntityState.Added;

    streetContext.SaveChanges();
}

Anyway it is still not reliable solution in highly concurrent system because other thread can insert the same street between you check and subsequent insert.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文