Skip to content

Commit

Permalink
Merge pull request #27 from canhorn/dev
Browse files Browse the repository at this point in the history
dev merge : Add support to CachedEntity finalization
+semver: minor
  • Loading branch information
canhorn authored Jul 11, 2021
2 parents d8f1d47 + a5cdb17 commit 3368bc3
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 25 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ appsettings.*.json
.vs
*.csproj.user
published
nuget-packages
nuget-packages
.idea
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
_invokableReference = DotNetObjectReference.Create(
_messageUpdateInvokeHelper
);

_funcCallbackClass = EventHorizonBlazorInterop.New(
new object[]
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<div>
<h3>Finalization Validation</h3>
<div class="--lighter"></div>
<div>
Status:
@if (TestStatus == "Passed")
{
<span class="green-badge">@TestStatus</span>
}
else if (TestStatus == "Failed")
{
<span class="red-badge">@TestStatus</span>
}
else
{
<span>@TestStatus</span>
}
</div>
<button class="run-btn" @onclick="HandleRunTest">Run</button>
</div>


@code {
public string TestStatus = "Pending";

private int result;

private async Task HandleRunTest()
{
result = 0;

await RunTest();
ValidateTest();
}

public async Task RunTest()
{
var list = CreateEntities();

GC.Collect();

// GC won't collect until we release the thread, so we offload
// the rest of it to be completed async.
await Task.Delay(10);

foreach (var guid in list)
{
var value = EventHorizonBlazorInterop.Get<string>(guid, "X");
if (value is not null)
result++;
}
}

static List<string> CreateEntities()
{
var list = new List<string>();

for (var i = 0; i < 100; i++)
{
var entity = EventHorizonBlazorInterop.FuncClass(
e => new Vector3CachedEntity(e),
new object[]
{
new[] { "funcClass", "func" },
}
);

list.Add(entity.___guid);
}

return list;
}

public void ValidateTest()
{
if (result == 0)
{
TestStatus = "Passed";
}
else
{
TestStatus = "Failed";
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@

<div class="testing-content">
<InteropNewArgumentAsNullTest />
</div>
<InteropFinalizationTest/>
</div>
2 changes: 1 addition & 1 deletion EventHorizon.Blazor.Interop/CachedEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ namespace EventHorizon.Blazor.Interop
public class CachedEntity : ICachedEntity
{
/// <inheritdoc />
public string ___guid { get; set; }
public CachedEntityRef ___guid { get; set; }
}
}
2 changes: 1 addition & 1 deletion EventHorizon.Blazor.Interop/CachedEntityConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ JsonSerializerOptions options
switch (propertyName)
{
case "___guid":
entity.___guid = reader.GetString();
entity.___guid = new CachedEntityRef(reader.GetString());
break;
}
}
Expand Down
84 changes: 84 additions & 0 deletions EventHorizon.Blazor.Interop/CachedEntityRef.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace EventHorizon.Blazor.Interop
{
/// <summary>
/// Represents a root reference to JS object. The JS object will live as long as this reference exists,
/// and once it is collected, the JS object will also be released for collection by the JS runtime.
/// </summary>
[JsonConverter(typeof(CachedEntityRefConverter))]
public sealed class CachedEntityRef : IEquatable<CachedEntityRef>
{
readonly string _guid;

internal CachedEntityRef(string guid)
{
_guid = guid;
}

/// <inheritdoc />
~CachedEntityRef()
{
EventHorizonBlazorInterop.RemoveEntity(_guid);
}

/// <inheritdoc />
public override string ToString()
{
return _guid;
}

/// <inheritdoc />
public override int GetHashCode()
{
return (_guid != null ? _guid.GetHashCode() : 0);
}

/// <inheritdoc />
public bool Equals(CachedEntityRef other)
{
return _guid == other?._guid;
}

/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((CachedEntityRef) obj);
}

/// <summary>
/// Converts an instance of this object to string.
/// </summary>
/// <param name="obj">A cached entity reference.</param>
/// <returns>The internal UUID of the cached entity.</returns>
public static implicit operator string(CachedEntityRef obj) => obj.ToString();
}


/// <summary>
/// This helps with the payload of passing a CacheEntity between the C# and client.
/// </summary>
/// <typeparam name="T"></typeparam>
internal class CachedEntityRefConverter : JsonConverter<CachedEntityRef>
{
/// <inheritdoc />
public override bool CanConvert(Type typeToConvert) =>
typeof(CachedEntityRef).IsAssignableFrom(typeToConvert);

/// <inheritdoc />
public override CachedEntityRef Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.Read() ? new CachedEntityRef(reader.GetString()) : null;
}

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, CachedEntityRef value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
}
42 changes: 29 additions & 13 deletions EventHorizon.Blazor.Interop/EventHorizonBlazorInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ params object[] args
"blazorInterop.funcClass",
args
);
return classBuilder(new CachedEntity { ___guid = cacheKey });
return classBuilder(new CachedEntity { ___guid = new CachedEntityRef(cacheKey) });
}

