My EF models are like so:
public class Base
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Foo : Base
{
public IEnumerable<Bar> Bars { get; set; }
}
public class Bar : Base
{
...
}
My intention was to build the API in such a way that if you specify null on an update it would discard that value however it does not appear to work that way. In my repository update code I do the following:
public override IEnumerable<Base> Update(IEnumerable<Base> items)
{
foreach (var item in items.OfType<Foo>())
{
var existingItem = _context.Foos.Find(item.Id);
if (existingItem == null)
{
throw new InvalidOperationException(
$"Can't update item of type `{typeof(Foo)}` as it doesn't exist. ");
}
var entry = _context.Entry(existingItem);
entry.CurrentValues.SetValues(item);
foreach (var property in entry.Properties)
{
var original = property.OriginalValue;
var current = property.CurrentValue;
property.IsModified = original != null && !original.Equals(current);
}
var collection = entry.Collection(nameof(Foo.Bars));
if (collection.CurrentValue == null)
collection.IsModified = false;
}
var rows = _context.SaveChanges();
return Read(items.Select(e => e.Id));
}
Since the Foo.Bars property is actually a Collection/Navigation property there's no OriginalValue property to it, only a CurrentValue and this means I can't discard the value if it is null. Also it seems that setting the collection.IsModified to false has no effect and the Foo.Bars property is set to null regardless of the IsModified state.
Looking for advice on perhaps a better way to handle this or something I'm missing. Thanks.
Related
I have a list that matches the requirements that I get through the request from js.
Data from the request comes filled in, but the list is not displayed
< ejs-treeview id="treedata" created="created">
< e-treeview-fields dataSource="#Model.Items" id="LevelCode" parentId="ParentLevelCode" text="Name" hasChildren="HasChild"></e-treeview-fields>
< /ejs-treeview>
function created()
{
getCategories();
}
function getCategories() {
let treedata = document.getElementById('treedata').ej2_instances[0];
let request = new ej.base.Ajax(`/Category/GetAll`, 'GET');
request.send();
request.onSuccess = data => {
if (treedata.element !== undefined) {
let final = JSON.parse(data);
treedata.fields.dataSource = final.Categories;
treedata.dataBind();
treedata.refresh();
}
};
}
public class GetAllCategoriesHandlerResponseItem
{
public string Id { get; set; }
public string Name { get; set; }
public bool HasChild { get; set; }
public string LevelCode { get; set; }
public string ParentLevelCode { get; set; }
}
In TreeView component, the fields property has been provided to set or get the data source and other data-related information. You can use this property to dynamically change the TreeView component data source. But you need to specify the properties in its predefined structure to update the TreeView data source.
Check the below code snippet.
function getCategories() {
let treedata = document.getElementById('treedata').ej2_instances[0];
let request = new ej.base.Ajax(`/Category/GetAll`, 'GET');
request.send();
request.onSuccess = data => {
if (treedata.element !== undefined) {
let final = JSON.parse(data);
treedata.fields = {datasource: final.Categories, id:"LevelCode", parentId:"ParentLevelCode", text:"Name", hasChildren:"HasChild" };
treedata.dataBind();
treedata.refresh();
}
};
}
You can refer to the below link to know about the details.
https://www.syncfusion.com/kb/10135/how-to-refresh-the-data-in-ej2-treeview
Say you had a simple object as follows:
public class PaymentLineItem
{
public DateTime TimeStamp { get; set; }
public decimal Amount { get; set; }
public string Description { get; set; }
public PayCodeDto PayCode { get; set; } //= PayCodeDto.Invalid;
public override string ToString()
{
return StringUtils.ToString(this);
}
public override bool Equals(object obj)
{
return Equals(obj as PaymentLineItem);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = TimeStamp.GetHashCode();
hashCode = (hashCode * 397) ^ Amount.GetHashCode();
hashCode = (hashCode * 397) ^ (Description != null ? Description.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (PayCode != null ? PayCode.GetHashCode() : 0);
return hashCode;
}
}
public bool Equals(PaymentLineItem other)
{
if (other == null) return false;
if (ReferenceEquals(this, other)) return true;
return TimeStamp.Equals(other.TimeStamp)
&& Amount.Equals(other.Amount)
&& string.Equals(Description, other.Description)
&& Object.Equals(PayCode, other.PayCode);
}
}
There is nothing special with this class apart from usage of the new C# auto-property initialize introduced in C# 6.0. PayCodeDto is simple with three properties and implements IEquatable.
If you create a collection of these payment items and serialize this using
var serialized = JsonConvert.SerializeObject(sut);
and you then take the correctly serialized string and deserialize using
var deserialized = JsonConvert.DeserializeObject<List<PaymentLineItemLight>>(serialized);
what you end up with is a collection all instances of the line items having the same PayCode.
This is the sample test:
[Test]
public void Should_Serialize_Collection_In_Package_Correctly()
{
var sut = new List<PaymentLineItemLight>
{
new PaymentLineItemLight
{
Amount = new decimal(153.0000),
Description = "48385 - Multiple items payment",
TimeStamp = DateTime.Parse("2018-01-30T10:40:47.477"),
PayCode = new PayCodeDto("105", "dont-care") {DictionaryId = 2}
},
new PaymentLineItemLight
{
Amount = new decimal(53.0000),
Description = "483816 - Multiple items payment",
TimeStamp = DateTime.Parse("2018-01-30T10:40:47.477"),
PayCode = new PayCodeDto("104", "dont-care") {DictionaryId = 2}
},
new PaymentLineItemLight
{
Amount = new decimal(200.0000),
Description = "483817 - Multiple items payment",
TimeStamp = DateTime.Parse("2018-01-30T10:40:47.477"),
PayCode = new PayCodeDto("102", "dont-care-102") {DictionaryId = 2}
}
};
var serialized = JsonConvert.SerializeObject(sut);
var deserialized = JsonConvert.DeserializeObject<List<PaymentLineItemLight>>(serialized);
CollectionAssert.AreEqual(sut, deserialized);
}
If I remove the auto-property initialize, the test passes. I must be missing something here. Anyone seen this?
I like to keep my data models clean (and not dependent on any Servicestack DLLs) by defining any attributes just in the database layer. However since upgrading to ver 5.0, my application fails to correctly recognise attributes set in c# using AddAttributes().
The code below shows a minimal reproducable example.
using ServiceStack;
using ServiceStack.DataAnnotations;
using ServiceStack.OrmLite;
namespace OrmliteAttributeTest
{
class Program
{
static void Main(string[] args)
{
var type = typeof(DataItem2);
type.AddAttributes(new AliasAttribute("DataItem2Table"));
var prop = type.GetProperty(nameof(DataItem2.ItemKey));
if (prop != null)
prop.AddAttributes(new PrimaryKeyAttribute());
prop = type.GetProperty(nameof(DataItem2.ItemDescription));
if (prop != null)
prop.AddAttributes(new StringLengthAttribute(100));
SqlServerDialect.Provider.GetStringConverter().UseUnicode = true;
var connectionString = #"Data Source=localhost\sqlexpress; Initial Catalog=OrmLiteTest; Integrated Security=True;";
var connectionFactory = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider);
using (var db = connectionFactory.OpenDbConnection())
{
db.CreateTableIfNotExists<DataItem>();
db.CreateTableIfNotExists<DataItem2>();
}
}
}
[Alias("DataItemTable")]
public class DataItem
{
[PrimaryKey]
public int ItemKey { get; set; }
[StringLength(100)]
public string ItemDescription { get; set; }
}
public class DataItem2
{
public int ItemKey { get; set; }
public string ItemDescription { get; set; }
}
}
The table for DataItem is created correctly using the attributes as specified. The table for DataItem2 fails to use any of the attibubes defined in the code.
The issue is that the static constructor of JsConfig.InitStatics() needs to be initialized once on Startup which reinitializes the static configuration (and dynamic attributes added) in ServiceStack Serializers.
This is implicitly called in ServiceStack libraries like OrmLiteConnectionFactory which because it hasn't been called before will reinitialize that ServiceStack.Text static configuration. To avoid resetting the dynamic attributes you can initialize the OrmLiteConnectionFactory before adding the attributes:
var connectionFactory = new OrmLiteConnectionFactory(connStr, SqlServerDialect.Provider);
var type = typeof(DataItem2);
type.AddAttributes(new AliasAttribute("DataItem2Table"));
var prop = type.GetProperty(nameof(DataItem2.ItemKey));
if (prop != null)
prop.AddAttributes(new PrimaryKeyAttribute());
prop = type.GetProperty(nameof(DataItem2.ItemDescription));
if (prop != null)
prop.AddAttributes(new StringLengthAttribute(100));
SqlServerDialect.Provider.GetStringConverter().UseUnicode = true;
Or if preferred you can explicitly call InitStatics() before adding any attributes, e.g:
JsConfig.InitStatics();
var type = typeof(DataItem2);
type.AddAttributes(new AliasAttribute("DataItem2Table"));
//...
Is there a way to ignore get-only properties using the Json.NET serializer but without using JsonIgnore attributes?
For example, I have a class with these get properties:
public Keys Hotkey { get; set; }
public Keys KeyCode
{
get
{
return Hotkey & Keys.KeyCode;
}
}
public Keys ModifiersKeys
{
get
{
return Hotkey & Keys.Modifiers;
}
}
public bool Control
{
get
{
return (Hotkey & Keys.Control) == Keys.Control;
}
}
public bool Shift
{
get
{
return (Hotkey & Keys.Shift) == Keys.Shift;
}
}
public bool Alt
{
get
{
return (Hotkey & Keys.Alt) == Keys.Alt;
}
}
public Modifiers ModifiersEnum
{
get
{
Modifiers modifiers = Modifiers.None;
if (Alt) modifiers |= Modifiers.Alt;
if (Control) modifiers |= Modifiers.Control;
if (Shift) modifiers |= Modifiers.Shift;
return modifiers;
}
}
public bool IsOnlyModifiers
{
get
{
return KeyCode == Keys.ControlKey || KeyCode == Keys.ShiftKey || KeyCode == Keys.Menu;
}
}
public bool IsValidKey
{
get
{
return KeyCode != Keys.None && !IsOnlyModifiers;
}
}
Do I need to add [JsonIgnore] to all of them (I also have many other classes), or there is better way to ignore all get-only properties?
You can do this by implementing a custom IContractResolver and using that during serialization. If you subclass the DefaultContractResolver, this becomes very easy to do:
class WritablePropertiesOnlyResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
return props.Where(p => p.Writable).ToList();
}
}
Here is a test program demonstrating how to use it:
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
class Program
{
static void Main(string[] args)
{
Widget w = new Widget { Id = 2, Name = "Joe Schmoe" };
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new WritablePropertiesOnlyResolver()
};
string json = JsonConvert.SerializeObject(w, settings);
Console.WriteLine(json);
}
}
class Widget
{
public int Id { get; set; }
public string Name { get; set; }
public string LowerCaseName
{
get { return (Name != null ? Name.ToLower() : null); }
}
}
Here is the output of the above. Notice that the read-only property LowerCaseName is not included in the output.
{"Id":2,"Name":"Joe Schmoe"}
Use the OptIn mode of JSON.net and you'll only need to decorate the properties you want to serialize. This isn't as good as automatically opting out all read only properties, but it can save you some work.
[JsonObject(MemberSerialization.OptIn)]
public class MyClass
{
[JsonProperty]
public string serializedProp { get; set; }
public string nonSerializedProp { get; set; }
}
Udate: Added another possibility using reflection
If the above solution still isn't quite what you're looking for, you could use reflection to make dictionary objects which would then be serialized. Of course the example below will only work for simple classes, so you would need to add recursion if your classes contain other classes. This should at least point you in the right direction.
The subroutine to put the filtered result into a dictionary:
private Dictionary<String, object> ConvertToDictionary(object classToSerialize)
{
Dictionary<String, object> resultDictionary = new Dictionary<string, object>();
foreach (var propertyInfo in classToSerialize.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (propertyInfo.CanWrite) resultDictionary.Add(propertyInfo.Name, propertyInfo.GetValue(classToSerialize, null));
}
return resultDictionary;
}
A snippet showing its use:
SampleClass sampleClass = new SampleClass();
sampleClass.Hotkey = Keys.A;
var toSerialize = ConvertToDictionary(sampleClass);
String resultText = JsonConvert.SerializeObject(toSerialize);
You can use a contract resolver like this:
public class ExcludeCalculatedResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = _ => ShouldSerialize(member);
return property;
}
internal static bool ShouldSerialize(MemberInfo memberInfo)
{
var propertyInfo = memberInfo as PropertyInfo;
if (propertyInfo == null)
{
return false;
}
if (propertyInfo.SetMethod != null)
{
return true;
}
var getMethod = propertyInfo.GetMethod;
return Attribute.GetCustomAttribute(getMethod, typeof(CompilerGeneratedAttribute)) != null;
}
}
It will exclude calculated properties but include C#6 get only properties and all properties with a set method.
Json.net does have the ability to conditionally serialize properties without an attribute or contract resolver. This is especially useful if you don't want your project to have a dependency on Json.net.
As per the Json.net documentation
To conditionally serialize a property, add a method that returns boolean with
the same name as the property and then prefix the method name with
ShouldSerialize. The result of the method determines whether the
property is serialized. If the method returns true then the property
will be serialized, if it returns false then the property will be
skipped.
I'm currently trying to work through MVC validation, and am coming up against some problems where a field is required depending on the value of another field. An example is below (that I haven't figured out yet) - If the PaymentMethod == "Cheque", then the ChequeName should be required, otherwise it can be let through.
[Required(ErrorMessage = "Payment Method must be selected")]
public override string PaymentMethod
{ get; set; }
[Required(ErrorMessage = "ChequeName is required")]
public override string ChequeName
{ get; set; }
I'm using the System.ComponentModel.DataAnnotations for the [Required], and have also extended a ValidationAttribute to try and get this working, but I can't pass a variable through to do the validation (extension below)
public class JEPaymentDetailRequired : ValidationAttribute
{
public string PaymentSelected { get; set; }
public string PaymentType { get; set; }
public override bool IsValid(object value)
{
if (PaymentSelected != PaymentType)
return true;
var stringDetail = (string) value;
if (stringDetail.Length == 0)
return false;
return true;
}
}
Implementation:
[JEPaymentDetailRequired(PaymentSelected = PaymentMethod, PaymentType = "Cheque", ErrorMessage = "Cheque name must be completed when payment type of cheque")]
Has anyone had experience with this sort of validation? Would it just be better to write it into the controller?
Thanks for your help.
If you want client side validation in addition to model validation on the server, I think the best way to go is a custom validation attribute (like Jaroslaw suggested). I'm including the source here of the one I use.
Custom attribute:
public class RequiredIfAttribute : DependentPropertyAttribute
{
private readonly RequiredAttribute innerAttribute = new RequiredAttribute();
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue) : base(dependentProperty)
{
TargetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// get a reference to the property this validation depends upon
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty(DependentProperty);
if (field != null)
{
// get the value of the dependent property
var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);
// compare the value against the target value
if ((dependentvalue == null && TargetValue == null) ||
(dependentvalue != null && dependentvalue.Equals(TargetValue)))
{
// match => means we should try validating this field
if (!innerAttribute.IsValid(value))
// validation failed - return an error
return new ValidationResult(ErrorMessage, new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif"
};
var depProp = BuildDependentPropertyId(DependentProperty, metadata, context as ViewContext);
// find the value on the control we depend on;
// if it's a bool, format it javascript style
// (the default is True or False!)
var targetValue = (TargetValue ?? "").ToString();
if (TargetValue != null)
if (TargetValue is bool)
targetValue = targetValue.ToLower();
rule.ValidationParameters.Add("dependentproperty", depProp);
rule.ValidationParameters.Add("targetvalue", targetValue);
yield return rule;
}
}
Jquery validation extension:
$.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'targetvalue'], function (options) {
options.rules['requiredif'] = {
dependentproperty: options.params['dependentproperty'],
targetvalue: options.params['targetvalue']
};
options.messages['requiredif'] = options.message;
});
$.validator.addMethod('requiredif',
function (value, element, parameters) {
var id = '#' + parameters['dependentproperty'];
// get the target value (as a string,
// as that's what actual value will be)
var targetvalue = parameters['targetvalue'];
targetvalue = (targetvalue == null ? '' : targetvalue).toString();
// get the actual value of the target control
var actualvalue = getControlValue(id);
// if the condition is true, reuse the existing
// required field validator functionality
if (targetvalue === actualvalue) {
return $.validator.methods.required.call(this, value, element, parameters);
}
return true;
}
);
Decorating a property with the attribute:
[Required]
public bool IsEmailGiftCertificate { get; set; }
[RequiredIf("IsEmailGiftCertificate", true, ErrorMessage = "Please provide Your Email.")]
public string YourEmail { get; set; }
Just use the Foolproof validation library that is available on Codeplex:
https://foolproof.codeplex.com/
It supports the following "requiredif" validation attributes / decorations:
[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]
To get started is easy:
Download the package from the provided link
Add a reference to the included .dll file
Import the included javascript files
Ensure that your views references the included javascript files from within its HTML for unobtrusive javascript and jquery validation.
I would write the validation logic in the model, not the controller. The controller should only handle interaction between the view and the model. Since it's the model that requires validation, I think it's widely regarded as the place for validation logic.
For validation that depends on the value of another property or field, I (unfortunately) don't see how to completely avoid writing some code for that in the model, such as shown in the Wrox ASP.NET MVC book, sort of like:
public bool IsValid
{
get
{
SetRuleViolations();
return (RuleViolations.Count == 0);
}
}
public void SetRuleViolations()
{
if (this.PaymentMethod == "Cheque" && String.IsNullOrEmpty(this.ChequeName))
{
RuleViolations.Add("Cheque name is required", "ChequeName");
}
}
Doing all validation declaratively would be great. I'm sure you could make a RequiredDependentAttribute, but that would only handle this one type of logic. Stuff that is even slightly more complex would require yet another pretty specific attribute, etc. which gets crazy quickly.
Your problem can be solved relatively simply by the usage of conditional validation attribute e.g.
[RequiredIf("PaymentMethod == 'Cheque'")]
public string ChequeName { get; set; }