在不将整个表加载到内存的情况下检查哪些消息已经存在

时间:2018-08-24 07:58:32

标签: c# entity-framework entity-framework-core

我想在将任何消息添加到数据库之前检查是否已经存在任何消息,但是我当前的查询将整个表加载到内存中。从我的代码生成的查询基本上只是select * from tableName

如何重写此查询以在数据库中进行评估?

public void AddMessages(IEnumerable<Message> messages)
{
    if (messages == null)
        throw new ArgumentNullException(nameof(messages));

    var duplicates = (from currMsg in context.Messages
                      where messages.Any(msg =>
                                                msg.Prop1 == currMsg.Prop1 &&
                                                msg.Prop2 == currMsg.Prop2 &&
                                                msg.Prop3 == currMsg.Prop3)
                      select currMsg);

    var messagesWithoutDuplicates = messages.Except(duplicates);

    context.Messages.AddRange(messagesWithoutDuplicates);
    context.SaveChanges();
}

我也可以循环运行它,但随后我将创建许多db调用,而不是1个,而我希望在单个调用中执行此操作。

3 个答案:

答案 0 :(得分:0)

如果要检查表中有多少行,可以使用SELECT COUNT(*)FROM TABLE。 在执行任务之前执行此查询。

,或者如果您希望更新是否无法插入行(重复) 您需要为此使用merge-insert。

合并插入(MySql)=> https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html

合并插入(Oracle)=> https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm#SQLRF01606

答案 1 :(得分:0)

根据您的用例,您可能需要一个一个地插入它们,并信任数据库的唯一索引(您有一个,对不对?),如果它是重复的,则将它丢回您的脸。 / p>

代码之外,还有两个弱点:并发性(如果在检查重复项时有人插入的话)以及要插入的记录本身可能是重复项,而不是重复项检查。

答案 2 :(得分:0)

由于没有一个简单的方法可以完成此操作,因此我决定牺牲性能并保留可读性和可测试性。这是我的解决方案:

using (var transaction = context.Database.BeginTransaction())
{
    try
    {
        foreach (var message in messages)
        {
            var exists = context.Messages.Any(msg => msg.Prop1 == message.Prop1 &&
                                                     msg.Prop2 == message.Prop2 &&
                                                     msg.Prop3 == message.Prop3 &&);

            if (!exists)
            {
                context.Messages.Add(message);
            }
        }

        context.SaveChanges();
        transaction.Commit();
    }
    catch (Exception ex)
    {
        _logger.Error(ex);
        transaction.Rollback();
        throw;
    }
}