Skip to content
This repository has been archived by the owner on May 10, 2020. It is now read-only.

Commit

Permalink
Throw exception when deleting a model used by an association
Browse files Browse the repository at this point in the history
  • Loading branch information
chanan committed Jun 1, 2018
1 parent bebdd79 commit 777988a
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 43 deletions.
2 changes: 1 addition & 1 deletion src/BlazorDB/BlazorDB.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
<LangVersion>7.3</LangVersion>
<PackageId>BlazorDB</PackageId>
<Version>0.1.0</Version>
<Version>0.1.1</Version>
<Authors>Chanan Braunstein</Authors>
<Title>Blazor localStorage Database</Title>
<Description>In memory, persisted to localstorage, database for .net Blazor browser framework</Description>
Expand Down
6 changes: 3 additions & 3 deletions src/BlazorDB/Storage/StorageManagerLoad.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Microsoft.AspNetCore.Blazor;
using Microsoft.Extensions.DependencyInjection;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.AspNetCore.Blazor;
using Microsoft.Extensions.DependencyInjection;

namespace BlazorDB.Storage
{
Expand Down
89 changes: 64 additions & 25 deletions src/BlazorDB/Storage/StorageManagerSave.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public int SaveContextToLocalStorage(StorageContext context)
return total;
}

