コード例 #1
0
 /// <summary>
 /// Create a new handler instance.
 /// </summary>
 public NetTopologySuiteGeographyHandler()
 {
     _writer = new SqlServerBytesWriter {
         IsGeography = true
     };
     _reader = new SqlServerBytesReader {
         IsGeography = true
     };
 }
コード例 #2
0
        protected override byte[] FromGeometry(object value)
        {
            var geometry = value as Geometry;

            if (geometry == null)
            {
                return(null);
            }

            SetDefaultSRID(geometry);
            var writer = new SqlServerBytesWriter {
                IsGeography = IsGeography
            };
            var bytes = writer.Write(geometry);

            return(bytes);
        }
コード例 #3
0
        /// <summary>
        /// Common logic for two versions of GetDataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="context"></param>
        /// <param name="type"></param>
        /// <param name="entities"></param>
        /// <param name="tableInfo"></param>
        /// <returns></returns>
        private static DataTable InnerGetDataTable <T>(DbContext context, ref Type type, IList <T> entities, TableInfo tableInfo)
        {
            var dataTable   = new DataTable();
            var columnsDict = new Dictionary <string, object>();
            var ownedEntitiesMappedProperties = new HashSet <string>();

            var isSqlServer          = context.Database.ProviderName.EndsWith(DbServer.SqlServer.ToString());
            var sqlServerBytesWriter = new SqlServerBytesWriter
            {
                IsGeography = true
            };

            var objectIdentifier = tableInfo.ObjectIdentifier;

            type = tableInfo.HasAbstractList ? entities[0].GetType() : type;
            var entityType           = context.Model.FindEntityType(type);
            var entityTypeProperties = entityType.GetProperties();
            var entityPropertiesDict = entityTypeProperties.Where(a => tableInfo.PropertyColumnNamesDict.ContainsKey(a.Name) ||
                                                                  (tableInfo.BulkConfig.OperationType != OperationType.Read && a.Name == tableInfo.TimeStampPropertyName))
                                       .ToDictionary(a => a.Name, a => a);
            var entityNavigationOwnedDict    = entityType.GetNavigations().Where(a => a.TargetEntityType.IsOwned()).ToDictionary(a => a.Name, a => a);
            var entityShadowFkPropertiesDict = entityTypeProperties.Where(a => a.IsShadowProperty() &&
                                                                          a.IsForeignKey() &&
                                                                          a.GetContainingForeignKeys().FirstOrDefault()?.DependentToPrincipal?.Name != null)
                                               .ToDictionary(a => a.Name, a => a);
            var entityShadowFkPropertyColumnNamesDict = entityShadowFkPropertiesDict.ToDictionary(a => a.Key, a => a.Value.GetColumnName(objectIdentifier));
            var shadowPropertyColumnNamesDict         = entityPropertiesDict.Where(a => a.Value.IsShadowProperty()).ToDictionary(a => a.Key, a => a.Value.GetColumnName(objectIdentifier));

            var properties          = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            var discriminatorColumn = GetDiscriminatorColumn(tableInfo);

            foreach (var property in properties)
            {
                if (entityPropertiesDict.ContainsKey(property.Name))
                {
                    var    propertyEntityType = entityPropertiesDict[property.Name];
                    string columnName         = propertyEntityType.GetColumnName(objectIdentifier);

                    var isConvertible = tableInfo.ConvertibleColumnConverterDict.ContainsKey(columnName);
                    var propertyType  = isConvertible ? tableInfo.ConvertibleColumnConverterDict[columnName].ProviderClrType : property.PropertyType;

                    var underlyingType = Nullable.GetUnderlyingType(propertyType);
                    if (underlyingType != null)
                    {
                        propertyType = underlyingType;
                    }

                    if (isSqlServer && (propertyType == typeof(Geometry) || propertyType.IsSubclassOf(typeof(Geometry))))
                    {
                        propertyType             = typeof(byte[]);
                        tableInfo.HasSpatialType = true;
                        if (tableInfo.BulkConfig.PropertiesToIncludeOnCompare != null || tableInfo.BulkConfig.PropertiesToIncludeOnCompare != null)
                        {
                            throw new InvalidOperationException("OnCompare properties Config can not be set for Entity with Spatial types like 'Geometry'");
                        }
                    }

                    if (!columnsDict.ContainsKey(property.Name))
                    {
                        dataTable.Columns.Add(columnName, propertyType);
                        columnsDict.Add(property.Name, null);
                    }
                }
                else if (entityShadowFkPropertiesDict.ContainsKey(property.Name))
                {
                    var fk = entityShadowFkPropertiesDict[property.Name];
                    entityPropertiesDict.TryGetValue(fk.GetColumnName(objectIdentifier), out var entityProperty);
                    if (entityProperty == null) // BulkRead
                    {
                        continue;
                    }

                    var columnName     = entityProperty.GetColumnName(objectIdentifier);
                    var propertyType   = entityProperty.ClrType;
                    var underlyingType = Nullable.GetUnderlyingType(propertyType);
                    if (underlyingType != null)
                    {
                        propertyType = underlyingType;
                    }

                    if (propertyType == typeof(Geometry) && isSqlServer)
                    {
                        propertyType = typeof(byte[]);
                    }

                    if (!columnsDict.ContainsKey(property.Name))
                    {
                        dataTable.Columns.Add(columnName, propertyType);
                        columnsDict.Add(columnName, null);
                    }
                }
                else if (entityNavigationOwnedDict.ContainsKey(property.Name)) // isOWned
                {
                    Type navOwnedType = type.Assembly.GetType(property.PropertyType.FullName);

                    var ownedEntityType = context.Model.FindEntityType(property.PropertyType);
                    if (ownedEntityType == null)
                    {
                        ownedEntityType = context.Model.GetEntityTypes().SingleOrDefault(a => a.DefiningNavigationName == property.Name && a.DefiningEntityType.Name == entityType.Name);
                    }
                    var ownedEntityProperties = ownedEntityType.GetProperties().ToList();
                    var ownedEntityPropertyNameColumnNameDict = new Dictionary <string, string>();

                    foreach (var ownedEntityProperty in ownedEntityProperties)
                    {
                        if (!ownedEntityProperty.IsPrimaryKey())
                        {
                            string columnName = ownedEntityProperty.GetColumnName(objectIdentifier);
                            if (tableInfo.PropertyColumnNamesDict.ContainsValue(columnName))
                            {
                                ownedEntityPropertyNameColumnNameDict.Add(ownedEntityProperty.Name, columnName);
                                ownedEntitiesMappedProperties.Add(property.Name + "_" + ownedEntityProperty.Name);
                            }
                        }
                    }

                    var innerProperties = property.PropertyType.GetProperties();
                    if (!tableInfo.LoadOnlyPKColumn)
                    {
                        foreach (var innerProperty in innerProperties)
                        {
                            if (ownedEntityPropertyNameColumnNameDict.ContainsKey(innerProperty.Name))
                            {
                                var columnName   = ownedEntityPropertyNameColumnNameDict[innerProperty.Name];
                                var propertyName = $"{property.Name}_{innerProperty.Name}";

                                if (tableInfo.ConvertibleColumnConverterDict.ContainsKey(propertyName))
                                {
                                    var convertor      = tableInfo.ConvertibleColumnConverterDict[propertyName];
                                    var underlyingType = Nullable.GetUnderlyingType(convertor.ProviderClrType) ?? convertor.ProviderClrType;
                                    dataTable.Columns.Add(columnName, underlyingType);
                                }
                                else
                                {
                                    var ownedPropertyType = Nullable.GetUnderlyingType(innerProperty.PropertyType) ?? innerProperty.PropertyType;
                                    dataTable.Columns.Add(columnName, ownedPropertyType);
                                }

                                columnsDict.Add(property.Name + "_" + innerProperty.Name, null);
                            }
                        }
                    }
                }
            }

            if (tableInfo.BulkConfig.EnableShadowProperties)
            {
                foreach (var shadowProperty in entityPropertiesDict.Values.Where(a => a.IsShadowProperty()))
                {
                    var columnName = shadowProperty.GetColumnName(objectIdentifier);

                    // If a model has an entity which has a relationship without an explicity defined FK, the data table will already contain the foreign key shadow property
                    if (dataTable.Columns.Contains(columnName))
                    {
                        continue;
                    }

                    var isConvertible = tableInfo.ConvertibleColumnConverterDict.ContainsKey(columnName);
                    var propertyType  = isConvertible ? tableInfo.ConvertibleColumnConverterDict[columnName].ProviderClrType : shadowProperty.ClrType;

                    var underlyingType = Nullable.GetUnderlyingType(propertyType);
                    if (underlyingType != null)
                    {
                        propertyType = underlyingType;
                    }

                    if (isSqlServer && (propertyType == typeof(Geometry) || propertyType.IsSubclassOf(typeof(Geometry))))
                    {
                        propertyType = typeof(byte[]);
                    }

                    dataTable.Columns.Add(columnName, propertyType);
                    columnsDict.Add(shadowProperty.Name, null);
                }
            }

            if (discriminatorColumn != null)
            {
                dataTable.Columns.Add(discriminatorColumn, typeof(string));
                columnsDict.Add(discriminatorColumn, type.Name);
            }
            bool hasConverterProperties = tableInfo.ConvertiblePropertyColumnDict.Count > 0;

            foreach (var entity in entities)
            {
                foreach (var property in properties)
                {
                    var propertyValue = tableInfo.FastPropertyDict.ContainsKey(property.Name) ? tableInfo.FastPropertyDict[property.Name].Get(entity) : null;

                    if (tableInfo.BulkConfig.DateTime2PrecisionForceRound && isSqlServer && tableInfo.DateTime2PropertiesPrecisionLessThen7Dict.ContainsKey(property.Name))
                    {
                        DateTime dateTimePropertyValue = (DateTime)propertyValue;

                        int precision      = tableInfo.DateTime2PropertiesPrecisionLessThen7Dict[property.Name];
                        int digitsToRemove = 7 - precision;
                        int powerOf10      = (int)Math.Pow(10, digitsToRemove);

                        long subsecondTicks = dateTimePropertyValue.Ticks % 10000000;
                        long ticksToRound   = subsecondTicks + (subsecondTicks % 10 == 0 ? 1 : 0); // if ends with 0 add 1 tick to make sure rounding of value .5_zeros is rounded to Upper like SqlServer is doing, not to Even as Math.Round works
                        int  roundedTicks   = Convert.ToInt32(Math.Round((decimal)ticksToRound / powerOf10, 0)) * powerOf10;
                        dateTimePropertyValue = dateTimePropertyValue.AddTicks(-subsecondTicks).AddTicks(roundedTicks);

                        propertyValue = dateTimePropertyValue;
                    }

                    if (hasConverterProperties && tableInfo.ConvertiblePropertyColumnDict.ContainsKey(property.Name))
                    {
                        string columnName = tableInfo.ConvertiblePropertyColumnDict[property.Name];
                        propertyValue = tableInfo.ConvertibleColumnConverterDict[columnName].ConvertToProvider.Invoke(propertyValue);
                    }

                    if (tableInfo.HasSpatialType && propertyValue is Geometry geometryValue)
                    {
                        geometryValue.SRID = tableInfo.BulkConfig.SRID;
                        propertyValue      = sqlServerBytesWriter.Write(geometryValue);
                    }

                    if (entityPropertiesDict.ContainsKey(property.Name))
                    {
                        columnsDict[property.Name] = propertyValue;
                    }
                    else if (entityShadowFkPropertiesDict.ContainsKey(property.Name))
                    {
                        var foreignKeyShadowProperty = entityShadowFkPropertiesDict[property.Name];
                        var columnName = entityShadowFkPropertyColumnNamesDict[property.Name];
                        entityPropertiesDict.TryGetValue(columnName, out var entityProperty);
                        if (entityProperty == null) // BulkRead
                        {
                            continue;
                        }

                        columnsDict[columnName] = propertyValue == null ? null : foreignKeyShadowProperty.FindFirstPrincipal().PropertyInfo.GetValue(propertyValue); // TODO Check if can be optimized
                    }
                    else if (entityNavigationOwnedDict.ContainsKey(property.Name) && !tableInfo.LoadOnlyPKColumn)
                    {
                        var ownedProperties = property.PropertyType.GetProperties().Where(a => ownedEntitiesMappedProperties.Contains(property.Name + "_" + a.Name));
                        foreach (var ownedProperty in ownedProperties)
                        {
                            var columnName         = $"{property.Name}_{ownedProperty.Name}";
                            var ownedPropertyValue = propertyValue == null ? null : tableInfo.FastPropertyDict[columnName].Get(propertyValue);

                            if (tableInfo.ConvertibleColumnConverterDict.ContainsKey(columnName))
                            {
                                var converter = tableInfo.ConvertibleColumnConverterDict[columnName];
                                columnsDict[columnName] = ownedPropertyValue == null ? null : converter.ConvertToProvider.Invoke(ownedPropertyValue);
                            }
                            else
                            {
                                columnsDict[columnName] = ownedPropertyValue;
                            }
                        }
                    }
                }

                if (tableInfo.BulkConfig.EnableShadowProperties)
                {
                    foreach (var shadowPropertyName in shadowPropertyColumnNamesDict.Keys)
                    {
                        var shadowProperty = entityPropertiesDict[shadowPropertyName];
                        var columnName     = shadowPropertyColumnNamesDict[shadowPropertyName];
                        var propertyValue  = context.Entry(entity).Property(shadowPropertyName).CurrentValue;

                        if (tableInfo.ConvertibleColumnConverterDict.ContainsKey(columnName))
                        {
                            propertyValue = tableInfo.ConvertibleColumnConverterDict[columnName].ConvertToProvider.Invoke(propertyValue);
                        }

                        columnsDict[shadowPropertyName] = propertyValue;
                    }
                }

                var record = columnsDict.Values.ToArray();
                dataTable.Rows.Add(record);
            }

            return(dataTable);
        }
