Column does not allow DBNull.Value - No KeepNulls - Proper Column Mappings

Simply put, you can't do what you want. The best reference for how the BulkCopy works with default values is This Answer by Rutzky.

The problem is, BulkCopy includes a step where it queries the target database and determines the structure of the table. If it determines that the target column is NOT NULLable, and you are passing null or DBNull, it throws an exception before even trying to pass the data.

If you use SQL Profiler, you'll see the BCP Calls, but not the data (The data would never show up anyway). All you'll see is the call to define the Column List and Flags.

When the BulkCopy finally decides to pass the data along. If the column exists, and the field is NULLable, and the value is DBNull.Value, and the column has a Default Value; Bulk Copy essentially passes the DEFAULT flag along for that column. But some decision was made such that those conditions, except if the field is NOT NULLable, that the default value should not be used and instead an exception should be thrown.

As far as I can tell this is a bug or oversight by Microsoft.

The common workaround, as some other answers state, is to just handle those values manually by calculating what the value should be in-code. Of course, if you calculate default values, then the DBA changes the actual SQL Default Value for a field, your systems won't match. The next step is to add a subsystem to your system that queries and/or tracks/caches the currently specified default values from the SQL Server you're hitting, and assigning those. That's way more work than should be required.

TLDR: You can't do what you want. But there are sub-optimal workarounds that others have specified.


This combination does not work for SqlBulkCopy:

  1. The date field in the SQL table is configured as not null.
  2. Your C# DataTable has records with unset date values (or has set values but they are not actual date/time values).

This combination does work:

  1. The date field in the SQL table is configured to allow nulls but use a default value.
  2. Your C# DataTable has records with unset date values (SQL will use the default value when the date field is not specified in the C# DataTable).

If the date field in your table MUST be configured to not accept null, then you have more work to do (create a staging table that accepts nulls, bulk insert into the staging table, call a stored proc that you write that inserts from the staging table into your regular table).

The simplest path to what I think you're looking for is this:

Your table should allow NULL for the date field but with a default:

CREATE TABLE [dbo].[MyTable](
    [Field1] [varchar](50) NULL,
    [MyDateField] [datetime] NULL,
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Table_1_MyDateField] DEFAULT (getdate()) FOR [MyDateField]
GO

Then in C#:

        DataTable dt = new DataTable("dbo.MyTable");

        dt.Columns.Add("Field1");
        dt.Columns.Add("MyDateField");

        DataRow row1 = dt.NewRow();
        row1["Field1"] = "test row 1";
        row1["MyDateField"] = DateTime.Now; //specify a value for the date field in C#
        dt.Rows.Add(row1);

        DataRow row2 = dt.NewRow();
        row2["Field1"] = "test row 2";
        //do not specify a value for the date field - SQL will use the default value
        dt.Rows.Add(row2);

        do the bulk copy