在EF6上发出查询之前始终检查DbSet.Local是安全的

时间:2017-04-16 21:20:47

标签: c# entity-framework-6

我正在阅读有关使用Entity Framework 6的缓存和性能的信息,并且在向数据库发出任何查询之前我有始终检查DbSet.Local集合的这种意思,如果目的是查询是加载单个实体信息。所以这就是我提出的模式:

pubilc TAccount LoadByUsername<TAccount>(string username)
{
    TAccount acc = null;

    // Check cache.
    acc = DbEntitySet.Local.FirstOrDefault(a => a.Username.Equals(username, StringComparison.CurrentCultureIgnoreCase));

    if (acc == null) {

        // Then try and load from db.
        return await DbEntitySet.FirstOrDefaultAsync(u => u.Username.Equals(username)).WithCurrentCulture();
    }

    return acc;
}

考虑到避免db往返+数据库处理的好处,我认为检查Local的开销是可以忽略的。

话虽如此,我继续阅读Identity Framework源代码以检查它们是否做了类似的事情,我发现他们总是发出另一个查询,即使数据的可能性很高已经在记忆中,这意味着:

FindById(accId);
FindByUsername(username);
FindByUsername(username);
FindByUsername(username);

导致4次往返数据包,即使username参数是FindById加载的帐户的用户名。

这是我的问题:我在这里做点什么,或者这种模式是不安全的?我不确定我是否应该使用它,考虑到身份并没有使用类似的东西,而且作者显然比我有更多的知识。 DbSet.Local这样安全吗?

- 更新

我刚发现一段代码破解了作为参数传递的LINQ查询,以检查它是否包含select by id,这样他们就可以在进入db之前检查实体的缓存(通过调用DbSet.Find )。所以,我想我可以做些什么,只是无法理解为什么他们没有将这个想法扩展到诸如&#34; FindByEmail&#34;等方法。

1 个答案:

答案 0 :(得分:0)

非常有趣的问题,我在这方面遇到了一些问题。

您发布的示例看起来很安全。您正在寻找具有已知键值的单个实体。不过,我认为你发现它在不同情况下并不安全。

例如,我已经继承了存储库首先使用本地(缓存)结果的一些代码,然后如果没有结果则返回向数据库发出查询:

public class Run extends Application {

        @Override
        public void start(final Stage primaryStage) {

            Button button = new Button();
            button.setText("Navigation");
            button.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {

                    Label secondLabel = new Label("Select from list");

                    StackPane secondaryLayout = new StackPane();
                    secondaryLayout.getChildren().add(secondLabel);

                    Scene secondScene = new Scene(secondaryLayout, 500, 400);

                    // New window (Stage)
                    Stage newWindow = new Stage();
                    newWindow.setTitle("Adding products");
                    newWindow.setScene(secondScene);

                    // Specifies the modality for new window.
                    newWindow.initModality(Modality.WINDOW_MODAL);

                    // Specifies the owner Window (parent) for new window
                    newWindow.initOwner(primaryStage);

                    // Set position of second window, related to primary window.
                    newWindow.setX(primaryStage.getX() + 200);
                    newWindow.setY(primaryStage.getY() + 100);

                    newWindow.show();
                }
            });

            Button button2 = new Button();
            button2.setText("X");
            button2.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {

                    Label secondLabel = new Label("Select from list");

                    StackPane secondaryLayout = new StackPane();
                    secondaryLayout.getChildren().add(secondLabel);

                    Scene secondScene = new Scene(secondaryLayout, 500, 400);

                    // New window (Stage)
                    Stage newWindow = new Stage();
                    newWindow.setTitle("Adding products");
                    newWindow.setScene(secondScene);

                    // Specifies the modality for new window.
                    newWindow.initModality(Modality.WINDOW_MODAL);

                    // Specifies the owner Window (parent) for new window
                    newWindow.initOwner(primaryStage);

                    // Set position of second window, related to primary window.
                    newWindow.setX(primaryStage.getX() + 200);
                    newWindow.setY(primaryStage.getY() + 100);

                    newWindow.show();
                }
            });

            StackPane root = new StackPane();
            root.getChildren().add(button);
            root.getChildren().add(button2);
            Scene scene = new Scene(root, 650, 450);
            primaryStage.setTitle("Shopping Buddy");
            primaryStage.setScene(scene);
            primaryStage.show();
        }

        public static void main(String[] args) {
            launch(args);
        }

    }

这在大多数情况下都很棒,但是在向LoadAll发出两个重叠结果请求时遇到了问题。例如加载带有图像1和2的图像列表,然后加载图像2和3中的图像列表。使用本地缓存完成两个查询,但第二个查询错误地仅包括图像2.图像3不在还有本地缓存​​。

我需要重新调整调用代码以将所有图像预加载到缓存中,或者我需要从任何返回列表的方法中删除本地缓存的结果。

当缓存的查询结果不够好并且发出非缓存查询时,我无法找到明显的方法。

MS发布了一篇文章,其中本地结果直接绑定到UI(https://msdn.microsoft.com/en-us/library/jj592872(v=vs.113).aspx)。这非常适合您希望能够进行更改并一次性提交所有内容的实例。不太确定它是否超越了这一点。