Rico-E
Rico-E

Reputation: 1844

Set up self referencing class

I have a recursive data structure, which has a (nullable) parent and a (nullable) collection of children.

I want to save that data structure into a local database on windows phone. I set up a DataContext which works rather nicely, but when I want to insert a new object (which has no parent neither children), I get the "Object reference not set to an instance of an object." error.

After searching, I found this thread LINQ to SQL: Self-referencing entity - ParentID/ChildID association and it states, that this exception is thrown because of the parent and children objects being null. But as this is an allowed state, this should not prevent my code from running properly.

So the question is: how to set up a self referncing class.

What I have done so far:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace Notepad.Codes.Models
{

    [Table]
    public class DirectoryItem 
    {
        [Column(IsVersion=true)]
        private Binary version;

        [Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
        public int primaryKey;

        private DirectoryItem parent;
        [Association(Storage = "parent", ThisKey = "primaryKey", OtherKey = "primaryKey")]
        public DirectoryItem Parent
        {
            get
            {
                return parent;
            }
            set
            {
                this.SetProperty(ref this.parent, value);
            }
        }

        private EntitySet<DirectoryItem > children;
        [Association(Storage = "children", OtherKey = "primaryKey")]
        public EntitySet<DirectoryItem > Children
        {
            get
            {
                if( children == null)
                    children = new EntitySet<DirectoryItem >();
                return children;
            }
            set
            {
                if (this.PropertyChanging != null)
                    PropertyChanging(this, new PropertyChangingEventArgs("Children"));
                this.children.Assign(value);
                if (this.PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Children"));
            }
        }
    }
}

Could somebody please tell me, how I have to use or change the Associoaton-Attribute so that I can insert nullable parents and children into my database?

Upvotes: 1

Views: 4128

Answers (2)

Rico-E
Rico-E

Reputation: 1844

I solved my problem with northwind. I followed these steps (I list them, because MSDN is a heavily changing place:)

  1. go to http://www.microsoft.com/en-us/download/details.aspx?id=23654
  2. download and run the installer
  3. open visual Studio (<2012)
  4. open Server Explorer, open the shortcut menu for data Connections and select add connection
  5. select Microsoft SQL sever database file
  6. choose browse and browse to the installed database (for me it was installed under C:\Sample Database (or similar)
  7. use Windows authentication and click ok*
  8. select all tables from the northwind database and drag'n'drop them into the object relational designer
  9. now you can Change to the code behind file, and see all the logic and attributes

And the solution for my case now looks like:

[Table]
public class DirectoryItem 
{
    [Column(IsVersion=true)]
    private Binary version;

    [Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
    public int primaryKey;

    private System.Nullable<int> parentId;
    [Column(Storage = "parentId", DbType="Int")]
    public System.Nullable<int> ParentId
    {
        get
        {
            return this.parentId;
        }
        set
        {
            this.SetProperty(ref this.parentId, value);
        }
    }

    private EntityRef<DirectoryItem > parent;
    [Association(Name = "DirectoryItem_parent", Storage = "parent", ThisKey = "ParentId", OtherKey = "primaryKey", IsForeignKey = true)]
    public DirectoryItem Parent
    {
        get
        {
            return parent.Entity;
        }
        set
        {
            if (this.PropertyChanging != null)
                PropertyChanging(this, new PropertyChangingEventArgs("Parent"));

            parent.Entity = value;

            if (this.PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Parent"));
        }
    }

    private EntitySet<DirectoryItem > children;
    [Association(Name = "DirectoryItem_DirectoryItem", Storage = "Children", ThisKey = "primaryKey", OtherKey = "ParentId")]
    public EntitySet<DirectoryItem > Children
    {
        get
        {
            if (children == null)
                children = new EntitySet<DirectoryItem >();
            return children;
        }
        set
        {
            if (this.PropertyChanging != null)
                PropertyChanging(this, new PropertyChangingEventArgs("Children"));

            if (children == null)
                children = new EntitySet<DirectoryItem >();
            this.children.Assign(value);

            if (this.PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Children"));
        }
    }
}

Appedix:

  1. instrucitons to install sample databases: http://msdn.microsoft.com/en-us/library/vstudio/8b6y4c7s.aspx

Upvotes: 0

Kevin Gosse
Kevin Gosse

Reputation: 39007

There's a problem in the setter of the Children property:

if (this.PropertyChanging != null)
    PropertyChanging(this, new PropertyChangingEventArgs("Children"));

this.children.Assign(value);

if (this.PropertyChanged != null)
    PropertyChanged(this, new PropertyChangedEventArgs("Children"));

You're calling this.children.Assign even though children might not be initialized. Use the same check as your getter to prevent this issue:

if( children == null)
    children = new EntitySet<DirectoryItem >();

this.children.Assign(value);

Upvotes: 1

Related Questions