如何防止多次进行BreezeJS MetaData调用

时间:2016-12-26 22:15:13

标签: breeze

我在Angular2中使用BreezeJS,并注意到多次调用MetaData调用。

我试图通过在我的BreezeDataService的构造函数上调用fetchMetadata()来填充缓存,但是这没有区别(它添加了另一个元数据调用)。

this.entityManager.fetchMetadata().then(function () { console.log('entityManager.fetchMetadata finished'); });

我认为它是由主页加载时发出的5个数据请求引起的,每个请求都会在不等待现有元数据调用完成的情况下获取元数据。

您可以使用NetWorks工具在www.quemesa.com上看到问题:enter image description here(前5个是PReflight CORS请求)

如何告诉代码等待任何现有元数据调用完成而不是再次获取它?

使用代码更新:

@Injectable()
export class DataBreezeService {
    public isSaving: boolean = false;
    public entityManager: EntityManager;

    constructor(private spinnerService: SpinnerService, private loggerService: LoggerService) {  
        this.entityManager = new EntityManager(environment.webApiServiceUrl);
        this.entityManager.metadataStore.namingConvention = NamingConvention.camelCase;

        this.entityManager.fetchMetadata().then(() => this.loggerService.info('entityManager.fetchMetadata finished'));
    }

    public executeQueryArray(query: EntityQuery) {
        return this.entityManager.executeQuery(query)
            .then((queryResult: QueryResult) => this.successArrayDataLoad(queryResult))
            .catch((error) => this.errorDataLoad(error))    
    }

然后我有不同的使用DataBreezeService的实体服务:

@Injectable()
export class BookingService {
    constructor(private dataBreezeService: DataBreezeService, private loggerService: LoggerService) {
    }

    public getBookingsForUser(userId: number, forceServerCall: boolean = false) {
        this.dataBreezeService.initialiseQuery('getBookingsForUser', [userId], forceServerCall);

        let query = new EntityQuery()
            .from("bookings")
            .where("createdByUserId", "==", userId)
            .where("visibilityId", "==", Status.Active)  //booking is active
            .toType('Booking')
            .expand('createdByUser, restaurant, restaurant.suburb, restaurantScheduleDiscount, rating')
            .orderBy("bookingDate Desc")
            .inlineCount();  //return total

        return this.dataBreezeService.executeQueryArray(query);
    }

2 个答案:

答案 0 :(得分:0)

我和我的团队想出了解决这个问题的方法。我会尽力解释我们是如何做到的。我们的更改涉及到breeze.debug.js的自定义。这个很重要!由于这涉及对Breeze.js的更改,如果您升级到新版本的Breeze.js,请准备好再次进行更改!

IndexedDB比使用localStorage更具挑战性,我们这样做是因为我们在网络工作者中运行Breeze - 并且Web工作者中没有本地存储。只有IndexedDB是。

1)我们在indexedDB数据库中检查proto.fetchMetadata中的元数据。如果未找到(或不支持indexedDB),则该方法将回退到默认行为。

2)如果支持IndexedDB,在proto.ajaxMetaData中的Ajax调用的成功承诺中,我们将元数据写入IndexedDB数据库。

请仔细考虑您希望在IndexedDB中保留元数据缓存多长时间。是每次会议吗?每个新构建(可能包含新元数据)?您可能希望创建一个定期删除元数据缓存的脚本。我们有代码返回网站DLL的DLL版本信息 - 我们将其与本地存储密钥进行比较,以确定我们是否有新版本。这是限制性最小的策略......只有在新的构建正在运行时才会清除缓存。

但是,您可能希望每次用户创建新会话时清除缓存。无论哪种方式,您为每个IndexedDB条目使用的密钥都应该是唯一的 - 包含路由,数据上下文(如果重置那么重要,还可以选择sessionid)。

您可以使用非常长的字符串作为键。我建议创建一个javascript对象,其中包含键的所有唯一部分(route,dllVersion,UserId),然后将对象转换为JSON字符串 - 并将该字符串用作IndexedDB键。

即使您没有应用元数据的缓存,您也应该在加载请求的元数据时至少广播一个事件,如下所示:

$rootScope.$broadcast('MetaDataLoaded' + serviceRoute, dataContext);

我们有一个包装类,它在元数据完全加载时广播此事件。如果您不使用包装器,您可能希望将广播放在proto.ajaxMetaData中,或者如果实现缓存,则在从IndexedDB缓存返回元数据之前将其广播。

在你的呼叫控制器中,使用它来监听该事件:

$scope.$on('MetaDataLoadedServiceRoute', function (event, args) {
     $scope.setup();
});

这使您可以在控制器中发出第一个请求之前等待元数据。

祝你好运!

答案 1 :(得分:0)

您是否真的说同一个EntityManager实例会发出多个元数据请求?我不记得是这样的。

如果确实如此,(a)我们应该修复 - 提出问题 - 以及(b)您可以在数据服务中解决它。

你有数据服务,对吗?您的组件永远不应该直接与EM通信,而是通过中间服务来隔离组件与管理器的直接联系,并将交互抽象为语义。无论是否使用Breeze,您都会在Angular应用程序中看到无处不在的建议。

假设您正在遵循该建议,您可以在初始化步骤中获取元数据,保留承诺,并通过已解决的承诺链接所有后续调用。

在您的应用中进行初始化步骤可能更简单,该步骤利用了元数据承诺和任何其他预加载缓存的启动活动。

您还可以在服务器上预先缓存元数据,并通过JavaScript标记将其加载为JSON-ish。这方面的例子在Breeze docs中。

为您提供了很多选择。

最后的想法。您创建了多少EntityManager个?默认情况下,每个都将获取元数据。大多数人只需要一个,但有理由想要更多。如果您需要更多,请确保将其指定为" master"并使用复制构造函数创建其他构建器。