/// <summary> /// Add to the navigation property map based on attributes on the class member. Checks a list of known annotations. /// </summary> /// <param name="memberInfo">Property or field of the class for which metadata is being generated</param> /// <param name="nmap">Navigation property definition</param> /// <param name="describer">The current EntityDescriptor</param> private static void AddAttributesToNavProperty(MemberInfo memberInfo, Dictionary <string, object> nmap, EntityDescriptor describer) { var validators = new List <Dictionary <string, object> >(); var attributes = memberInfo.GetCustomAttributes(); foreach (var attr in attributes) { var name = attr.GetType().Name; if (name.EndsWith("Attribute")) { // get the name without "Attribute" on the end name = name.Substring(0, name.Length - "Attribute".Length); } if (name == "ForeignKey") { var names = new string[] { GetAttributeValue(attr, "Name").ToString() }; nmap["foreignKeyNamesOnServer"] = names; } else if (name == "InverseProperty") { nmap["custom"] = new Dictionary <string, object> { { "inverseProperty", GetAttributeValue(attr, "Property") } }; } else if (name == "Reference") { // ServiceStack: attribute indicates a navigation property } else if (attr is ValidationAttribute) { // Mapping custom validation attributes to client validators var validator = describer.MapValidationAttribute((ValidationAttribute)attr, nmap); if (validator == null && attr is RegularExpressionAttribute) { var pattern = GetAttributeValue(attr, "Pattern"); validator = new[] { new Dictionary <string, object> { { "name", "regularExpression" }, { "expression", pattern } } }; } if (validator != null) { validators.AddRange(validator); } } else { describer.MapCustomAttribute(attr, nmap); } } validators = describer.PostProcessValidators(validators, nmap).ToList(); if (validators.Any()) { nmap.Add("validators", validators); } }
/// <summary> /// Add the metadata for an entity. /// </summary> /// <param name="type">Type for which metadata is being generated</param> void AddType(Type type) { // "Customer:#Breeze.Models.NorthwindIBModel": { var classKey = type.Name + ":#" + type.Namespace; var cmap = new Dictionary <string, object>(); _typeList.Add(cmap); _typeMap.Add(type, cmap); cmap.Add("shortName", type.Name); cmap.Add("namespace", type.Namespace); if (!type.IsInterface) { var interfaces = type.GetInterfaces().Except(GetBaseType(type).GetInterfaces()).Where(t => _types.Contains(t)).Select(t => t.Name).ToList(); if (interfaces.Any()) { var custom = new Dictionary <string, object>() { { "interfaces", string.Join(", ", interfaces) } }; cmap.Add("custom", custom); } } if (_describer.IsComplexType(type)) { cmap.Add("isComplexType", true); } else { // Only identify the base type if it is also an entity in the type list if (_entityTypes.Contains(GetBaseType(type))) { var baseTypeName = GetBaseType(type).Name + ":#" + GetBaseType(type).Namespace; cmap.Add("baseTypeName", baseTypeName); } if (type.IsAbstract) { cmap.Add("isAbstract", true); } // Get the autoGeneratedKeyType for this type var keyGenerator = _describer.GetAutoGeneratedKeyType(type); if (keyGenerator != null) { cmap.Add("autoGeneratedKeyType", keyGenerator); } var resourceName = _describer.GetResourceName(type); cmap.Add("defaultResourceName", resourceName); _resourceMap.Add(resourceName, classKey); } var dataList = new List <Dictionary <string, object> >(); cmap.Add("dataProperties", dataList); AddDataProperties(type, dataList); // Change the autoGeneratedKeyType if an attribute was found on a data property var keyProp = FindEntry(dataList, "isPartOfKey"); if (keyProp != null) { var custom = keyProp.Get("custom"); if ("Identity".Equals(custom)) { cmap["autoGeneratedKeyType"] = "Identity"; keyProp.Remove("custom"); } else if ("Computed".Equals(custom)) { cmap["autoGeneratedKeyType"] = "KeyGenerator"; keyProp.Remove("custom"); } } else if (!type.IsAbstract && !type.IsEnum && !_describer.IsComplexType(type) && !_entityTypes.Contains(GetBaseType(type))) { // No key for an entity => error or add the key var missingFKHandling = _describer.GetMissingPKHandling(type); if (missingFKHandling == MissingKeyHandling.Error) { throw new Exception("Key not found for entity " + classKey); } else if (missingFKHandling == MissingKeyHandling.Add) { var dmap = new Dictionary <string, object>(); dmap.Add("nameOnServer", type.Name + "GenKey"); dmap.Add("dataType", "Guid"); // TODO make this configurable dmap.Add("isPartOfKey", true); dmap.Add("custom", "pk_generated"); dataList.Add(dmap); } else { Console.Error.WriteLine("Key not found for entity " + classKey); } } // Validators var validators = new List <Dictionary <string, object> >(); var attributes = type.GetCustomAttributes(); foreach (var attr in attributes) { if (attr is ValidationAttribute) { var validator = _describer.MapValidationAttribute((ValidationAttribute)attr, cmap); if (validator != null) { validators.AddRange(validator); } } else { _describer.MapCustomAttribute(attr, cmap); } } validators = _describer.PostProcessValidators(validators, cmap).ToList(); if (validators.Any()) { cmap.Add("validators", validators); } }
/// <summary> /// Add to the data property map based on attributes on the class member. Checks a list of known annotations. /// </summary> /// <param name="memberInfo">Property or field of the class for which metadata is being generated</param> /// <param name="dmap">Data property definition</param> /// <param name="describer">The current EntityDescriptor</param> private static void AddAttributesToDataProperty(MemberInfo memberInfo, Dictionary <string, object> dmap, EntityDescriptor describer) { var validators = new List <Dictionary <string, object> >(); var attributes = memberInfo.GetCustomAttributes(); foreach (var attr in attributes) { var name = attr.GetType().Name; if (name.EndsWith("Attribute")) { // get the name without "Attribute" on the end name = name.Substring(0, name.Length - "Attribute".Length); } if (name == "Key" || name == "PrimaryKey") { dmap["isPartOfKey"] = true; } else if (name == "ConcurrencyCheck") { dmap["concurrencyMode"] = "Fixed"; } else if (name == "Required") { dmap["isNullable"] = false; var validator = new Dictionary <string, object>() { { "name", "required" } }; validators.Add(validator); } else if (name == "DefaultValue") { dmap["defaultValue"] = GetAttributeValue(attr, "Value"); } else if (name == "MaxLength") { var max = GetAttributeValue(attr, "Length"); dmap["maxLength"] = max; var validator = new Dictionary <string, object>() { { "name", "maxLength" }, { "maxLength", max } }; validators.Add(validator); } else if (name == "StringLength") { // ServiceStack [StringLength(max, min)] var max = GetAttributeValue(attr, "MaximumLength"); dmap["maxLength"] = max; var validator = new Dictionary <string, object>() { { "name", "maxLength" }, { "maxLength", max } }; validators.Add(validator); var min = (int)GetAttributeValue(attr, "MinimumLength"); if (min > 0) { dmap["minLength"] = min; var minValidator = new Dictionary <string, object>() { { "name", "minLength" }, { "minLength", min } }; validators.Add(minValidator); } } else if (name == "DatabaseGenerated") { var opt = GetAttributeValue(attr, "DatabaseGeneratedOption").ToString(); if (opt != "None") { dmap["custom"] = opt; } } else if (name == "ForeignKey") { var relatedType = GetAttributeValue(attr, "Type"); if (relatedType != null) { // ServiceStack: foreign key points to related type dmap["__relatedType"] = relatedType; } else { // DataAnnotation: ForeignKey points to navigation property name // will be resolved & removed while processing navigation properties dmap["__foreignKey"] = GetAttributeValue(attr, "Name"); } } else if (name == "References") { // ServiceStack: like a foreign key, but no actual db fk relationship exists var relatedType = GetAttributeValue(attr, "Type"); if (relatedType != null) { // ServiceStack: foreign key points to related type dmap["__relatedType"] = relatedType; } } else if (name == "InverseProperty") { dmap["custom"] = new Dictionary <string, object> { { "inverseProperty", GetAttributeValue(attr, "Property") } }; } else if (attr is ValidationAttribute) { // Mapping custom validation attributes to client validators var validator = describer.MapValidationAttribute((ValidationAttribute)attr, dmap); if (validator == null && attr is RegularExpressionAttribute) { var pattern = GetAttributeValue(attr, "Pattern"); validator = new[] { new Dictionary <string, object> { { "name", "regularExpression" }, { "expression", pattern } } }; } if (validator != null) { validators.AddRange(validator); } } else { describer.MapCustomAttribute(attr, dmap); } } validators = describer.PostProcessValidators(validators, dmap).ToList(); if (validators.Any()) { dmap.Add("validators", validators); } }