Ejemplo n.º 1
0
        /// <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(type.BaseType.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(type.BaseType))
                {
                    var baseTypeName = type.BaseType.Name + ":#" + type.BaseType.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(type.BaseType))
            {
                // 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);
                    if (validator != null)
                    {
                        validators.Add(validator);
                    }
                }
            }

            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);
                    if (validator == null && attr is RegularExpressionAttribute)
                    {
                        var pattern = GetAttributeValue(attr, "Pattern");
                        validator = new Dictionary <string, object> {
                            { "name", "regularExpression" }, { "expression", pattern }
                        };
                    }

                    if (validator != null)
                    {
                        validators.Add(validator);
                    }
                }
            }

            if (validators.Any())
            {
                dmap.Add("validators", validators);
            }
        }