ManyToOne relation not working, throwing NullReferenceExc.

Issue #7 wontfix
Benjamin created an issue

The table layout is as following:

    public class Artist
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }

        public string Name { get; set; }

        [OneToMany]
        public List<Track> Tracks { get; set; }
    }

    public class Track
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }

        public string Title { get; set; }
        public string Path { get; set; }
        public string Filename { get; set; }

        [ManyToOne]
        public Artist Artist { get; set; }

        [ForeignKey(typeof(Artist))]
        public int ArtistId { get; set; }
    }

I can create artists without a problem (not linking any tracks). Creating a track linked to an artist gives a NullReferenceException thrown in TableQuery.cs:407

                if (mem.Expression != null && mem.Expression.NodeType == ExpressionType.Parameter)
                {
                    //
                    // This is a column of our table, output just the column name
                    // Need to translate it if that column name is mapped
                    //
                    string columnName = Table.FindColumnWithPropertyName(mem.Member.Name).Name;
                    return new CompileResult
                    {
                        CommandText = "\"" + columnName + "\""
                    };
                }

This is because we are trying to find a column named Artist, instead of the relation itself.

I don't really know enough about this library to see where (and why) it goes wrong.

If you need more info, please ask.

Edit:

When removing the ArtistId column and letting the ORM create its own mapping, I get the following create table query:

"create table if not exists \"Track\"(\n\"Id\" integer primary key autoincrement not null ,\n\"Title\" varchar(140) ,\n\"Path\" varchar(140) ,\n\"Filename\" varchar(140) )"

Where I don't see any reference to the Artist table.

Edit number 2:

The class responsible for managing data access (sorta)

        public MusicTrackerDataContext()
        {
            connection = new SQLiteConnection(new SQLitePlatformWinRT(), Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, DATABASE_NAME));

            Initialize();

            ArtistTable = connection.Table<Artist>();
            TrackTable = connection.Table<Track>();
        }

        private void Initialize()
        {
            var result = connection.CreateTable<Artist>();
            result = connection.CreateTable<Track>();
        }

That class is called whenever a connection to the database is made.

Then the class where we use the database (scanning a library and adding entities)

        private Track CreateTrack(string trackTitle, Artist artist)
        {
            var track = dbContext.TrackTable.Where(
                t => 
                    t.Title == trackTitle && t.Artist == artist
                ).FirstOrDefault();

            if (track == null)
            {
                track = new Track();
                track.Title = trackTitle;
                dbContext.Insert(track);

                track.Artist = artist;
                dbContext.InsertWithChildren(track);
            }

            return track;
        }

        private Artist CreateArtist(string artistName)
        {
            var artist = dbContext.ArtistTable.Where(
                    a => a.Name == artistName
                ).FirstOrDefault();

            if (artist == null)
            {
                artist = new Artist();
                artist.Name = artistName;
                dbContext.Insert(artist);
            }

            return artist;
        }

The code fails when we try the Where(), because the column Artist does not exist.

It is circumvented by the following code

            var track = dbContext.TrackTable.Where(
                t => 
                    t.Title == trackTitle && t.ArtistId == artist.Id
                ).FirstOrDefault();

It works, but it's not how I want to interact with my data. Using it like this means that wherever I want to look for something, I need to remember to use ids, and not entities. (the same applies for adding, updating and retrieving entities with a OneToMany relation, I need to {Insert,Update,Get}WithChildren()).

Comments (7)

  1. Guillermo Gutiérrez

    The NullReferenceException is not being raised in SQLite-Net Extensions code, but in SQLite-Net code. ManyToOne (and all relationship attributes) inherit from IgnoreAttribute, so SQLite-Net should ignore that properties when inserting/reading the objects into/from the database.

    Maybe you are missing the call to conn.CreateTable<Artist>? If you can provide a sample project I'll try to help you further.

  2. Guillermo Gutiérrez

    I see, the problem is that you are accessing a SQLite-net Extension relationship property in a SQLite-Net query. Joining tables in queries is not currently supported in SQLite-Net Extensions, and won't be supported in a near future.

  3. Benjamin reporter

    That's too bad. Do you know what would be needed to support this? If I have some time I might take a look (but I'm fairly new to RT and LINQ)

  4. Log in to comment