/// <summary>
Expand Down Expand Up @@ -192,7 +192,7 @@ params object[] args
var index = 0;
foreach (var result in results)
{
array[index] = classBuilder(new CachedEntity { ___guid = result });
array[index] = classBuilder(new CachedEntity { ___guid = new CachedEntityRef(result) });
index++;
}

Expand Down Expand Up @@ -287,7 +287,7 @@ Func<ICachedEntity, T> classBuilder
)
);

return classBuilder(new CachedEntity { ___guid = result });
return classBuilder(new CachedEntity { ___guid = new CachedEntityRef(result) });
}

/// <summary>
Expand Down Expand Up @@ -331,7 +331,7 @@ Func<ICachedEntity, T> classBuilder
var index = 0;
foreach (var result in results)
{
array[index] = classBuilder(new CachedEntity { ___guid = result });
array[index] = classBuilder(new CachedEntity { ___guid = new CachedEntityRef(result) });
index++;
}

Expand Down Expand Up @@ -409,10 +409,11 @@ public static ICachedEntity New(
params object[] args
)
{
return RUNTIME.Invoke<CachedEntity>(
var cacheRef = new CachedEntityRef(RUNTIME.Invoke<string>(
"blazorInterop.new",
args
);
));
return new CachedEntity { ___guid = cacheRef };
}

/// <summary>
Expand Down Expand Up @@ -530,16 +531,20 @@ object value
/// <param name="identifier">The <see cref="ICachedEntity.___guid"/> on the client.</param>
/// <param name="prop">The property on the root the value should be from.</param>
/// <returns>The cache identifier of the prop value.</returns>
public static ICachedEntity cacheEntity(
public static ICachedEntity CacheEntity(
string identifier,
string prop
)
{
return RUNTIME.Invoke<CachedEntity>(
"blazorInterop.cacheEntity",
identifier,
prop
var cacheRef = new CachedEntityRef(
RUNTIME.Invoke<string>(
"blazorInterop.cacheEntity",
identifier,
prop
)
);

return new CachedEntity { ___guid = cacheRef };
}


Expand Down Expand Up @@ -614,7 +619,7 @@ params object[] args
"blazorInterop.taskClass",
args
);
return classBuilder(new CachedEntity { ___guid = cacheKey });
return classBuilder(new CachedEntity { ___guid = new CachedEntityRef(cacheKey) });
}

/// <summary>
Expand Down Expand Up @@ -692,12 +697,23 @@ params object[] args
var index = 0;
foreach (var result in results)
{
array[index] = classBuilder(new CachedEntity { ___guid = result });
array[index] = classBuilder(new CachedEntity { ___guid = new CachedEntityRef(result) });
index++;
}

return array;
}

/// <summary>
/// Removes an entity from the cache, allowing the JS runtime to garbage collect it.
/// This method is called automatically by any objects derived from <see cref="CachedEntity"/>
/// upon finalization.
/// </summary>
/// <param name="identifier">Identifier is a <see cref="ICachedEntity.___guid"/></param>
public static void RemoveEntity(string identifier)
{
RUNTIME.InvokeVoid("blazorInterop.removeEntity", identifier);
}
}

internal struct JavaScriptMethodRunner
Expand Down
2 changes: 1 addition & 1 deletion EventHorizon.Blazor.Interop/ICachedEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public interface ICachedEntity
/// <summary>
/// The Client identifier for this specific entity.
/// </summary>
string ___guid { get; set; }
CachedEntityRef ___guid { get; set; }
}
}
13 changes: 6 additions & 7 deletions EventHorizon.Blazor.Interop/wwwroot/interop-bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,7 @@
var newObject = new createNew(...args);
newObject[cacheKey] = guid();
argumentCache.set(newObject[cacheKey], newObject);
return {
[cacheKey]: newObject[cacheKey]
};
return newObject[cacheKey];
} catch (ex) {
console.log("error", ex);
}
Expand Down Expand Up @@ -719,9 +717,10 @@
var newObject = cachedEntity[prop];
newObject[cacheKey] = guid();
argumentCache.set(newObject[cacheKey], newObject);
return {
[cacheKey]: newObject[cacheKey]
};
return newObject[cacheKey];
},
removeEntity: (identifier) => {
argumentCache.delete(identifier);
},
};
})();
})();

0 comments on commit 3368bc3

Please sign in to comment.