none
ForeignKeyReferenceAlreadyHasValueException Inconsistently Thrown RRS feed

  • Question

  • I'm seeing some inconsistency in when this exception is thrown and I'm confused. I understand why it gets thrown in the cases where it does, but I don't understand why it isn't being thrown in one case where I think it should be (if that makes any sense). Below is the code for the DBML-generated class. I've cut out irrelevant stuff to try to keep it small(er).

    As you can see, the entity MemberPlaceholder has two properties that are of type EntityRef, Member and TemplateType. However, in the setter for MemberId it does not check for _Member.HasLoadedOrAssignedValue but in the setting for TemplateTypeCode it does check for _TemplateType.HasLoadedOrAssignedValue, and throws the ForeignKeyReferenceAlreadyHasValueException if true. The setters for Member and TemplateType behave virtually identically (in particular, backfilling MemberId and TemplateTypeCode respectively from the incoming new object).

    So why are MemberId and TemplateTypeCode being handled differently?

    [Table(Name="dbo.MemberPlaceholder")]
    public partial class MemberPlaceholder : INotifyPropertyChanging, INotifyPropertyChanged
    {
        private int _MemberID;
        private TemplateTypeCode _TemplateTypeCode;
        private EntityRef<Member> _Member;
        private EntityRef<TemplateType> _TemplateType;
        
        public MemberPlaceholder()
        {
            this._Member = default(EntityRef<Member>);
            this._TemplateType = default(EntityRef<TemplateType>);
        }
        
        [Column(Name="MemberID", Storage="_MemberID", DbType="Int NOT NULL", IsPrimaryKey=true)]
        public int MemberId
        {
            get
            {
                return this._MemberID;
            }
            set
            {
                if ((this._MemberID != value))
                {
                    this.OnMemberIdChanging(value);
                    this.SendPropertyChanging();
                    this._MemberID = value;
                    this.SendPropertyChanged("MemberId");
                    this.OnMemberIdChanged();
                }
            }
        }
        
        [Column(Storage="_TemplateTypeCode", DbType="Int NOT NULL", CanBeNull=false, IsPrimaryKey=true)]
        public TemplateTypeCode TemplateTypeCode
        {
            get
            {
                return this._TemplateTypeCode;
            }
            set
            {
                if ((this._TemplateTypeCode != value))
                {
                    if (this._TemplateType.HasLoadedOrAssignedValue)
                    {
                        throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
                    }
                    this.OnTemplateTypeCodeChanging(value);
                    this.SendPropertyChanging();
                    this._TemplateTypeCode = value;
                    this.SendPropertyChanged("TemplateTypeCode");
                    this.OnTemplateTypeCodeChanged();
                }
            }
        }
        
        [Association(Name="Member_MemberPlaceholder", Storage="_Member", ThisKey="MemberId", OtherKey="MemberId", IsForeignKey=true)]
        public Member Member
        {
            get
            {
                return this._Member.Entity;
            }
            set
            {
                Member previousValue = this._Member.Entity;
                if (((previousValue != value) 
                            || (this._Member.HasLoadedOrAssignedValue == false)))
                {
                    this.SendPropertyChanging();
                    if ((previousValue != null))
                    {
                        this._Member.Entity = null;
                        previousValue.MemberPlaceholders.Remove(this);
                    }
                    this._Member.Entity = value;
                    if ((value != null))
                    {
                        value.MemberPlaceholders.Add(this);
                        this._MemberID = value.MemberId;
                    }
                    else
                    {
                        this._MemberID = default(int);
                    }
                    this.SendPropertyChanged("Member");
                }
            }
        }
        
        [Association(Name="TemplateType_MemberPlaceholder", Storage="_TemplateType", ThisKey="TemplateTypeCode", OtherKey="TemplateTypeCode", IsForeignKey=true)]
        public TemplateType TemplateType
        {
            get
            {
                return this._TemplateType.Entity;
            }
            set
            {
                TemplateType previousValue = this._TemplateType.Entity;
                if (((previousValue != value) 
                            || (this._TemplateType.HasLoadedOrAssignedValue == false)))
                {
                    this.SendPropertyChanging();
                    if ((previousValue != null))
                    {
                        this._TemplateType.Entity = null;
                        previousValue.MemberPlaceholders.Remove(this);
                    }
                    this._TemplateType.Entity = value;
                    if ((value != null))
                    {
                        value.MemberPlaceholders.Add(this);
                        this._TemplateTypeCode = value.TemplateTypeCode;
                    }
                    else
                    {
                        this._TemplateTypeCode = default(TemplateTypeCode);
                    }
                    this.SendPropertyChanged("TemplateType");
                }
            }
        }
    }
    

    • Edited by Craig Wagner Saturday, May 9, 2009 4:49 PM Clean up source
    Saturday, May 9, 2009 4:46 PM

All replies

  • It's because MemberID is also the primary key of your MemberPlaceholder class, and LINQ to SQL does not allow you to change primary keys of entities and then re-save them.
    Sunday, May 10, 2009 5:30 PM
    Answerer
  • While you are correct that I cannot directly change the value and then call SubmitChanges, it doesn't answer the question I originally posed, which was why does one property (MemberId) check for an existing object reference and throw an exception and the other property (TemplateTypeCode) doesn't?

    To the point you raised, both properties (MemberID and TemplateTypeCode) belong to the multi-part primary key, so that would seem to be further evidence that they should be dealt with consistently.
    Monday, May 11, 2009 12:06 AM
  • Yes, this should really throw in my opinion. Consider filing a connect bug on it.

    Another bug in this area was that multiple relationships on a foreign key would only check and throw this exception if the first relationship had loaded. This issue was fixed in my T4 template and for .NET 4.0.

    [)amien
    Monday, May 11, 2009 6:44 AM
    Moderator