예제 #1
0
        /// <summary>
        /// Creates a new TableInfo for the specified type, with optional table mapper.
        /// </summary>
        /// <param name="type">The entity <see cref="Type"/>.</param>
        /// <param name="tableNameMapper">A delegate for how to generate the table name from the <paramref name="type"/>.</param>
        /// <exception cref="ArgumentNullException"><paramref name="type"/></exception>
        public TableInfo(Type type, TableNameMapperDelegate tableNameMapper)
        {
            ClassType = type ?? throw new ArgumentNullException(nameof(type));

            if (tableNameMapper != null)
            {
                TableName = TableNameMapper(type);
            }
            else
            {
                var tableAttr = type.GetCustomAttributes(false).SingleOrDefaultOfType("TableAttribute");

                if (tableAttr != null)
                {
                    TableName = tableAttr.Name;
                    if (tableAttr.Schema != null)
                    {
                        SchemaName = tableAttr.Schema;
                    }
                }
                else
                {
                    TableName = type.Name + "s";
                    if (type.IsInterface && TableName.StartsWith("I"))
                    {
                        TableName = TableName.Substring(1);
                    }
                }
            }

            Columns = type.GetProperties()
                      .Where(typeProperty => !typeProperty.GetCustomAttributes(false).AnyOfType <IgnoreAttribute>())
                      .Select(typeProperty =>
            {
                var attributes           = typeProperty.GetCustomAttributes(false);
                var columnAtt            = attributes.SingleOrDefaultOfType("ColumnAttribute");
                var seqAtt               = attributes.SingleOrDefaultOfType <SequenceAttribute>();
                var genAtt               = attributes.SingleOrDefaultOfType <DatabaseGeneratedAttribute>();
                var hasReadOnlyAttribute = attributes.AnyOfType <ReadOnlyAttribute>();

                // Microsoft implies that TimestampAttribute is equivalent to ConcurrencyCheck + IsGenerated.
                // @see https://www.learnentityframeworkcore.com/configuration/data-annotation-attributes/timestamp-attribute
                var hasTimestampAttribute = attributes.OfType <TimestampAttribute>().Any();

                var ci = new ColumnInfo
                {
                    Property     = typeProperty,
                    ColumnName   = columnAtt?.Name ?? typeProperty.Name,
                    PropertyName = typeProperty.Name,
                    IsKey        = attributes.AnyOfType <KeyAttribute>(),
                    IsIdentity   = genAtt?.DatabaseGeneratedOption == DatabaseGeneratedOption.Identity ||
                                   seqAtt != null,
                    IsGenerated = genAtt != null && genAtt.DatabaseGeneratedOption != DatabaseGeneratedOption.None ||
                                  seqAtt != null ||
                                  hasTimestampAttribute,
                    IsConcurrencyToken = hasTimestampAttribute ||
                                         attributes.AnyOfType <ConcurrencyCheckAttribute>(),
                    ExcludeOnSelect = attributes.AnyOfType <IgnoreSelectAttribute>(),
                    SequenceName    = seqAtt?.Name
                };

                ci.IsNullable = !ci.IsKey &&                                   // do not allow Keys to be nullable
                                !attributes.AnyOfType <RequiredAttribute>() && // Required cannot be null. LATER: do we want to validate empty values? Using this for pre-C# 8 nullable enforcement
                                ci.Property.IsNullable();

                ci.ExcludeOnInsert = ci.IsGenerated && seqAtt == null ||
                                     attributes.AnyOfType <IgnoreInsertAttribute>() ||
                                     hasReadOnlyAttribute;

                ci.ExcludeOnUpdate = ci.IsGenerated ||
                                     attributes.AnyOfType <IgnoreUpdateAttribute>() ||
                                     hasReadOnlyAttribute;

                if (!ci.IsGenerated)
                {
                    return(ci);
                }

                var parameter  = Expression.Parameter(type);
                var property   = Expression.Property(parameter, ci.Property);
                var conversion = Expression.Convert(property, typeof(object));
                var lambda     = Expression.Lambda(conversion, parameter);
                ci.Output      = lambda;

                return(ci);
            })
                      .ToArray();

            if (!Columns.Any(columnInfo => columnInfo.IsKey))
            {
                var idProp = Columns.FirstOrDefault(columnInfo =>
                                                    string.Equals(columnInfo.PropertyName, "id", StringComparison.CurrentCultureIgnoreCase));

                if (idProp != null)
                {
                    idProp.IsKey          = idProp.IsGenerated =
                        idProp.IsIdentity = idProp.ExcludeOnInsert = idProp.ExcludeOnUpdate = true;
                }
            }

            _insertColumns =
                new Lazy <IEnumerable <ColumnInfo> >(() => Columns.Where(ci => !ci.ExcludeOnInsert), true);
            _updateColumns =
                new Lazy <IEnumerable <ColumnInfo> >(() => Columns.Where(ci => !ci.ExcludeOnUpdate), true);
            _selectColumns =
                new Lazy <IEnumerable <ColumnInfo> >(() => Columns.Where(ci => !ci.ExcludeOnSelect), true);
            _keyColumns              = new Lazy <IEnumerable <ColumnInfo> >(() => Columns.Where(ci => ci.IsKey), true);
            _generatedColumns        = new Lazy <IEnumerable <ColumnInfo> >(() => Columns.Where(ci => ci.IsGenerated), true);
            _concurrencyCheckColumns = new Lazy <IEnumerable <ColumnInfo> >(() => Columns.Where(ci => ci.IsConcurrencyToken), true);
            _comparisonColumns       = new Lazy <IEnumerable <ColumnInfo> >(() => Columns.Where(ci => ci.IsKey || ci.IsConcurrencyToken), true);
            _propertyList            = new Lazy <IEnumerable <PropertyInfo> >(() => Columns.Select(ci => ci.Property), true);
        }
