Creating a Conditional Relationship in Linq2SQL
I'm using linq2sql against an old database system. In this system there are three tables, Invoices, Users, and SignupUsers. The Invoices table has a UID field that usually references the UID key on the Users table. BUT, it also has a IsSignup bit field that indicates the UID field should reference the UID key on the SignupUsers table.
I don't really care about the relationship with the SignupUsers table, but we do have a problem where it is very easy to forget to check the "IsSignup" value when using the linq2sql entities. I'd like to make the Invoices->Users relationship in the entities conditional upon the IsSignup bit field.
I've tried a couple of approaches. First I tried setting up OnLoaded:
public partial class Invoice
{
partial void OnLoaded()
{
if (IsSignup)
{
InvoiceUser = null;
}
}
}
This fails because it tries to actually set the UID field to null when it saves the invoice back-- which we cannot do.
I poked around with DataLoadOptions, but couldn't find a way to make that work either.
I could just change the InvoiceUsers property getter, but then it'd get overwritten every 开发者_如何学Pythontime the dbml gets changed.
Am I just out of luck here?
I'll put in my solution here, but still hope someone can come up with something better.
I'm using LINQ to SQL templates for T4 to generate the code. I went into the .tt file for this particular .dbml file and found the portion that generates the properties for the relationships. I then hacked in an "if" statement that modified the property if it applied to this particular relationship:
<#=code.Format(association.MemberAttributes)#><#=association.Type.Name#> <#=association.Member#>
{
get {
<#if(association.Name == "InvoiceUser_Invoice"){#>
//HACK IN THE .TT FILE TO ALWAYS INCLUDE THIS CONDITIONAL RELATIONSHIP
if(IsSignup){return null;}
<#}#>
<#if (needsSerializationFlag && serialization) {#>
if (serializing && !<#=association.Storage#>.HasLoadedOrAssignedValue) {
return null;
}
<#}#>
return <#=association.Storage#>.Entity;
}
This makes the generated code for that particular property come out as desired:
[Association(Name=@"InvoiceUser_Invoice", Storage=@"_InvoiceUser", ThisKey=@"UID", OtherKey=@"UID", IsForeignKey=true)]
public InvoiceUser InvoiceUser
{
get {
//HACK IN THE .TT FILE TO ALWAYS INCLUDE THIS CONDITIONAL RELATIONSHIP
if(IsSignup){return null;}
return _InvoiceUser.Entity;
}
set {
InvoiceUser previousValue = _InvoiceUser.Entity;
if ((previousValue != value) || (!_InvoiceUser.HasLoadedOrAssignedValue)) {
SendPropertyChanging();
if (previousValue != null) {
_InvoiceUser.Entity = null;
previousValue.Invoices.Remove(this);
}
_InvoiceUser.Entity = value;
if (value != null) {
value.Invoices.Add(this);
_UID = value.UID;
}
else {
_UID = default(int);
}
SendPropertyChanged("InvoiceUser");
}
}
}
So still a nasty hack, but it at least prevents changes to the .dbml file from removing the needed conditional relationship.
精彩评论