WP8 – SQL Ce – Linq2Sql – CodeFirst Approach

Hallo Leute,

nachdem die ersten Erfahrungen mit SQLite nicht wirklich erfolgreich waren (keine Relationen supported, etc.), haben wir das DB-Modell auf SQLCe umgebaut und haben dabei den Microsoft „Code First“-Approach verfolgt.

Prinzipiell ist es ja recht einfach, man muss nur die eine oder andere Klasse erzeugen und diese mit Attributen verzieren. Beispiel:

[Table(Name = "TABELLENNAME")]
    public class Tabellenname : Base
    {

        private int _id = -1;
        [Column(Storage = "Id", DbType = "int IDENTITY(1,1) NOT NULL", IsPrimaryKey = true, IsDbGenerated = true)]
        public int Id
        {
            get
            {
                return _id;
            }
            set
            {
                if (value != _id)
                {
                    NotifyPropertyChanging();
                    _id = value;
                    NotifyPropertyChanged("Id");
                }
            }
        }        
    }

Wir Ihr seht, leite ich von „Base“ ab. Das könnt Ihr getrost ignorieren, meine Base-Klasse leitet im lediglich von INotifyPropertyChanged und INotifyPropertyChanging ab und implementiert deren Methoden (NotifiyPropertyChanging() bzw. NotifyPropertyChanged()). Damit ist die Klasse auch gleich tauglich für ein mögliches DataBinding …

Spannender wirds im Attribut der Eigenschaft „Id“

[Column(Storage = "Id", DbType = "int IDENTITY(1,1) NOT NULL", IsPrimaryKey = true, IsDbGenerated = true)]

Storage… So heißt die Spalte in der Datenbank-Tabelle
DbType… Datentyp der Spalte, in diesem Fall als Autoincrement
IsPrimaryKey… Klare Sache, die ID soll Primärschlüssel sein
IsDbGenerated… Wichtig wenn Ihr ein AutoIncrement verwendet, teilt Linq2Sql mit, dass dieser Wert nicht in die DB geschrieben werden soll

Nun benötigt Ihr einen DataContext, welchem Ihr diese Tabelle zuordnet. Ich habe dafür die Klasse „DataContextBase“ geschaffen, welche von System.Data.Linq.DataContext ableitet.

 public partial class DataContextBase : System.Data.Linq.DataContext
    {
        private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();

        #region Extensibility Method Definitions
        partial void OnCreated();

        #endregion

        public DataContextBase(string connection) : base(connection, mappingSource)
        {
            OnCreated();
        }

        public DataContextBase(string connection,
            System.Data.Linq.Mapping.MappingSource mappingSource) :
                base(connection, mappingSource)
        {
            OnCreated();
        }

        #region Tables
        public System.Data.Linq.Table<Tabellenname> Tabellenname
        {
            get
            {
                return this.GetTable<Tabellenname>();
            }
        }

        

        #endregion
    }

Abschließend müsst Ihr den DataContext noch in der App.xaml.cs „registrieren“. Dafür bedarf es mehrerer Schritte.

1.) Deklarieren von Events, Konstanten und Eigenschaften:

public partial class App : Application
    {
        public const string DatabaseFilename = "isostore:vacation.sdf";
        public static string DatabasePassword = "";

        // Data Context
        public static DataContextBase YourDc { get; set; }

// ...
// ...
// ...
// ...
}

Im Konstruktor müsst Ihr euren DataContext noch intialisieren…

public App()
        {
// ...
// ...
// ...
            YourDc = new DataContextBase("Data Source='" + DatabaseFilename + "'" + (DatabasePassword.Length == 0 ? "" : ";Password='" + DatabasePassword + "'"));
            YourDc.ObjectTrackingEnabled = true;
        }

Abschließend müsst Ihr eurem DataContext natürlich noch mitteilen, dass die DB-Tabellen erzeugt werden sollen und evtl. ein paar System Daten erstellen..

Dafür habe ich drei Methoden erzeugt, welche ich in eine eigene DataHelper-Klasse ausgelagert habe:

 public static void InitializeDatabase(bool wipe, DataContextBase todo)
        {
            using (IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (wipe)
                    WipeDatabase(iso);
                else
                {
                    if (iso.FileExists(DbName))
                        return;
                }

                CreateDatabase(todo);
            }
        }
private static void CreateDatabase(DataContextBase context)
        {
            try
            {
                // Generate the database (with structure) from the code-based data context
                context.CreateDatabase();

                // Populate the database with system data
                //Task items = new Task();
                //items.Id = Guid.NewGuid();
                //items.Title = "Welcome to the \"Todo\"!";
                //items.LocationID = new Guid(Utils.LocationIDDefault);
                //items.Completed = true;
                //context.Items.InsertOnSubmit(items);

                //context.SubmitChanges();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error while creating the DB: " + ex.Message);
                System.Diagnostics.Debug.WriteLine("Error while creating the DB: " + ex.Message);
            }

        }
        
        public static void WipeDatabase(IsolatedStorageFile iso)
        {
            if (iso.FileExists(DbName))
                iso.DeleteFile(DbName);
        }

Cheers,
Christian

Leave a reply