Skip to content

Instantly share code, notes, and snippets.

@ayende
Last active May 22, 2020 09:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ayende/41327791cd1d09d56eca13d2f935a4e5 to your computer and use it in GitHub Desktop.
Save ayende/41327791cd1d09d56eca13d2f935a4e5 to your computer and use it in GitHub Desktop.
public static class SessionExtensions
{
public static async Task<(T Entity, string ChangeVector)?> ConditionalLoadAsync<T>(this IAsyncDocumentSession session,
string id, string changeVector, CancellationToken token = default)
{
if (session.Advanced.IsLoaded(id))
{
var e = await session.LoadAsync<T>(id, token);
var cv = session.Advanced.GetChangeVectorFor(e);
return (e, cv);
}
var cmd = new ConditionalGetDocumentCommand(id, changeVector);
await session.Advanced.RequestExecutor.ExecuteAsync(cmd, session.Advanced.Context, null, token);
switch (cmd.StatusCode)
{
case HttpStatusCode.NotModified:
return default; // value not changed
case HttpStatusCode.NotFound:
return (default, default); // value is missing
}
if (cmd.Result.Results.Length == 0)
return (default, cmd.Result.ChangeVector);
var documentInfo = DocumentInfo.GetNewDocumentInfo((BlittableJsonReaderObject)cmd.Result.Results[0]);
var r = ((InMemoryDocumentSessionOperations)session).TrackEntity<T>(documentInfo);
return (r, cmd.Result.ChangeVector);
}
public class ConditionalGetResult
{
public BlittableJsonReaderArray Results { get; set; }
public string ChangeVector;
}
public class ConditionalGetDocumentCommand : RavenCommand<ConditionalGetResult>
{
private readonly string _changeVector;
private readonly string _id;
/// <summary>
/// Here we explicitly do _NOT_ want to have caching
/// by the Request Executer, we want to manage it ourselves
/// </summary>
public override bool IsReadRequest => false;
public ConditionalGetDocumentCommand(string id, string changeVector)
{
_changeVector = changeVector;
_id = id;
}
public override async Task<ResponseDisposeHandling> ProcessResponse(JsonOperationContext context, HttpCache cache, HttpResponseMessage response, string url)
{
if(response.StatusCode== HttpStatusCode.NotModified)
return ResponseDisposeHandling.Automatic;
var result = await base.ProcessResponse(context, cache, response, url);
Result.ChangeVector = response.Headers.ETag.Tag;
return result;
}
public class Deserialize : JsonDeserializationBase
{
public static Func<BlittableJsonReaderObject, ConditionalGetResult> ConditionalGet = GenerateJsonDeserializationRoutine<ConditionalGetResult>();
}
public override void SetResponse(JsonOperationContext context, BlittableJsonReaderObject response, bool fromCache)
{
if (response == null)
{
Result = null;
return;
}
if (fromCache)
{
// we have to clone the response here because otherwise the cached item might be freed while
// we are still looking at this result, so we clone it to the side
response = response.Clone(context);
}
Result = Deserialize.ConditionalGet(response);
}
public override HttpRequestMessage CreateRequest(JsonOperationContext ctx, ServerNode node, out string url)
{
var pathBuilder = new StringBuilder(node.Url);
pathBuilder.Append("/databases/")
.Append(node.Database)
.Append("/docs?")
.Append("&id=").Append(Uri.EscapeDataString(_id));
var request = new HttpRequestMessage
{
Method = HttpMethod.Get
};
request.Headers.Add("If-None-Match", '"' + _changeVector + '"');
url = pathBuilder.ToString();
return request;
}
}
}
@trevhunter
Copy link

FYI,
This will throw a NRE if there is no document found. I'd suggest a case to handle HttpStatusCode.NotFound around line 17, although a different return value may be needed so the client code can distinguish between 304 and 404.

Also line 95 needs to escape the change vector otherwise a FormatException will occur.

Should be something like:

    request.Headers.Add("If-None-Match", $"\"{_changeVector}\"");

@ayende
Copy link
Author

ayende commented May 22, 2020

@trevhunter - Thanks, I applied your changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment