예제 #1
0
        /// <summary>
        /// Gets information about how the property value is stored in database (column name, type, size).
        /// </summary>
        /// <param name="prop">The property.</param>
        /// <returns></returns>
        private PropertyMapInfo GetPropertyMapInfo(SettingsProperty prop)
        {
            // Perform general validation
            if (prop == null)
            {
                throw new ArgumentNullException();
            }
            string cpd = System.Convert.ToString(prop.Attributes["CustomProviderData"]);

            if (string.IsNullOrEmpty(cpd))
            {
                throw new ProviderException(string.Format("CustomProviderData is missing or empty for property {0}.", prop.Name));
            }
            if (!System.Text.RegularExpressions.Regex.IsMatch(cpd, CustomProviderDataFormat))
            {
                throw new ProviderException(string.Format("Invalid format of CustomProviderData for property {0}.", prop.Name));
            }
            string[] parts = cpd.Split(';');

            PropertyMapInfo pmi = new PropertyMapInfo();

            pmi.ColumnName = parts[0];
            try {
                pmi.Type = (SqlDbType)Enum.Parse(typeof(SqlDbType), parts[1], true);
            }
            catch {
                throw new ProviderException(string.Format("SqlDbType '{0}' specified for property {1} is invalid.", parts[1], prop.Name));
            }
            if (parts.Length == 3)
            {
                pmi.Length = System.Convert.ToInt32(parts[2]);
            }
            return(pmi);
        }
예제 #2
0
 public ColumnExpression(Type type, TableAlias alias, string name, PropertyMapInfo map)
     : base(DbExpressionType.Column, type)
 {
     Alias   = alias;
     Name    = name;
     MapInfo = map;
 }
예제 #3
0
            protected override void OnConfiguring(EntityContextOptionsBuilder builder)
            {
                var map = new PropertyMapInfo {
                    IsPrimaryKey = true, DataType = System.Data.DbType.Boolean, Description = "dd"
                };
                var p = new GeneralProperty {
                    Info = map
                };

                PropertyUnity.RegisterProperty(typeof(SysUser), p);
                builder.UseSqlServer(connstr);
            }
예제 #4
0
        private static object RecursivelyMap(object value, PropertyMapInfo info)
        {
            if (!info.PropertyType.IsInstanceOfType(value))
            {
                // If the property value is an IPublishedContent, then we can map it to the target type.
                if (value is IPublishedContent content && info.PropertyType.IsClass)
                {
                    return(content.MapTo(info.PropertyType));
                }

                // If the property value is an IEnumerable<IPublishedContent>, then we can map it to the target type.
                if (value.GetType().IsEnumerableOfType(typeof(IPublishedContent)) && info.IsEnumerableType)
                {
                    Type genericType = info.PropertyType.GetEnumerableType();
                    if (genericType != null && genericType.IsClass)
                    {
                        return(((IEnumerable <IPublishedContent>)value).MapTo(genericType));
                    }
                }
            }

            return(value);
        }
예제 #5
0
        private Expression GetMemberExpressionFromCustomExpression(PropertyMapInfo lastWithCustExpression,
                                                                   PropertyMapInfo lastInList,
                                                                   List <PropertyMapInfo> beforeCustExpression,
                                                                   List <PropertyMapInfo> afterCustExpression,
                                                                   Expression visitedParentExpr)
        {
            return(PrependParentMemberExpression
                   (
                       new PrependParentNameVisitor
                       (
                           lastWithCustExpression.CustomExpression.Parameters[0] /*Parent parameter of current property*/,
                           BuildFullName(beforeCustExpression),
                           visitedParentExpr
                       )
                   ));

            Expression PrependParentMemberExpression(PrependParentNameVisitor visitor)
            => visitor.Visit
            (
                lastInList != lastWithCustExpression
                        ? lastWithCustExpression.CustomExpression.Body.MemberAccesses(afterCustExpression)
                        : lastWithCustExpression.CustomExpression.Body
            );
        }
예제 #6
0
        private static object MapProperty(PropertyMap <T> map, IPublishedContent content, object result)
        {
            // Users might want to use lazy loading with API controllers that do not inherit from UmbracoAPIController.
            // Certain mappers like Archtype require the context so we want to ensure it exists.
            EnsureUmbracoContext();

            object value = null;

            // If we have a mapping function, use that and skip Umbraco
            if (map.Info.HasPredicate)
            {
                value = map.Predicate.Invoke((T)result, content);
            }
            else
            {
                // Ensure the property mapper is always invoked first
                if (!(map.PropertyMapper is UmbracoPropertyMapper))
                {
                    value = new UmbracoPropertyMapper(map.Info).Map(content, null);
                }

                // Other mappers
                value = map.PropertyMapper.Map(content, value);
            }

            PropertyMapInfo info = map.Info;

            value = SantizeValue(value, info);

            if (value != null)
            {
                value = RecursivelyMap(value, info);
            }

            return(value);
        }
예제 #7
0
 protected Expression GetMemberExpressionFromCustomExpression(List <PropertyMapInfo> propertyMapInfoList, PropertyMapInfo lastWithCustExpression, Expression mappedParentExpr)
 => GetMemberExpressionFromCustomExpression
 (
     lastWithCustExpression,
     propertyMapInfoList.Last(),
     propertyMapInfoList.Aggregate(new List <PropertyMapInfo>(), (list, next) =>
 {
     if (propertyMapInfoList.IndexOf(next) < propertyMapInfoList.IndexOf(lastWithCustExpression))
     {
         list.Add(next);
     }
     return(list);
 }),
     propertyMapInfoList.Aggregate(new List <PropertyMapInfo>(), (list, next) =>
 {
     if (propertyMapInfoList.IndexOf(next) > propertyMapInfoList.IndexOf(lastWithCustExpression))
     {
         list.Add(next);
     }
     return(list);
 }),
     mappedParentExpr
 );
예제 #8
0
        /// <summary>
        /// Gets information about how the property value is stored in database (column name, type, size).
        /// </summary>
        /// <param name="prop">The property.</param>
        /// <returns></returns>
        private static PropertyMapInfo GetPropertyMapInfo(SettingsProperty prop)
        {
            // Perform general validation
            if (prop == null) throw new ArgumentNullException();
            string cpd = System.Convert.ToString(prop.Attributes["CustomProviderData"]);
            if (string.IsNullOrEmpty(cpd)) throw new ProviderException(string.Format("CustomProviderData is missing or empty for property {0}.", prop.Name));
            if (!System.Text.RegularExpressions.Regex.IsMatch(cpd, CustomProviderDataFormat)) throw new ProviderException(string.Format("Invalid format of CustomProviderData for property {0}.", prop.Name));
            string[] parts = cpd.Split(';');

            PropertyMapInfo pmi = new PropertyMapInfo();
            pmi.ColumnName = parts[0];
            try {
                pmi.Type = (SqlDbType)Enum.Parse(typeof(SqlDbType), parts[1], true);
            }
            catch {
                throw new ProviderException(string.Format("SqlDbType '{0}' specified for property {1} is invalid.", parts[1], prop.Name));
            }
            if (parts.Length == 3) pmi.Length = System.Convert.ToInt32(parts[2]);
            return pmi;
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="NuPickerEnumPropertyMapper"/> class.
 /// </summary>
 /// <param name="info">The property map information</param>
 public NuPickerEnumPropertyMapper(PropertyMapInfo info)
     : base(info)
 {
 }
예제 #10
0
        /// <summary>
        /// Sets the values of the specified group of property settings.
        /// </summary>
        /// <param name="context">A <see cref="T:System.Configuration.SettingsContext"/> describing the current application usage.</param>
        /// <param name="collection">A <see cref="T:System.Configuration.SettingsPropertyValueCollection"/> representing the group of property settings to set.</param>
        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
        {
            // Validate arguments
            if (!(bool)context["IsAuthenticated"])
            {
                throw new NotSupportedException("This provider does not support anonymous profiles");
            }
            string userName = (string)context["UserName"];

            if (string.IsNullOrEmpty(userName) || collection.Count == 0 || !this.HasDirtyProperties(collection))
            {
                return;                                                                                                  // no work here
            }
            // Construct command
            using (SqlCommand cmd = new SqlCommand()) {
                StringBuilder insertCommandText1 = new StringBuilder("INSERT INTO $Profiles ($UserName, $LastUpdate");
                StringBuilder insertCommandText2 = new StringBuilder(" VALUES (@UserName, GETDATE()");
                StringBuilder updateCommandText  = new StringBuilder("UPDATE $Profiles SET $LastUpdate=GETDATE()");
                cmd.Parameters.Add("@UserName", SqlDbType.VarChar, 100).Value = userName;

                // Cycle trough collection
                int i = 0;
                foreach (SettingsPropertyValue propVal in collection)
                {
                    PropertyMapInfo pmi = GetPropertyMapInfo(propVal.Property);

                    // Always add parameter
                    SqlParameter p = new SqlParameter("@Param" + i, pmi.Type);
                    if (pmi.Length != 0)
                    {
                        p.Size = pmi.Length;
                    }
                    if (propVal.Deserialized && propVal.PropertyValue == null)
                    {
                        p.Value = DBNull.Value;
                    }
                    else
                    {
                        p.Value = propVal.PropertyValue;
                    }
                    cmd.Parameters.Add(p);

                    // Always add to insert
                    insertCommandText1.Append(", " + pmi.ColumnName);
                    insertCommandText2.Append(", @Param" + i);

                    // Add dirty properties to update
                    if (propVal.IsDirty)
                    {
                        updateCommandText.Append(", " + pmi.ColumnName + "=@Param" + i);
                    }

                    i++;
                }

                // Complete command
                insertCommandText1.Append(")");
                insertCommandText2.Append(")");
                updateCommandText.Append(" WHERE $UserName=@UserName");
                cmd.CommandText = this.ExpandCommand("IF EXISTS (SELECT * FROM $Profiles WHERE $UserName=@UserName) BEGIN " + updateCommandText.ToString() + " END ELSE BEGIN " + insertCommandText1.ToString() + insertCommandText2.ToString() + " END");

                // Execute command
                try {
                    using (HostingEnvironment.Impersonate())
                        using (SqlConnection db = OpenDatabase()) {
                            cmd.Connection = db;
                            cmd.ExecuteNonQuery();
                        }
                }
                catch {
                    throw;
                }
            }
        }
예제 #11
0
        private static object SantizeValue(object value, PropertyMapInfo info)
        {
            bool propertyIsCastableEnumerable    = info.IsCastableEnumerableType;
            bool propertyIsConvertableEnumerable = info.IsConvertableEnumerableType;

            if (value != null)
            {
                Type valueType = value.GetType();
                if (valueType == info.PropertyType)
                {
                    return(value);
                }

                bool valueIsConvertableEnumerable = valueType.IsConvertableEnumerableType();

                // You cannot set an enumerable of type from an empty object array.
                // This should allow the casting back of IEnumerable<T> to an empty List<T> Collection<T> etc.
                // I cant think of any that don't have an empty constructor
                if (value.Equals(Enumerable.Empty <object>()) && propertyIsCastableEnumerable)
                {
                    Type typeArg = info.PropertyType.GetTypeInfo().GenericTypeArguments.First();
                    return(info.PropertyType.IsInterface ? EnumerableInvocations.Cast(typeArg, (IEnumerable)value) : info.PropertyType.GetInstance());
                }

                // Ensure only a single item is returned when requested.
                if (valueIsConvertableEnumerable && !propertyIsConvertableEnumerable)
                {
                    // Property is not enumerable, but value is, so grab first item
                    IEnumerator enumerator = ((IEnumerable)value).GetEnumerator();
                    return(enumerator.MoveNext() ? enumerator.Current : null);
                }

                // And now check for the reverse situation.
                if (!valueIsConvertableEnumerable && propertyIsConvertableEnumerable)
                {
                    var array = Array.CreateInstance(value.GetType(), 1);
                    array.SetValue(value, 0);
                    return(array);
                }
            }
            else
            {
                if (propertyIsCastableEnumerable)
                {
                    if (info.PropertyType.IsInterface && !info.IsEnumerableOfKeyValueType)
                    {
                        // Value is null, but property is enumerable interface, so return empty enumerable
                        return(EnumerableInvocations.Empty(info.PropertyType.GenericTypeArguments.First()));
                    }

                    // Concrete enumerables cannot be cast from Array so we create an instance and return it
                    // if we know it has an empty constructor.
                    ParameterInfo[] constructorParams = info.PropertyType.GetConstructorParameters();
                    if (constructorParams != null && constructorParams.Length == 0)
                    {
                        // Internally this uses Activator.CreateInstance which is heavily optimized.
                        return(info.PropertyType.GetInstance());
                    }
                }
            }

            return(value);
        }
예제 #12
0
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.NodeType == ExpressionType.Constant)
            {
                return(base.VisitMember(node));
            }

            string sourcePath = null;

            ParameterExpression parameterExpression = node.GetParameterExpression();

            if (parameterExpression == null)
            {
                return(base.VisitMember(node));
            }

            infoDictionary.Add(parameterExpression, this.TypeMappings);

            Type sType = parameterExpression.Type;

            if (infoDictionary.ContainsKey(parameterExpression) && node.IsMemberExpression())
            {
                sourcePath = node.GetPropertyFullName();
            }
            else
            {
                return(base.VisitMember(node));
            }

            List <PropertyMapInfo> propertyMapInfoList = new List <PropertyMapInfo>();

            FindDestinationFullName(sType, infoDictionary[parameterExpression].DestType, sourcePath, propertyMapInfoList);
            string fullName = null;

            if (propertyMapInfoList.Any(x => x.CustomExpression != null))
            {
                PropertyMapInfo        last = propertyMapInfoList.Last(x => x.CustomExpression != null);
                List <PropertyMapInfo> beforeCustExpression = propertyMapInfoList.Aggregate(new List <PropertyMapInfo>(), (list, next) =>
                {
                    if (propertyMapInfoList.IndexOf(next) < propertyMapInfoList.IndexOf(last))
                    {
                        list.Add(next);
                    }
                    return(list);
                });

                List <PropertyMapInfo> afterCustExpression = propertyMapInfoList.Aggregate(new List <PropertyMapInfo>(), (list, next) =>
                {
                    if (propertyMapInfoList.IndexOf(next) > propertyMapInfoList.IndexOf(last))
                    {
                        list.Add(next);
                    }
                    return(list);
                });

                fullName = BuildFullName(beforeCustExpression);
                PrependParentNameVisitor visitor = new PrependParentNameVisitor(last.CustomExpression.Parameters[0].Type /*Parent type of current property*/, fullName, infoDictionary[parameterExpression].NewParameter);

                Expression ex = propertyMapInfoList[propertyMapInfoList.Count - 1] != last
                    ? visitor.Visit(last.CustomExpression.Body.AddExpressions(afterCustExpression))
                    : visitor.Visit(last.CustomExpression.Body);

                return(ex);
            }
            else
            {
                fullName = BuildFullName(propertyMapInfoList);
                MemberExpression me = infoDictionary[parameterExpression].NewParameter.BuildExpression(fullName);
                return(me);
            }
        }
예제 #13
0
        protected void FindDestinationFullName(Type typeSource, Type typeDestination, string sourceFullName, List <PropertyMapInfo> propertyMapInfoList)
        {
            const string PERIOD = ".";

            if (typeSource == typeDestination)
            {
                string[] sourceFullNameArray = sourceFullName.Split(new char[] { PERIOD[0] }, StringSplitOptions.RemoveEmptyEntries);
                propertyMapInfoList = sourceFullNameArray.Aggregate(propertyMapInfoList, (list, next) =>
                {
                    if (list.Count == 0)
                    {
                        AddPropertyMapInfo(typeSource, next, list);
                    }
                    else
                    {
                        PropertyMapInfo last = list[list.Count - 1];
                        AddPropertyMapInfo(last.CustomExpression == null
                            ? last.DestinationPropertyInfos[last.DestinationPropertyInfos.Count - 1].GetMemberType()
                            : last.CustomExpression.ReturnType, next, list);
                    }
                    return(list);
                });
                return;
            }

            TypeMap typeMap = this.ConfigurationProvider.ResolveTypeMap(typeDestination, typeSource);//The destination becomes the source because to map a source expression to a destination expression,

            //we need the expressions used to create the source from the destination

            if (sourceFullName.IndexOf(PERIOD) < 0)
            {
                PropertyMap propertyMap      = typeMap.GetPropertyMaps().SingleOrDefault(item => item.DestinationProperty.Name == sourceFullName);
                MemberInfo  sourceMemberInfo = typeSource.GetMember(propertyMap.DestinationProperty.Name).First();
                if (propertyMap.ValueResolverConfig != null)
                {
                    #region Research
                    #endregion

                    throw new InvalidOperationException(Resource.customResolversNotSupported);
                }
                else if (propertyMap.CustomExpression != null)
                {
                    if (propertyMap.CustomExpression.ReturnType.IsValueType() && sourceMemberInfo.GetMemberType() != propertyMap.CustomExpression.ReturnType)
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.expressionMapValueTypeMustMatchFormat, propertyMap.CustomExpression.ReturnType.Name, propertyMap.CustomExpression.ToString(), sourceMemberInfo.GetMemberType().Name, propertyMap.DestinationProperty.Name));
                    }
                }
                else
                {
                    if (propertyMap.SourceMember.GetMemberType().IsValueType() && sourceMemberInfo.GetMemberType() != propertyMap.SourceMember.GetMemberType())
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.expressionMapValueTypeMustMatchFormat, propertyMap.SourceMember.GetMemberType().Name, propertyMap.SourceMember.Name, sourceMemberInfo.GetMemberType().Name, propertyMap.DestinationProperty.Name));
                    }
                }

                propertyMapInfoList.Add(new PropertyMapInfo(propertyMap.CustomExpression, propertyMap.SourceMembers.ToList()));
            }
            else
            {
                string      propertyName = sourceFullName.Substring(0, sourceFullName.IndexOf(PERIOD));
                PropertyMap propertyMap  = typeMap.GetPropertyMaps().SingleOrDefault(item => item.DestinationProperty.Name == propertyName);

                MemberInfo sourceMemberInfo = typeSource.GetMember(propertyMap.DestinationProperty.Name).First();
                if (propertyMap.CustomExpression == null && propertyMap.SourceMember == null)//If sourceFullName has a period then the SourceMember cannot be null.  The SourceMember is required to find the ProertyMap of its child object.
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.srcMemberCannotBeNullFormat, typeSource.Name, typeDestination.Name, propertyName));
                }

                propertyMapInfoList.Add(new PropertyMapInfo(propertyMap.CustomExpression, propertyMap.SourceMembers.ToList()));
                string childFullName = sourceFullName.Substring(sourceFullName.IndexOf(PERIOD) + 1);

                FindDestinationFullName(sourceMemberInfo.GetMemberType(), propertyMap.CustomExpression == null
                    ? propertyMap.SourceMember.GetMemberType()
                    : propertyMap.CustomExpression.ReturnType, childFullName, propertyMapInfoList);
            }
        }
