1

I know the method below isn't meant to work with any Entity, and its use shouldn't be enforced.

I found a System.Data.Linq.Table extension method which uses SqlBulkCopy to insert data. I am trying to adapt it to the Entity Framework but it throws a strange exception, while the original works for Linq-To-Sql data classes. I couldn't find the flaw so far, it happens with any SQL table, in a 1-1 mapping. Could you help me?

public static class ObjectQueryExtensions
{
    public static string GetName<TEntity>(
        this ObjectQuery<TEntity> objectQuery)
        where TEntity : class
    {
        var tableNameGroup = new Regex(@"FROM\s([^\s]*)\s"
            , RegexOptions.IgnoreCase);
        var sql = objectQuery.ToTraceString();
        var tableNameGroupMatch = tableNameGroup.Match(sql);
        return tableNameGroupMatch.Groups[1].Value;
    }

    public static void BulkInsert<TEntity>(
          this ObjectQuery<TEntity> objectQuery
        , IEnumerable<TEntity> items)
        where TEntity : class
    {
        using (var dt = new DataTable())
        {
            var properties = typeof(TEntity)
                .GetProperties()
                .Where(property => property.Name  != "EntityKey")
                .Where(property => property.Name  != "EntityState")
                ;
            foreach (var property in properties)
            {
                dt.Columns.Add(property.Name
                   , Nullable.GetUnderlyingType(property.PropertyType)
                   ?? property.PropertyType);
            }

            foreach (var t in items)
            {
                DataRow row = dt.NewRow();
                foreach (var info in properties)
                {
                    row[info.Name] = info.GetValue(t, null) ?? DBNull.Value;
                }
                dt.Rows.Add(row);
            }

            var entityConnection = (EntityConnection)objectQuery
                .Context.Connection;
            using (var sqlBulkCopy = new SqlBulkCopy(
                 entityConnection.StoreConnection.ConnectionString))
            {
                sqlBulkCopy.DestinationTableName = objectQuery.GetName();
                sqlBulkCopy.WriteToServer(dt);
            }
        }
    }
}

The exception

Test method LinqExtensionsTest.ObjectQueryExtensionsTest.BulkInsertTest threw exception: System.InvalidOperationException: The given value of type Int64 from the data source cannot be converted to type datetime of the specified target column. ---> System.InvalidCastException: Failed to convert parameter value from a Int64 to a DateTime. ---> System.InvalidCastException: Invalid cast from 'Int64' to 'DateTime'..

The stack trace

System.Int64.System.IConvertible.ToDateTime(IFormatProvider provider) System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) System.Data.SqlClient.SqlParameter.CoerceValue(Object value, MetaType destinationType) System.Data.SqlClient.SqlParameter.CoerceValue(Object value, MetaType destinationType) System.Data.SqlClient.SqlBulkCopy.ConvertValue(Object value, _SqlMetaData metadata) System.Data.SqlClient.SqlBulkCopy.ConvertValue(Object value, _SqlMetaData metadata) System.Data.SqlClient.SqlBulkCopy.WriteToServerInternal() System.Data.SqlClient.SqlBulkCopy.WriteRowSourceToServer(Int32 columnCount) System.Data.SqlClient.SqlBulkCopy.WriteToServer(DataTable table, DataRowState rowState) System.Data.SqlClient.SqlBulkCopy.WriteToServer(DataTable table) LinqExtensions.ObjectQueryExtensions.BulkInsert[TEntity](ObjectQuery1 objectQuery, IEnumerable1 items) in LinqExtensions\LinqExtensions\ObjectQueryExtensions.cs: line 60 LinqExtensionsTest.ObjectQueryExtensionsTest.BulkInsertTest() in LinqExtensions\LinqExtensionsTest\ObjectQueryExtensionsTest.cs: line 88

Jader Dias
  • 88,211
  • 155
  • 421
  • 625

2 Answers2

3

I know that you created your DataTable from your entities, but my suggestion is to check if the order of the columns in the DataTable is the same of the columns in the table (maybe you changed your database and didn't update your model). If it is not the same you might have mismatching data type and bump into the invalid cast error. It happened to me ;)

  • That's true, if you do any modification on the database that not is mirrored on the edmx file it will lead you on that problem. It the column order on the edmx not match the order on the database table will lead you on that kind of exception. – John Prado Sep 10 '14 at 04:02
  • A solution to different column orders in entities and databases is [to use column mappings](https://stackoverflow.com/a/2340053/12534). – Christian Davén Dec 13 '17 at 11:44
0

The exception you got says pretty much it all: in your entity object you have a property of type Int64 which is defined as DateTime in the database, and there is no way to implicitly convertfrom one to another. Do you really intend to represent that database column as an integer? Maybe it is just a mistake in the definition of the entity class.

Konamiman
  • 49,681
  • 17
  • 108
  • 138
  • Your answer is the most obvious, but I find hard to believe in it since I used the Visual Studio Entity Designer to create the entity class, and it wouldn't have such a bug. – Jader Dias Oct 27 '09 at 12:52
  • I also rechecked every field in the DataTable and in the Sql Profiler and your suspicions were not confirmed. – Jader Dias Oct 27 '09 at 12:57
  • It's clearly a type error, and it's not coming from the designer. But it's a type error nonetheless. It's not in the code you've shown here, but it's in your project. So you have to look up the stack and find the error. – Craig Stuntz Oct 27 '09 at 13:29
  • I would have to step into the .NET code to find out how to circumvent this problem. – Jader Dias Oct 27 '09 at 17:40