Search code examples
c#sqliteentity-framework-6code-firstsystem.drawing

Code first approach to store System.Drawing.PointF in Sqlite DB


I'm currently working on a customer project were I have to store data from the entities in a sqlite DB. I'm working with EF6 and I've chosen the "code first" approach.

Problem: I would like to store geographic coordinates (as single Point in Boo and as a List of Points in Loo) as System.Drawing.PointF in the DB. Because it's not a primitive type it's unfortunately not working like I taught.

Question: What do I have to change in my Entity definition and/or in my Model Configuration to store the single Point of Boo and also the List of Points of Loo in the DB? Thank you in advance!

My Entity classes:

public class Collection
{
    [Key]
    public int Id { get; set; }
    public virtual List<Foo> Foos { get; set; }
}

public class Foo
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual Collection FooCollection;
}

public class Boo : Foo
{
    public PointF Location { get; set; }
}

public class Loo : Foo
{
    public List<PointF> Line { get; set; }
}

My Model Configuration:

public class ModelConfiguration
{
    public static void Configure(DbModelBuilder modelBuilder)
    {
        ConfigureFooCollectionEntity(modelBuilder);
    }

    private static void ConfigureFooCollectionEntity(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Collection>().ToTable("base.MyTable")
            .HasRequired(t => t.Foos)
            .WithMany()
            .WillCascadeOnDelete(false);
    }

    private static void ConfigureGridElementEntity(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Foo>()
            .HasRequired(p => p.FooCollection)
            .WithMany(fooCollection => fooCollection.Foos)
            .WillCascadeOnDelete(true);
    }
}

My DbContext:

public class DbContext : DbContext
{
    public DbSet<Collection> DataCollection { get; set; }

    public DbContext(string nameOrConnectionString)
        : base(nameOrConnectionString)
    {
        Configure();
    }

    public DbContext(DbConnection connection, bool contextOwnsConnection)
        : base(connection, contextOwnsConnection)
    {
        Configure();
    }

    private void Configure()
    {
        Configuration.ProxyCreationEnabled = true;
        Configuration.LazyLoadingEnabled = true;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        ModelConfiguration.Configure(modelBuilder);
        var initializer = new DbInitializer(modelBuilder);
        Database.SetInitializer(initializer);
    }
}

My DbInitializer:

class DbInitializer : 
SqliteDropCreateDatabaseWhenModelChanges<DbContext>
{
    public GDbInitializer(DbModelBuilder modelBuilder)
        : base(modelBuilder, typeof(CustomHistory))
    { }
}

Solution

  • The easiest way to solve this problem is to work not with System.Drawing.PointF but with your own point type. This way you are able to give it a ID property and EF can store it as a separate DB-table.

    This is how it works for me:

    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class GeoPoint
    {
        [Key]
        public int Id { get; set; }
        public float X { get; set; }
        public float Y { get; set; }
    
        [NotMapped]
        public PointF GetPointF { get { return new PointF(X, Y); } }
    
        public GeoPoint()
        {
            X = 0; Y = 0;
        }
    
        public GeoPoint(float x, float y)
        {
            X = x; Y = y;
        }
    
        public GeoPoint(PointF point)
        {
            X = point.X;
            Y = point.Y;
        }
    
    }