简体   繁体   English

EF4.1 DBContext:在没有身份PK的一个Save()函数中插入/更新

[英]EF4.1 DBContext: Insert/update in one Save() function without identity PK

I have a streets table, which has a combo of two string columns acting as the PK, being postalcode and streetcode. 我有一个街道表,它有两个作为PK的字符串列的组合,是邮政编码和街道代码。

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. 使用EF4.1和DBContext,我想编写一个“保存”方法,而不是一个街道(进入未连接状态),检查它是否已存在于数据库中。 If it does, it issues an UPDATE, and if it doesn't, it issues an INSERT. 如果是,则发出UPDATE,如果没有,则发出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 ? 尝试插入街道(州=已添加)并捕获任何违反PK的情况,在性能方面不是更好吗? In the catch block, I can then change the state to modified and call SaveChanges() again. 在catch块中,我可以将状态更改为modified并再次调用SaveChanges()。 Or would that not be a good practice ? 或者那不是一个好习惯吗?

Any suggestions ? 有什么建议么 ?

Thanks 谢谢

Select all the streets then make a for each loop that compares and change states. 选择所有街道,然后为每个比较和更改状态的循环创建一个。 After the loop is done call saveChanges. 循环完成后,调用saveChanges。 This way you only make a few calls to the db instead of several thousends 这样你只需要调用db而不是几个thousends

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) ps:将方法重命名为Import(),因为我发现一个更合适的名称,用于从外部源(如我的案例中的文本文件)导入实体(批量)

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. ps2:我知道捕捉异常并不是最好的做法是让它在没有做任何事情的情况下默默地死去,但在我的特殊情况下我没有必要对它做任何事情。 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);
        }
} 

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

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. 无论如何,它仍然不是高度并发系统中的可靠解决方案,因为其他线程可以在您检查和后续插入之间插入相同的街道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM