Reading the documentation regarding SqlBulkCopy , especially SqlBulkCopyOptions , I would draw the same conclusion as you: SQL Server should be "smart" enough to use the default constraint, where applicable, especially since you do not use the SqlBulkCopyOptions.KeepNulls attribute.
However, in this case, I suspect that the documentation is subtly incorrect; if not mistaken, this, of course, is misleading.
As you noticed, with a non-empty field with a default constraint (in this case GetDate() ) SqlBulkCopy fails with the above error.
As a test, try creating a second table that mimics the first, but this time make the CreatedOn and LastUpdatedOn null. In my tests, using the default parameters ( SqlBulkCopyOptions.Default ), the process works without errors and CreatedOn and LastUpdatedOn , both have the correct DateTime value populated in the table, despite the DataTable values for these fields were DBNull.Value .
As another test, using the same table with zero fields, run SqlBulkCopy only this time using the SqlBulkCopyOptions.KeepNulls attribute. I suspect that you will see the same results as me, that is, CreatedOn and LastUpdatedOn are zero in the table.
This behavior is similar to executing the T-SQL “vanilla” statement to insert data into a table.
As an example, you can use the source table (fields with a non-empty value) if you execute
INSERT INTO csvrf_References ([Type], [Location], [Description], [CreatedOn], [LastUpdatedOn], [LastUpdatedUser]) VALUES ('test', 'test', 'test', null, null, null)
you will get a similar error regarding null values that are not allowed in the table.
However, if you omit non-zero fields, the SQL Server statement uses the default restrictions for these fields from the statement:
INSERT INTO csvrf_References ([Type], [Location], [Description] VALUES ('test', 'test', 'still testing')
Based on this, I would suggest either to make the fields nullable in the table (in my opinion, not a good option), or to use the "intermediate" table for the SqlBulkCopy process (where the fields are NULL and have similar default values in place). After the data is in the staging table, run the second statement to move the data to the actual destination destination table.