예제 #2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="type"></param>
        /// <param name="tablenameMapper"></param>
        public TableInfo(Type type, TableNameMapperDelegate tablenameMapper)
        {
            ClassType = type;

            if (tablenameMapper != null)
            {
                TableName = TableNameMapper(type);
            }
            else
            {
                //NOTE: This as dynamic trick should be able to handle both our own Table-attribute as well as the one in EntityFramework
                var tableAttr = type
#if NETSTANDARD1_3
                                .GetTypeInfo()
#endif
                                .GetCustomAttributes(false).SingleOrDefault(attr => attr.GetType().Name == "TableAttribute") as dynamic;

                if (tableAttr != null)
                {
                    TableName = tableAttr.Name;
                    if (tableAttr.Schema != null)
                    {
                        SchemaName = tableAttr.Schema;
                    }
                }
                else
                {
                    TableName = type.Name + "s";
                    if (type.IsInterface() && TableName.StartsWith("I"))
                    {
                        TableName = TableName.Substring(1);
                    }
                }
            }

            ColumnInfos = type.GetProperties()
                          .Where(t => t.GetCustomAttributes(typeof(IgnoreAttribute), false).Count() == 0)
                          .Select(t =>
            {
                var columnAtt = t.GetCustomAttributes(false).SingleOrDefault(attr => attr.GetType().Name == "ColumnAttribute") as dynamic;

                var ci = new ColumnInfo
                {
                    Property     = t,
                    ColumnName   = columnAtt?.Name ?? t.Name,
                    PropertyName = t.Name,
                    IsKey        = t.GetCustomAttributes(true).Any(a => a is KeyAttribute),
                    IsIdentity   = t.GetCustomAttributes(true).Any(a => a is DatabaseGeneratedAttribute &&
                                                                   (a as DatabaseGeneratedAttribute).DatabaseGeneratedOption == DatabaseGeneratedOption.Identity),
                    IsGenerated = t.GetCustomAttributes(true).Any(a => a is DatabaseGeneratedAttribute &&
                                                                  (a as DatabaseGeneratedAttribute).DatabaseGeneratedOption != DatabaseGeneratedOption.None),
                    ExcludeOnSelect = t.GetCustomAttributes(true).Any(a => a is IgnoreSelectAttribute)
                };

                ci.ExcludeOnInsert = ci.IsGenerated ||
                                     t.GetCustomAttributes(true).Any(a => a is IgnoreInsertAttribute) ||
                                     t.GetCustomAttributes(true).Any(a => a is ReadOnlyAttribute);

                ci.ExcludeOnUpdate = ci.IsGenerated ||
                                     t.GetCustomAttributes(true).Any(a => a is IgnoreUpdateAttribute) ||
                                     t.GetCustomAttributes(true).Any(a => a is ReadOnlyAttribute);

                return(ci);
            })
                          .ToArray();

            if (!ColumnInfos.Any(k => k.IsKey))
            {
                var idProp = ColumnInfos.FirstOrDefault(p => string.Equals(p.PropertyName, "id", StringComparison.CurrentCultureIgnoreCase));

                if (idProp != null)
                {
                    idProp.IsKey = idProp.IsGenerated = idProp.IsIdentity = idProp.ExcludeOnInsert = idProp.ExcludeOnUpdate = true;
                }
            }
        }