コード例 #4
0
 /// <summary>
 ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
 ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
 ///     any release. You should only use it directly in your code with extreme caution and knowing that
 ///     doing so can result in application failures when updating to a new Entity Framework Core release.
 /// </summary>
 public GeometryValueConverter(SqlServerBytesReader reader, SqlServerBytesWriter writer)
     : base(
         g => new SqlBytes(writer.Write(g)),
         b => (TGeometry)reader.Read(b.Value))
 {
 }
コード例 #5
0
        /// <summary>
        /// Common logic for two versions of GetDataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="context"></param>
        /// <param name="type"></param>
        /// <param name="entities"></param>
        /// <param name="tableInfo"></param>
        /// <returns></returns>
        private static DataTable InnerGetDataTable <T>(DbContext context, ref Type type, IList <T> entities, TableInfo tableInfo)
        {
            var dataTable   = new DataTable();
            var columnsDict = new Dictionary <string, object>();
            var ownedEntitiesMappedProperties = new HashSet <string>();

            var isSqlServer          = context.Database.ProviderName.EndsWith(DbServer.SqlServer.ToString());
            var sqlServerBytesWriter = new SqlServerBytesWriter
            {
                IsGeography = true
            };

            type = tableInfo.HasAbstractList ? entities[0].GetType() : type;
            var entityType                   = context.Model.FindEntityType(type);
            var entityTypeProperties         = entityType.GetProperties();
            var entityPropertiesDict         = entityTypeProperties.Where(a => tableInfo.PropertyColumnNamesDict.ContainsKey(a.Name)).ToDictionary(a => a.Name, a => a);
            var entityNavigationOwnedDict    = entityType.GetNavigations().Where(a => a.GetTargetType().IsOwned()).ToDictionary(a => a.Name, a => a);
            var entityShadowFkPropertiesDict = entityTypeProperties.Where(a =>
                                                                          a.IsShadowProperty() &&
                                                                          a.IsForeignKey() &&
                                                                          a.GetContainingForeignKeys().FirstOrDefault()?.DependentToPrincipal?.Name != null)
                                               .ToDictionary(x => x.Name, a => a);

            var properties          = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            var discriminatorColumn = GetDiscriminatorColumn(tableInfo);

            foreach (var property in properties)
            {
                if (entityPropertiesDict.ContainsKey(property.Name))
                {
                    var    propertyEntityType = entityPropertiesDict[property.Name];
                    string columnName         = propertyEntityType.GetColumnName();

                    var isConvertible = tableInfo.ConvertibleProperties.ContainsKey(columnName);
                    var propertyType  = isConvertible ? tableInfo.ConvertibleProperties[columnName].ProviderClrType : property.PropertyType;

                    var underlyingType = Nullable.GetUnderlyingType(propertyType);
                    if (underlyingType != null)
                    {
                        propertyType = underlyingType;
                    }

                    if (isSqlServer && (propertyType == typeof(Geometry) || propertyType.IsSubclassOf(typeof(Geometry))))
                    {
                        propertyType             = typeof(byte[]);
                        tableInfo.HasSpatialType = true;
                        if (tableInfo.BulkConfig.PropertiesToIncludeOnCompare != null || tableInfo.BulkConfig.PropertiesToIncludeOnCompare != null)
                        {
                            throw new InvalidOperationException("OnCompare properties Config can not be set for Entity with Spatial types like 'Geometry'");
                        }
                    }

                    if (!columnsDict.ContainsKey(property.Name))
                    {
                        dataTable.Columns.Add(columnName, propertyType);
                        columnsDict.Add(property.Name, null);
                    }
                }
                else if (entityShadowFkPropertiesDict.ContainsKey(property.Name))
                {
                    var fk = entityShadowFkPropertiesDict[property.Name];
                    entityPropertiesDict.TryGetValue(fk.GetColumnName(), out var entityProperty);
                    if (entityProperty == null) // BulkRead
                    {
                        continue;
                    }

                    var columnName     = entityProperty.GetColumnName();
                    var propertyType   = entityProperty.ClrType;
                    var underlyingType = Nullable.GetUnderlyingType(propertyType);
                    if (underlyingType != null)
                    {
                        propertyType = underlyingType;
                    }

                    if (propertyType == typeof(Geometry) && isSqlServer)
                    {
                        propertyType = typeof(byte[]);
                    }

                    if (!columnsDict.ContainsKey(property.Name))
                    {
                        dataTable.Columns.Add(columnName, propertyType);
                        columnsDict.Add(columnName, null);
                    }
                }
                else if (entityNavigationOwnedDict.ContainsKey(property.Name)) // isOWned
                {
                    Type navOwnedType = type.Assembly.GetType(property.PropertyType.FullName);

                    var ownedEntityType = context.Model.FindEntityType(property.PropertyType);
                    if (ownedEntityType == null)
                    {
                        ownedEntityType = context.Model.GetEntityTypes().SingleOrDefault(a => a.DefiningNavigationName == property.Name && a.DefiningEntityType.Name == entityType.Name);
                    }
                    var ownedEntityProperties = ownedEntityType.GetProperties().ToList();
                    var ownedEntityPropertyNameColumnNameDict = new Dictionary <string, string>();

                    foreach (var ownedEntityProperty in ownedEntityProperties)
                    {
                        if (!ownedEntityProperty.IsPrimaryKey())
                        {
                            string columnName = ownedEntityProperty.GetColumnName();
                            if (tableInfo.PropertyColumnNamesDict.ContainsValue(columnName))
                            {
                                ownedEntityPropertyNameColumnNameDict.Add(ownedEntityProperty.Name, columnName);
                                ownedEntitiesMappedProperties.Add(property.Name + "_" + ownedEntityProperty.Name);
                            }
                        }
                    }

                    var innerProperties = property.PropertyType.GetProperties();
                    if (!tableInfo.LoadOnlyPKColumn)
                    {
                        foreach (var innerProperty in innerProperties)
                        {
                            if (ownedEntityPropertyNameColumnNameDict.ContainsKey(innerProperty.Name))
                            {
                                var columnName   = ownedEntityPropertyNameColumnNameDict[innerProperty.Name];
                                var propertyName = $"{property.Name}_{innerProperty.Name}";

                                if (tableInfo.ConvertibleProperties.ContainsKey(propertyName))
                                {
                                    var convertor      = tableInfo.ConvertibleProperties[propertyName];
                                    var underlyingType = Nullable.GetUnderlyingType(convertor.ProviderClrType) ?? convertor.ProviderClrType;
                                    dataTable.Columns.Add(columnName, underlyingType);
                                }
                                else
                                {
                                    var ownedPropertyType = Nullable.GetUnderlyingType(innerProperty.PropertyType) ?? innerProperty.PropertyType;
                                    dataTable.Columns.Add(columnName, ownedPropertyType);
                                }

                                columnsDict.Add(property.Name + "_" + innerProperty.Name, null);
                            }
                        }
                    }
                }
            }

            if (tableInfo.BulkConfig.EnableShadowProperties)
            {
                foreach (var sp in entityPropertiesDict.Values.Where(y => y.IsShadowProperty()))
                {
                    var columnName = sp.GetColumnName();

                    // If a model has an entity which has a relationship without an explicity defined FK, the data table will already contain the foreign key shadow property
                    if (dataTable.Columns.Contains(columnName))
                    {
                        continue;
                    }

                    var isConvertible = tableInfo.ConvertibleProperties.ContainsKey(columnName);
                    var propertyType  = isConvertible ? tableInfo.ConvertibleProperties[columnName].ProviderClrType : sp.ClrType;

                    var underlyingType = Nullable.GetUnderlyingType(propertyType);
                    if (underlyingType != null)
                    {
                        propertyType = underlyingType;
                    }

                    if (isSqlServer && (propertyType == typeof(Geometry) || propertyType.IsSubclassOf(typeof(Geometry))))
                    {
                        propertyType = typeof(byte[]);
                    }

                    dataTable.Columns.Add(columnName, propertyType);
                    columnsDict.Add(sp.Name, null);
                }
            }

            if (discriminatorColumn != null)
            {
                dataTable.Columns.Add(discriminatorColumn, typeof(string));
                columnsDict.Add(discriminatorColumn, type.Name);
            }

            foreach (var entity in entities)
            {
                foreach (var property in properties)
                {
                    var propertyValue = tableInfo.FastPropertyDict.ContainsKey(property.Name) ? tableInfo.FastPropertyDict[property.Name].Get(entity) : null;

                    if (entityPropertiesDict.ContainsKey(property.Name))
                    {
                        string columnName = entityPropertiesDict[property.Name].GetColumnName();
                        if (tableInfo.ConvertibleProperties.ContainsKey(columnName))
                        {
                            propertyValue = tableInfo.ConvertibleProperties[columnName].ConvertToProvider.Invoke(propertyValue);
                        }
                    }

                    if (propertyValue is Geometry geometryValue && isSqlServer)
                    {
                        geometryValue.SRID = tableInfo.BulkConfig.SRID;
                        propertyValue      = sqlServerBytesWriter.Write(geometryValue);
                    }

                    if (entityPropertiesDict.ContainsKey(property.Name))
                    {
                        columnsDict[property.Name] = propertyValue;
                    }
                    else if (entityShadowFkPropertiesDict.ContainsKey(property.Name))
                    {
                        var fk         = entityShadowFkPropertiesDict[property.Name];
                        var columnName = fk.GetColumnName();
                        entityPropertiesDict.TryGetValue(fk.GetColumnName(), out var entityProperty);
                        if (entityProperty == null) // BulkRead
                        {
                            continue;
                        }

                        columnsDict[columnName] = propertyValue == null ? null : fk.FindFirstPrincipal().PropertyInfo.GetValue(propertyValue);
                    }
                    else if (entityNavigationOwnedDict.ContainsKey(property.Name) && !tableInfo.LoadOnlyPKColumn)
                    {
                        var ownedProperties = property.PropertyType.GetProperties().Where(a => ownedEntitiesMappedProperties.Contains(property.Name + "_" + a.Name));
                        foreach (var ownedProperty in ownedProperties)
                        {
                            var columnName         = $"{property.Name}_{ownedProperty.Name}";
                            var ownedPropertyValue = propertyValue == null ? null : tableInfo.FastPropertyDict[columnName].Get(propertyValue);

                            if (tableInfo.ConvertibleProperties.ContainsKey(columnName))
                            {
                                var converter = tableInfo.ConvertibleProperties[columnName];
                                columnsDict[columnName] = ownedPropertyValue == null ? null : converter.ConvertToProvider.Invoke(ownedPropertyValue);
                            }
                            else
                            {
                                columnsDict[columnName] = ownedPropertyValue;
                            }
                        }
                    }
                }

                if (tableInfo.BulkConfig.EnableShadowProperties)
                {
                    foreach (var sp in entityPropertiesDict.Values.Where(y => y.IsShadowProperty()))
                    {
                        var propertyValue = context.Entry(entity).Property(sp.Name).CurrentValue;
                        var columnName    = sp.GetColumnName();

                        if (tableInfo.ConvertibleProperties.ContainsKey(columnName))
                        {
                            propertyValue = tableInfo.ConvertibleProperties[columnName].ConvertToProvider.Invoke(propertyValue);
                        }

                        columnsDict[sp.Name] = propertyValue;
                    }
                }

                var record = columnsDict.Values.ToArray();
                dataTable.Rows.Add(record);
            }

            return(dataTable);
        }