예제 #14
0
        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
        {
            if (!(bool)context["IsAuthenticated"])
            {
                throw new NotSupportedException("This provider does not support anonymous profiles");
            }
            string username = (string)context["UserName"];

            if (String.IsNullOrEmpty(username) || collection.Count == 0 || !this.HasDirtyProperties(collection))
            {
                return;
            }

            StringBuilder updateValues  = new StringBuilder();
            StringBuilder insertColumns = new StringBuilder();
            StringBuilder insertValues  = new StringBuilder();
            SqlCommand    cmd           = new SqlCommand();

            int i = 0;

            foreach (SettingsPropertyValue value in collection)
            {
                PropertyMapInfo pmi = GetPropertyMapInfo(value.Property);

                SqlParameter p = new SqlParameter("@Param" + i, pmi.Type);
                if (pmi.Length != 0)
                {
                    p.Size = pmi.Length;
                }
                if (value.Deserialized && value.PropertyValue == null)
                {
                    p.Value = DBNull.Value;
                }
                else
                {
                    p.Value = value.PropertyValue;
                }

                cmd.Parameters.Add(p);

                insertColumns.Append(", " + pmi.ColumnName);
                insertValues.Append(", @Param" + i);

                if (value.IsDirty)
                {
                    updateValues.AppendFormat(", {0} = @Param{1}", pmi.ColumnName, i);
                }

                i++;
            }

            using (SqlConnection cn = OpenConnection())
            {
                string sql = this.ExpandCommand(SQL_SetPropertyValues);
                sql             = sql.Replace("$UpdateValues", updateValues.ToString());
                sql             = sql.Replace("$InsertColumns", insertColumns.ToString());
                sql             = sql.Replace("$InsertValues", insertValues.ToString());
                cmd.Connection  = cn;
                cmd.CommandText = sql;
                cmd.Parameters.Add("@UserName", SqlDbType.VarChar, CustomMembershipProvider.UserNameSize).Value = username;
                cmd.ExecuteNonQuery();
            }
        }
