From c1146fc50e2936ea1cf5716a861dad5a30f6314a Mon Sep 17 00:00:00 2001 From: Chanan Braunstein Date: Tue, 5 Jun 2018 21:34:42 -0600 Subject: [PATCH] Validations --- README.md | 4 ++ src/BlazorDB/BlazorDB.csproj | 2 +- src/BlazorDB/BlazorDBUpdateException.cs | 11 ++++ src/BlazorDB/DataAnnotations/MaxLength.cs | 15 +++++ src/BlazorDB/DataAnnotations/Required.cs | 9 +++ src/BlazorDB/Storage/StorageManagerSave.cs | 68 +++++++++++++++++++++- src/Sample/Models/Person.cs | 3 + src/Sample/Pages/Index.cshtml | 19 ++++++ 8 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 src/BlazorDB/BlazorDBUpdateException.cs create mode 100644 src/BlazorDB/DataAnnotations/MaxLength.cs create mode 100644 src/BlazorDB/DataAnnotations/Required.cs diff --git a/README.md b/README.md index a0184a6..861ba7b 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,10 @@ As you can see in the example above BlazorDB will detect associations added to t **Note:** At this time removing/deleting is not done automatically and needs to be done manually. A future update of BlazorDB will handle deletions properly. +## Validations + +You can annotate your model's propeties with `[Required]` and `[MaxLength(int)]` to enforce required and max length on properties. + ## Example A Todo sample built with BlazorDB is included in the sample project: diff --git a/src/BlazorDB/BlazorDB.csproj b/src/BlazorDB/BlazorDB.csproj index 2672604..990e5c4 100644 --- a/src/BlazorDB/BlazorDB.csproj +++ b/src/BlazorDB/BlazorDB.csproj @@ -7,7 +7,7 @@ false 7.3 BlazorDB - 0.1.2 + 0.1.3 Chanan Braunstein Blazor localStorage Database In memory, persisted to localstorage, database for .net Blazor browser framework diff --git a/src/BlazorDB/BlazorDBUpdateException.cs b/src/BlazorDB/BlazorDBUpdateException.cs new file mode 100644 index 0000000..70e97a4 --- /dev/null +++ b/src/BlazorDB/BlazorDBUpdateException.cs @@ -0,0 +1,11 @@ +using System; + +namespace BlazorDB +{ + public class BlazorDBUpdateException : Exception + { + public BlazorDBUpdateException(string error) : base(error) + { + } + } +} diff --git a/src/BlazorDB/DataAnnotations/MaxLength.cs b/src/BlazorDB/DataAnnotations/MaxLength.cs new file mode 100644 index 0000000..a02bd57 --- /dev/null +++ b/src/BlazorDB/DataAnnotations/MaxLength.cs @@ -0,0 +1,15 @@ +using System; + +namespace BlazorDB.DataAnnotations +{ + [AttributeUsage(AttributeTargets.Property)] + public class MaxLength : Attribute + { + public MaxLength(int length) + { + this.length = length; + } + + internal int length; + } +} \ No newline at end of file diff --git a/src/BlazorDB/DataAnnotations/Required.cs b/src/BlazorDB/DataAnnotations/Required.cs new file mode 100644 index 0000000..d6984ee --- /dev/null +++ b/src/BlazorDB/DataAnnotations/Required.cs @@ -0,0 +1,9 @@ +using System; + +namespace BlazorDB.DataAnnotations +{ + [AttributeUsage(AttributeTargets.Property)] + public class Required : Attribute + { + } +} \ No newline at end of file diff --git a/src/BlazorDB/Storage/StorageManagerSave.cs b/src/BlazorDB/Storage/StorageManagerSave.cs index 1ef18fc..25247d8 100644 --- a/src/BlazorDB/Storage/StorageManagerSave.cs +++ b/src/BlazorDB/Storage/StorageManagerSave.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using BlazorDB.DataAnnotations; using Microsoft.AspNetCore.Blazor; namespace BlazorDB.Storage @@ -15,12 +16,73 @@ public int SaveContextToLocalStorage(StorageContext context) var contextType = context.GetType(); Logger.ContextSaved(contextType); var storageSets = StorageManagerUtil.GetStorageSets(contextType); - var metadataMap = LoadMetadataList(context, storageSets, contextType); - total = SaveStorageSets(context, total, contextType, storageSets, metadataMap); - Logger.EndGroup(); + var error = ValidateModels(context, storageSets); + if (error == null) + { + var metadataMap = LoadMetadataList(context, storageSets, contextType); + total = SaveStorageSets(context, total, contextType, storageSets, metadataMap); + Logger.EndGroup(); + } + else + { + BlazorLogger.Logger.Error("SaveChanges() terminated due to validation error"); + Logger.EndGroup(); + throw new BlazorDBUpdateException(error); + } + return total; } + private string ValidateModels(StorageContext context, IEnumerable storageSets) + { + string error = null; + foreach (var storeageSetProp in storageSets) + { + 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[] { }); + while (enumerator.MoveNext()) + { + var model = enumerator.Current; + foreach (var prop in model.GetType().GetProperties()) + { + if (Attribute.IsDefined(prop, typeof(Required))) + { + var value = prop.GetValue(model); + if (value == null) + { + error = + $"{model.GetType().FullName}.{prop.Name} is a required field. SaveChanges() has been terminated."; + break; + } + } + + if (Attribute.IsDefined(prop, typeof(MaxLength))) + { + var maxLength = (MaxLength) Attribute.GetCustomAttribute(prop, typeof(MaxLength)); + var value = prop.GetValue(model); + if (value != null) + { + var str = value.ToString(); + if (str.Length > maxLength.length) + { + error = + $"{model.GetType().FullName}.{prop.Name} length is longer than {maxLength.length}. SaveChanges() has been terminated."; + break; + } + } + } + } + + if (error != null) break; + } + } + + return error; + } + private static IReadOnlyDictionary LoadMetadataList(StorageContext context, IEnumerable storageSets, Type contextType) { diff --git a/src/Sample/Models/Person.cs b/src/Sample/Models/Person.cs index c5b0e5b..5cb8b87 100644 --- a/src/Sample/Models/Person.cs +++ b/src/Sample/Models/Person.cs @@ -1,11 +1,14 @@ using System.Collections.Generic; +using BlazorDB.DataAnnotations; namespace Sample.Models { public class Person { public int Id { get; set; } + [Required] public string FirstName { get; set; } + [MaxLength(20)] public string LastName { get; set; } public Address HomeAddress { get; set; } public List
OtherAddresses { get; set; } diff --git a/src/Sample/Pages/Index.cshtml b/src/Sample/Pages/Index.cshtml index 589177f..1df64a5 100644 --- a/src/Sample/Pages/Index.cshtml +++ b/src/Sample/Pages/Index.cshtml @@ -12,6 +12,11 @@ +

Validations

+ + + + @if (@Person != null) { @@ -54,4 +59,18 @@ { Context.SaveChanges(); } + + void OnRequired() + { + var person = new Person {LastName = "Firstname required!"}; + Context.People.Add(person); + Context.SaveChanges(); + } + + void OnMaxLength() + { + var person = new Person { FirstName = "Lastname is long", LastName = "This is a very long last name and should throw!" }; + Context.People.Add(person); + Context.SaveChanges(); + } }