I was working with a simple entity class with LINQ to SQL (SQL Server 2005 SP3 x64).
[Table( Name="TBL_REGISTRATION" )]
public sealed class Registration : IDataErrorInfo
{
[Column( Name = "TBL_REGISTRATION_PK", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert )]
public Guid RegistrationID { get; private set; }
/* other properties ommited for brevity */
}
There are only two somewhat interesting things here:
- The class and property names aren't the same as the table and column names
- The primary key is a Guid (uniqueidentifier)
Here's what the table looks like:
create table dbo.TBL_REGISTRATION
(
TBL_REGISTRATION_PK uniqueidentifier primary key clustered
rowguidcol
default newid(),
/* other columns ommited for brevity */
)
When I attach this entity to my table and submit changes on my DataContext, the LINQ stack throws back a SqlException:
SqlException (0x80131904): Invalid column name 'RegistrationID'
LINQ seems to be ignoring the Column( Name = "TBL_REGISTRATION_PK" ) attribute on my RegistrationID property. I spent a while futzing with different attribute decorations trying to get it to work. In the end I settled on a private TBL_REGISTRATION_PK property to wrap my RegistrationID property to make LINQ happy.
[Table( Name="TBL_REGISTRATION" )]
public sealed class Registration : IDataErrorInfo
{
public Guid RegistrationID { get; private set; }
[Column( IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert )]
private Guid TBL_REGISTRATION_PK { get { return RegistrationID; } set { RegistrationID = value; } }
/* other properties ommited for brevity */
}
This works.
Why didn't it work the first way? Am I doing something wrong here or is this a LINQ defect?
This is a bug in Linq-to-SQL. It is fixed in .net 4.0.
See Connect #381883:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=381883
Your property needs 'private' removed from 'private set;' When you make the short hand properties in VS 2008 without implementing get/set, the compiler creates private member variables for you (of which name who knows). The Storage option in ColumnAttribute specifies which private member to use.
Linq to SQL doesn't know how to set the property if you mark the setter private and have the getter public (don't ask me why). If you wish to make your property readonly, make a private member variable like you did above.
You can clean it up by writing it like the following:
[Table( Name="TBL_REGISTRATION" )]
public sealed class Registration : IDataErrorInfo
{
public Guid RegistrationID { get { return _registrationID; } }
[Column( IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
private Guid _registrationID;
}
Have you tried using the Storage property?
[Table( Name="TBL_REGISTRATION" )]
public sealed class Registration : IDataErrorInfo
{
[Column( Name="TBL_REGISTRATION_PK", Storage="_RegistrationID", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert )]
public Guid RegistrationID { get { return _RegistrationID; } set { _RegistrationID = value; } }
private Guid _RegistrationID;
/* other properties ommited for brevity */
}
See also Attribute-Based Mapping (LINQ to SQL)
Use the "Storage" Property, with the name of the column:
[Column( Name="TBL_REGISTRATION_PK", Storage="TBL_REGISTRATION_PK", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert )]
public Guid RegistrationID { get { return _RegistrationID; } set { _RegistrationID = value; } }
Because the storage name of the column is TBL_REGISTRATION_PK.
The best solution that I have found to this problem is to create a private Guid field in the class that has the exact same name as the primary key in the database and use that as the backing field for the property that conforms to the Framework Guideline naming conventions.
// Primary key to TBL_REGISTRATIONT
[Column( Name = "TBL_REGISTRATIONT_PK", IsDbGenerated = true, AutoSync = AutoSync.OnInsert )]
public Guid RegistrationID
{
get
{
return TBL_REGISTRATIONT_PK;
}
private set
{
TBL_REGISTRATIONT_PK = value;
}
}
[Column( IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert )]
private Guid TBL_REGISTRATIONT_PK;