private static IReadOnlyDictionary<string, Metadata> LoadMetadataList(StorageContext context, IEnumerable<PropertyInfo> storageSets, Type contextType)
private static IReadOnlyDictionary<string, Metadata> LoadMetadataList(StorageContext context,
IEnumerable<PropertyInfo> storageSets, Type contextType)
{
var map = new Dictionary<string, Metadata>();
foreach (var prop in storageSets)
Expand All @@ -48,7 +49,7 @@ private static int SaveStorageSets(StorageContext context, int total, Type conte
var storageSetValue = prop.GetValue(context);
var modelType = prop.PropertyType.GetGenericArguments()[0];
var storageTableName = Util.GetStorageTableName(contextType, modelType);

EnsureAllModelsHaveIds(storageSetValue, modelType, metadataMap);
EnsureAllAssociationsHaveIds(context, storageSetValue, modelType, storageSets, metadataMap);

Expand All @@ -63,36 +64,43 @@ private static int SaveStorageSets(StorageContext context, int total, Type conte
return total;
}

private static void EnsureAllAssociationsHaveIds(StorageContext context, object storageSetValue, Type modelType, List<PropertyInfo> storageSets, IReadOnlyDictionary<string, Metadata> metadataMap)
private static void EnsureAllAssociationsHaveIds(StorageContext context, object storageSetValue, Type modelType,
List<PropertyInfo> storageSets, IReadOnlyDictionary<string, Metadata> metadataMap)
{
var storageSetType = StorageManagerUtil.GenericStorageSetType.MakeGenericType(modelType);
var method = storageSetType.GetMethod(StorageManagerUtil.GetEnumerator);
var enumerator = (IEnumerator)method.Invoke(storageSetValue, new object[] { });
var enumerator = (IEnumerator) method.Invoke(storageSetValue, new object[] { });
while (enumerator.MoveNext())
{
var model = enumerator.Current;
foreach (var prop in model.GetType().GetProperties())
{
if (prop.GetValue(model) == null || (!StorageManagerUtil.IsInContext(storageSets, prop) &&
!StorageManagerUtil.IsListInContext(storageSets, prop))) continue;
if (StorageManagerUtil.IsInContext(storageSets, prop)) EnsureOneAssociationHasId(context, prop.GetValue(model), prop.PropertyType, storageSets, metadataMap);
if (StorageManagerUtil.IsListInContext(storageSets, prop)) EnsureManyAssociationHasId(context, prop.GetValue(model), prop, storageSets, metadataMap);
if (prop.GetValue(model) == null || !StorageManagerUtil.IsInContext(storageSets, prop) &&
!StorageManagerUtil.IsListInContext(storageSets, prop)) continue;
if (StorageManagerUtil.IsInContext(storageSets, prop))
EnsureOneAssociationHasId(context, prop.GetValue(model), prop.PropertyType, storageSets,
metadataMap);
if (StorageManagerUtil.IsListInContext(storageSets, prop))
EnsureManyAssociationHasId(context, prop.GetValue(model), prop, storageSets, metadataMap);
}
}
}

private static void EnsureManyAssociationHasId(StorageContext context, object listObject, PropertyInfo prop, List<PropertyInfo> storageSets, IReadOnlyDictionary<string, Metadata> metadataMap)
private static void EnsureManyAssociationHasId(StorageContext context, object listObject, PropertyInfo prop,
List<PropertyInfo> storageSets, IReadOnlyDictionary<string, Metadata> metadataMap)
{
var method = listObject.GetType().GetMethod(StorageManagerUtil.GetEnumerator);
var enumerator = (IEnumerator)method.Invoke(listObject, new object[] { });
var enumerator = (IEnumerator) method.Invoke(listObject, new object[] { });
while (enumerator.MoveNext())
{
var model = enumerator.Current;
EnsureOneAssociationHasId(context, model, prop.PropertyType.GetGenericArguments()[0], storageSets, metadataMap);
EnsureOneAssociationHasId(context, model, prop.PropertyType.GetGenericArguments()[0], storageSets,
metadataMap);
}
}

private static void EnsureOneAssociationHasId(StorageContext context, object associatedModel, Type propType, List<PropertyInfo> storageSets, IReadOnlyDictionary<string, Metadata> metadataMap)
private static void EnsureOneAssociationHasId(StorageContext context, object associatedModel, Type propType,
List<PropertyInfo> storageSets, IReadOnlyDictionary<string, Metadata> metadataMap)
{
var idProp = GetIdProperty(associatedModel);
var id = Convert.ToString(idProp.GetValue(associatedModel));
Expand All @@ -102,26 +110,56 @@ private static void EnsureOneAssociationHasId(StorageContext context, object ass
metadata.MaxId = metadata.MaxId + 1;
SaveAssociationModel(context, associatedModel, propType, storageSets, metadata.MaxId);
}
else
{
EnsureAssociationModelExistsOrThrow(context, Convert.ToInt32(id), storageSets, propType);
}
}

private static void EnsureAllModelsHaveIds(object storageSetValue, Type modelType, IReadOnlyDictionary<string, Metadata> metadataMap)
private static void EnsureAssociationModelExistsOrThrow(StorageContext context, int id,
IEnumerable<PropertyInfo> storageSets, Type propType)
{
var q = from p in storageSets
where p.PropertyType.GetGenericArguments()[0] == propType
select p;
var storeageSetProp = q.Single();
var storeageSet = storeageSetProp.GetValue(context);
var listProp = storeageSet.GetType().GetProperty(StorageManagerUtil.List, StorageManagerUtil.Flags);
var list = listProp.GetValue(storeageSet);
var method = list.GetType().GetMethod(StorageManagerUtil.GetEnumerator);
var enumerator = (IEnumerator) method.Invoke(list, new object[] { });
var found = false;
while (enumerator.MoveNext())
{
var model = enumerator.Current;
if (id != GetId(model)) continue;
found = true;
break;
}

if (!found)
throw new InvalidOperationException(
$"A model of type: {propType.Name} with Id {id} was deleted but still being used by an association. Remove it from the association as well.");
}

private static void EnsureAllModelsHaveIds(object storageSetValue, Type modelType,
IReadOnlyDictionary<string, Metadata> metadataMap)
{
var storageSetType = StorageManagerUtil.GenericStorageSetType.MakeGenericType(modelType);
var method = storageSetType.GetMethod(StorageManagerUtil.GetEnumerator);
var metadata = metadataMap[Util.GetFullyQualifiedTypeName(modelType)];
var enumerator = (IEnumerator)method.Invoke(storageSetValue, new object[] { });
var enumerator = (IEnumerator) method.Invoke(storageSetValue, new object[] { });
while (enumerator.MoveNext())
{
var model = enumerator.Current;
if (GetId(model) == 0)
{
metadata.MaxId = metadata.MaxId + 1;
SetId(model, metadata.MaxId);
}
if (GetId(model) != 0) continue;
metadata.MaxId = metadata.MaxId + 1;
SetId(model, metadata.MaxId);
}
}

private static void SaveMetadata(string storageTableName, List<Guid> guids, Type context, Type modelType, Metadata oldMetadata)
private static void SaveMetadata(string storageTableName, List<Guid> guids, Type context, Type modelType,
Metadata oldMetadata)
{
var metadata = new Metadata
{
Expand Down Expand Up @@ -169,8 +207,8 @@ private static string ScanModelForAssociations(object model, List<PropertyInfo>
var result = serializedModel;
foreach (var prop in model.GetType().GetProperties())
{
if (prop.GetValue(model) == null || (!StorageManagerUtil.IsInContext(storageSets, prop) &&
!StorageManagerUtil.IsListInContext(storageSets, prop))) continue;
if (prop.GetValue(model) == null || !StorageManagerUtil.IsInContext(storageSets, prop) &&
!StorageManagerUtil.IsListInContext(storageSets, prop)) continue;
if (StorageManagerUtil.IsInContext(storageSets, prop)) result = FixOneAssociation(model, prop, result);
if (StorageManagerUtil.IsListInContext(storageSets, prop))
result = FixManyAssociation(model, prop, result);
Expand Down Expand Up @@ -203,18 +241,19 @@ private static string FixOneAssociation(object model, PropertyInfo prop, string
return result;
}

private static int SaveAssociationModel(StorageContext context, object associatedModel, Type propType, IEnumerable<PropertyInfo> storageSets, int id)
private static int SaveAssociationModel(StorageContext context, object associatedModel, Type propType,
IEnumerable<PropertyInfo> storageSets, int id)
{
var q = from p in storageSets
where p.PropertyType.GetGenericArguments()[0] == propType
select p;
select p;
var storeageSetProp = q.Single();
var storeageSet = storeageSetProp.GetValue(context);
var listProp = storeageSet.GetType().GetProperty(StorageManagerUtil.List, StorageManagerUtil.Flags);
var list = listProp.GetValue(storeageSet);
var addMethod = list.GetType().GetMethod(StorageManagerUtil.Add);
SetId(associatedModel, id);
addMethod.Invoke(list, new[] { associatedModel });
addMethod.Invoke(list, new[] {associatedModel});
return id;
}

Expand Down
14 changes: 1 addition & 13 deletions src/BlazorDB/StorageSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,7 @@ IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}

//TODO: Consider using an "Id table"
private int SetId(TModel item)
{
var prop = item.GetType().GetProperty("Id");
if (prop == null) throw new ArgumentException("Model must have an Id property");
var max = List.Select(i => (int) prop.GetValue(i)).Concat(new[] {0}).Max();

var id = max + 1;
prop.SetValue(item, id);
return id;
}


private static int GetId(TModel item)
{
var prop = item.GetType().GetProperty("Id");
Expand Down
16 changes: 16 additions & 0 deletions src/Sample/Pages/Associations.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@

<BlazorButton Color="Color.Primary" onclick="@OnLoadPerson">Load Person</BlazorButton>

<h2>Delete Test (Should throw exception)</h2>

<BlazorButton Color="Color.Primary" onclick="@OnDelete">Add a Person with Addresses then delete Address</BlazorButton>

<h2>Result</h2>

@if (_person != null)
Expand Down Expand Up @@ -76,4 +80,16 @@
_person = Context.People[1];
StateHasChanged();
}

void OnDelete(UIMouseEventArgs e)
{
var person = new Person { FirstName = "John", LastName = "Smith" };
var address = new Address { Street = "221 Baker Streeet", City = "This should be a refrence to address since Address exists in the context" };
person.HomeAddress = address;
Context.People.Add(person);
Context.SaveChanges();

Context.Addresses.RemoveAt(0);
Context.SaveChanges();
}
}
2 changes: 1 addition & 1 deletion src/Sample/global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "2.1.300-preview2-008533"
"version": "2.1.300"
}
}

0 comments on commit 777988a

Please sign in to comment.