예제 #15
0
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.NodeType == ExpressionType.Constant)
            {
                return(base.VisitMember(node));
            }

            string sourcePath = null;

            ParameterExpression parameterExpression = node.GetParameterExpression();

            if (parameterExpression == null)
            {
                return(base.VisitMember(node));
            }

            InfoDictionary.Add(parameterExpression, this.TypeMappings);
            Type sType = parameterExpression.Type;

            if (sType != null && InfoDictionary.ContainsKey(parameterExpression) && node.IsMemberExpression())
            {
                sourcePath = node.GetPropertyFullName();
            }
            else
            {
                return(base.VisitMember(node));
            }

            List <PropertyMapInfo> propertyMapInfoList = new List <PropertyMapInfo>();

            FindDestinationFullName(sType, InfoDictionary[parameterExpression].DestType, sourcePath, propertyMapInfoList);
            string fullName = null;

            if (propertyMapInfoList.Any(x => x.CustomExpression != null))//CustomExpression takes precedence over DestinationPropertyInfo
            {
                PropertyMapInfo        last = propertyMapInfoList.Last(x => x.CustomExpression != null);
                List <PropertyMapInfo> beforeCustExpression = propertyMapInfoList.Aggregate(new List <PropertyMapInfo>(), (list, next) =>
                {
                    if (propertyMapInfoList.IndexOf(next) < propertyMapInfoList.IndexOf(last))
                    {
                        list.Add(next);
                    }
                    return(list);
                });

                List <PropertyMapInfo> afterCustExpression = propertyMapInfoList.Aggregate(new List <PropertyMapInfo>(), (list, next) =>
                {
                    if (propertyMapInfoList.IndexOf(next) > propertyMapInfoList.IndexOf(last))
                    {
                        list.Add(next);
                    }
                    return(list);
                });


                fullName = BuildFullName(beforeCustExpression);

                PrependParentNameVisitor visitor = new PrependParentNameVisitor(last.CustomExpression.Parameters[0].Type /*Parent type of current property*/, fullName, InfoDictionary[parameterExpression].NewParameter);

                Expression ex = propertyMapInfoList[propertyMapInfoList.Count - 1] != last
                    ? visitor.Visit(last.CustomExpression.Body.AddExpressions(afterCustExpression))
                    : visitor.Visit(last.CustomExpression.Body);

                FindMemberExpressionsVisitor v = new FindMemberExpressionsVisitor(InfoDictionary[parameterExpression].NewParameter);
                v.Visit(ex);

                return(v.Result);
            }
            else
            {
                fullName = BuildFullName(propertyMapInfoList);
                MemberExpression me = InfoDictionary[parameterExpression].NewParameter.BuildExpression(fullName);
                if (me.Expression.NodeType == ExpressionType.MemberAccess && (me.Type == typeof(string) || me.Type.GetTypeInfo().IsValueType || (me.Type.GetTypeInfo().IsGenericType &&
                                                                                                                                                 me.Type.GetGenericTypeDefinition().Equals(typeof(Nullable <>)) &&
                                                                                                                                                 Nullable.GetUnderlyingType(me.Type).GetTypeInfo().IsValueType)))
                {
                    return(me.Expression);
                }

                return(me);
            }
        }
예제 #16
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ArchetypeFactoryPropertyMapper"/> class.
 /// </summary>
 /// <param name="info">The property map information</param>
 public ArchetypeFactoryPropertyMapper(PropertyMapInfo info)
     : base(info)
 {
 }