Asp.net web api 使用Breeze.js连续进行相同的Web API调用时返回的缓存数据
在使用Durandal SPA编写Breeze.js时,我遇到了一个奇怪的问题(主要基于John Papa伟大的SPA Jumpstart()培训),当一个用户尝试连续多次调用同一个服务器API时,服务器只被命中一次,第二次调用时,我似乎得到了缓存数据(调用甚至没有命中服务器),这也可以在Jumpstart示例中复制,下面是在Jumpstart上复制这一点的修改代码Asp.net web api 使用Breeze.js连续进行相同的Web API调用时返回的缓存数据,asp.net-web-api,breeze,single-page-application,durandal-2.0,Asp.net Web Api,Breeze,Single Page Application,Durandal 2.0,在使用Durandal SPA编写Breeze.js时,我遇到了一个奇怪的问题(主要基于John Papa伟大的SPA Jumpstart()培训),当一个用户尝试连续多次调用同一个服务器API时,服务器只被命中一次,第二次调用时,我似乎得到了缓存数据(调用甚至没有命中服务器),这也可以在Jumpstart示例中复制,下面是在Jumpstart上复制这一点的修改代码 var activate = function () { for (var i = 0 ; i <
var activate = function () {
for (var i = 0 ; i < 2 ; i++) {
datacontext.getSessionPartials(sessions, true);
}
};
var activate = function () {
for (var i = 0 ; i < 2 ; i++)
{
datacontext.getSpeakerPartials(speakers, true);
}
};
var activate=函数(){
对于(变量i=0;i<2;i++){
getSessionPartials(sessions,true);
}
};
变量激活=函数(){
对于(变量i=0;i<2;i++)
{
getSpeakerPartials(speakers,true);
}
};
编辑-使用完整代码更新:
我的Viewmodel的代码
define(['services/datacontext'], function (datacontext) {
var speakers = ko.observableArray();
var activate = function () {
for (var i = 0 ; i < 2 ; i++)
{
datacontext.getSpeakerPartials(speakers, true);
}
};
var refresh = function () {
return datacontext.getSpeakerPartials(speakers, true);
};
var vm = {
activate: activate,
speakers: speakers,
title: 'Speakers',
refresh: refresh
};
return vm;
});
define(['services/datacontext'],函数(datacontext){
变量演讲者=ko.observearray();
变量激活=函数(){
对于(变量i=0;i<2;i++)
{
getSpeakerPartials(speakers,true);
}
};
var refresh=函数(){
返回datacontext.getSpeakerPartials(speakers,true);
};
var vm={
激活:激活,
发言者:发言者,
标题:"演讲者",,
刷新:刷新
};
返回虚拟机;
});
下面是我的datacontext/服务的代码:
define([
'durandal/system',
'services/model',
'config',
'services/logger',
'services/breeze.partial-entities'],
function (system, model, config, logger, partialMapper) {
var EntityQuery = breeze.EntityQuery;
var manager = configureBreezeManager();
var orderBy = model.orderBy;
var entityNames = model.entityNames;
var getSpeakerPartials = function (speakersObservable, forceRemote) {
if (!forceRemote) {
var p = getLocal('Persons', orderBy.speaker);
if (p.length > 0) {
speakersObservable(p);
return Q.resolve();
}
}
var query = EntityQuery.from('Speakers')
.select('id, firstName, lastName, imageSource')
.orderBy(orderBy.speaker);
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
var list = partialMapper.mapDtosToEntities(
manager, data.results, entityNames.speaker, 'id');
if (speakersObservable) {
speakersObservable(list);
}
log('Retrieved [Speaker] from remote data source',
data, true);
}
};
var getSessionPartials = function (sessionsObservable, forceRemote) {
if (!forceRemote) {
var s = getLocal('Sessions', orderBy.session);
if (s.length > 3) {
// Edge case
// We need this check because we may have 1 entity already.
// If we start on a specific person, this may happen. So we check for > 2, really
sessionsObservable(s);
return Q.resolve();
}
}
var query = EntityQuery.from('Sessions')
.select('id, title, code, speakerId, trackId, timeSlotId, roomId, level, tags')
.orderBy('timeSlotId, level, speaker.firstName');
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
var list = partialMapper.mapDtosToEntities(
manager, data.results, entityNames.session, 'id');
if (sessionsObservable) {
sessionsObservable(list);
}
log('Retrieved [Sessions] from remote data source',
data, true);
}
};
var getSessionById = function(sessionId, sessionObservable) {
// 1st - fetchEntityByKey will look in local cache
// first (because 3rd parm is true)
// if not there then it will go remote
return manager.fetchEntityByKey(
entityNames.session, sessionId, true)
.then(fetchSucceeded)
.fail(queryFailed);
// 2nd - Refresh the entity from remote store (if needed)
function fetchSucceeded(data) {
var s = data.entity;
return s.isPartial() ? refreshSession(s) : sessionObservable(s);
}
function refreshSession(session) {
return EntityQuery.fromEntities(session)
.using(manager).execute()
.then(querySucceeded)
.fail(queryFailed);
}
function querySucceeded(data) {
var s = data.results[0];
s.isPartial(false);
log('Retrieved [Session] from remote data source', s, true);
return sessionObservable(s);
}
};
var cancelChanges = function() {
manager.rejectChanges();
log('Canceled changes', null, true);
};
var saveChanges = function() {
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
function saveSucceeded(saveResult) {
log('Saved data successfully', saveResult, true);
}
function saveFailed(error) {
var msg = 'Save failed: ' + getErrorMessages(error);
logError(msg, error);
error.message = msg;
throw error;
}
};
var primeData = function () {
var promise = Q.all([
getLookups(),
getSpeakerPartials(null, true)])
.then(applyValidators);
return promise.then(success);
function success() {
datacontext.lookups = {
rooms: getLocal('Rooms', 'name', true),
tracks: getLocal('Tracks', 'name', true),
timeslots: getLocal('TimeSlots', 'start', true),
speakers: getLocal('Persons', orderBy.speaker, true)
};
log('Primed data', datacontext.lookups);
}
function applyValidators() {
model.applySessionValidators(manager.metadataStore);
}
};
var createSession = function() {
return manager.createEntity(entityNames.session);
};
var hasChanges = ko.observable(false);
manager.hasChangesChanged.subscribe(function(eventArgs) {
hasChanges(eventArgs.hasChanges);
});
var datacontext = {
createSession: createSession,
getSessionPartials: getSessionPartials,
getSpeakerPartials: getSpeakerPartials,
hasChanges: hasChanges,
getSessionById: getSessionById,
primeData: primeData,
cancelChanges: cancelChanges,
saveChanges: saveChanges
};
return datacontext;
//#region Internal methods
function getLocal(resource, ordering, includeNullos) {
var query = EntityQuery.from(resource)
.orderBy(ordering);
if (!includeNullos) {
query = query.where('id', '!=', 0);
}
return manager.executeQueryLocally(query);
}
function getErrorMessages(error) {
var msg = error.message;
if (msg.match(/validation error/i)) {
return getValidationMessages(error);
}
return msg;
}
function getValidationMessages(error) {
try {
//foreach entity with a validation error
return error.entitiesWithErrors.map(function(entity) {
// get each validation error
return entity.entityAspect.getValidationErrors().map(function(valError) {
// return the error message from the validation
return valError.errorMessage;
}).join('; <br/>');
}).join('; <br/>');
}
catch (e) { }
return 'validation error';
}
function queryFailed(error) {
var msg = 'Error retreiving data. ' + error.message;
logError(msg, error);
throw error;
}
function configureBreezeManager() {
breeze.NamingConvention.camelCase.setAsDefault();
var mgr = new breeze.EntityManager(config.remoteServiceName);
model.configureMetadataStore(mgr.metadataStore);
return mgr;
}
function getLookups() {
return EntityQuery.from('Lookups')
.using(manager).execute()
.then(processLookups)
.fail(queryFailed);
}
function processLookups() {
model.createNullos(manager);
}
function log(msg, data, showToast) {
logger.log(msg, data, system.getModuleId(datacontext), showToast);
}
function logError(msg, error) {
logger.logError(msg, error, system.getModuleId(datacontext), true);
}
//#endregion
});
定义([
“durandal/系统”,
“服务/模型”,
“配置”,
“服务/记录器”,
“服务/breeze.partial实体”],
功能(系统、型号、配置、记录器、partialMapper){
var EntityQuery=breeze.EntityQuery;
var manager=configureBreezeManager();
var orderBy=model.orderBy;
var entityNames=model.entityNames;
var getSpeakerPartials=函数(speakersObservable,forceRemote){
如果(!forceRemote){
var p=getLocal('Persons',orderBy.speaker);
如果(p.length>0){
可观察的说话人(p);
返回Q.resolve();
}
}
var query=EntityQuery.from('Speakers')
.select('id,firstName,lastName,imageSource')
.orderBy(orderBy.speaker);
return manager.executeQuery(查询)
.然后(查询成功)
。失败(查询失败);
函数查询成功(数据){
var list=partialMapper.MapdToToEntities(
经理,data.results,entityNames.speaker,“id”);
if(可观察到的扬声器){
可观察的说话者(列表);
}
日志('从远程数据源检索到[扬声器],
数据(真实);
}
};
var getSessionPartials=函数(sessionsObservable,forceRemote){
如果(!forceRemote){
var s=getLocal('Sessions',orderBy.session);
如果(s.长度>3){
//边缘案例
//我们需要这张支票,因为我们可能已经有1个实体了。
//如果我们从一个特定的人开始,这可能会发生。所以我们检查>2,真的
可观察的会期;
返回Q.resolve();
}
}
var query=EntityQuery.from('会话')
.select('id、title、code、speakerId、trackId、timeSlotId、roomId、level、tags')
.orderBy('timeSlotId,level,speaker.firstName');
return manager.executeQuery(查询)
.然后(查询成功)
。失败(查询失败);
函数查询成功(数据){
var list=partialMapper.MapdToToEntities(
manager、data.results、entityNames.session、“id”);
if(可观察的会话){
可观察的会话(列表);
}
日志('从远程数据源检索[会话],
数据(真实);
}
};
var getSessionById=函数(sessionId,sessionObservable){
//第一个-fetchEntityByKey将在本地缓存中查找
//第一(因为第三个参数是正确的)
//如果不在那里,那么它将远程运行
return manager.fetchEntityByKey(
entityNames.session,sessionId,true)
.然后(成功)
。失败(查询失败);
//第二个-从远程存储刷新实体(如果需要)
函数获取成功(数据){
var s=数据实体;
返回s.isPartial()?刷新会话:会话可观察;
}
功能刷新会话(会话){
返回EntityQuery.fromEntities(会话)
.using(manager.execute())
.然后(查询成功)
。失败(查询失败);
}
函数查询成功(数据){
var s=数据。结果[0];
s、 i部分(假);
日志('从远程数据源检索[Session],s,true);
可观测的返回会话;
}
};
var cancelChanges=函数(){
manager.rejectChanges();
日志(“已取消的更改”,null,true);
};
var saveChanges=function(){
returnmanager.saveChanges()
.然后(保存成功)
.失败(保存失败);
函数savesuccessed(saveResult){
日志(“成功保存数据”,保存结果,true);
}
函数保存失败(错误){
var msg='保存失败:'+getErrorMessages(错误);
日志错误(消息,错误);
error.message=msg;
投掷误差;
}
namespace CodeCamper.Controllers
{
[BreezeController]
public class BreezeController : ApiController
{
readonly EFContextProvider<CodeCamperDbContext> _contextProvider =
new EFContextProvider<CodeCamperDbContext>();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
[HttpGet]
public object Lookups()
{
var rooms = _contextProvider.Context.Rooms;
var tracks = _contextProvider.Context.Tracks;
var timeslots = _contextProvider.Context.TimeSlots;
return new {rooms, tracks, timeslots};
}
[HttpGet]
public IQueryable<Session> Sessions()
{
return _contextProvider.Context.Sessions;
}
[HttpGet]
public IQueryable<Person> Persons()
{
return _contextProvider.Context.Persons;
}
[HttpGet]
public IQueryable<Person> Speakers()
{
return _contextProvider.Context.Persons
.Where(p => p.SpeakerSessions.Any());
}
}
}