From 4f5ba9cf74ee1c4d945bc04bd78c0e4d038b5209 Mon Sep 17 00:00:00 2001 From: F4lcon Date: Wed, 28 Nov 2018 23:30:59 +0100 Subject: [PATCH] Migrate to Entity Framework Core This PR migrates from the entity framework to entity framework core. It also supports SQLite as database option now. --- .../Data/Configurations/ConfigurationBase.cs | 34 +-- .../DispositionedResourceConfiguration.cs | 19 +- .../Configurations/OperationConfiguration.cs | 90 +++--- .../OperationResourceConfiguration.cs | 21 +- .../Configurations/SettingConfiguration.cs | 20 +- .../Data/Contexts/ContextCreationOptions.cs | 73 +++-- Backend/Data/Contexts/MainDbContext.cs | 61 ++-- .../Contexts/MainDbContextConfiguration.cs | 41 --- .../Data/Contexts/MainDbContextInitializer.cs | 25 -- Backend/Data/Contexts/MySqlHistoryContext.cs | 53 ---- Backend/Data/Data.csproj | 31 +- Backend/Data/Data/IUnitOfWork.cs | 3 + Backend/Data/Data/Repository.cs | 16 +- .../201510221659525_Initial.Designer.cs | 29 -- .../Migrations/201510221659525_Initial.cs | 112 ------- .../Migrations/201510221659525_Initial.resx | 126 -------- .../20181230152708_Initial.Designer.cs | 276 ++++++++++++++++++ .../Data/Migrations/20181230152708_Initial.cs | 149 ++++++++++ Backend/Data/Migrations/Configuration.cs | 29 -- .../Migrations/MainDbContextModelSnapshot.cs | 274 +++++++++++++++++ Backend/Data/Properties/Resources.Designer.cs | 11 +- Backend/Data/Properties/Resources.resx | 216 ++++++++------ Backend/Service/Service.csproj | 1 + Backend/ServiceContracts/Backend.config | 4 + BackendServices/Management/DataExtensions.cs | 37 +-- BackendServices/System/DatabaseChecker.cs | 33 ++- 26 files changed, 1069 insertions(+), 715 deletions(-) delete mode 100644 Backend/Data/Contexts/MainDbContextConfiguration.cs delete mode 100644 Backend/Data/Contexts/MainDbContextInitializer.cs delete mode 100644 Backend/Data/Contexts/MySqlHistoryContext.cs delete mode 100644 Backend/Data/Migrations/201510221659525_Initial.Designer.cs delete mode 100644 Backend/Data/Migrations/201510221659525_Initial.cs delete mode 100644 Backend/Data/Migrations/201510221659525_Initial.resx create mode 100644 Backend/Data/Migrations/20181230152708_Initial.Designer.cs create mode 100644 Backend/Data/Migrations/20181230152708_Initial.cs delete mode 100644 Backend/Data/Migrations/Configuration.cs create mode 100644 Backend/Data/Migrations/MainDbContextModelSnapshot.cs diff --git a/Backend/Data/Configurations/ConfigurationBase.cs b/Backend/Data/Configurations/ConfigurationBase.cs index 205004ee..c0f416b1 100644 --- a/Backend/Data/Configurations/ConfigurationBase.cs +++ b/Backend/Data/Configurations/ConfigurationBase.cs @@ -13,9 +13,12 @@ // You should have received a copy of the GNU General Public License // along with AlarmWorkflow. If not, see . +using System; using System.ComponentModel.DataAnnotations.Schema; -using System.Data.Entity.ModelConfiguration; +using System.Linq.Expressions; using AlarmWorkflow.Backend.Data.Types; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace AlarmWorkflow.Backend.Data.Configurations { @@ -23,34 +26,17 @@ namespace AlarmWorkflow.Backend.Data.Configurations /// Abstract base class for a entity type configuration. /// /// - abstract class ConfigurationBase : EntityTypeConfiguration where TEntity : EntityBase + abstract class ConfigurationBase : IEntityTypeConfiguration where TEntity : EntityBase { - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - protected ConfigurationBase() - : base() - { - ToTable(GetTableName()); - - MapKeys(); - } - - #endregion - #region Methods - /// - /// Performs automatic mapping of the key. - /// - protected virtual void MapKeys() + public virtual void Configure(EntityTypeBuilder builder) { - HasKey(e => e.Id); - Property(e => e.Id).HasColumnName("id").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); + builder.ToTable(GetTableName()); + builder.HasKey(e => e.Id); + builder.Property(e => e.Id).HasColumnName("id").ValueGeneratedOnAdd(); } - + /// /// When overridden in a derived class, customizes the table name for the provided . /// diff --git a/Backend/Data/Configurations/DispositionedResourceConfiguration.cs b/Backend/Data/Configurations/DispositionedResourceConfiguration.cs index abc2bd13..834b3995 100644 --- a/Backend/Data/Configurations/DispositionedResourceConfiguration.cs +++ b/Backend/Data/Configurations/DispositionedResourceConfiguration.cs @@ -14,23 +14,24 @@ // along with AlarmWorkflow. If not, see . using AlarmWorkflow.Backend.Data.Types; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace AlarmWorkflow.Backend.Data.Configurations { + // ReSharper disable once UnusedMember.Global class DispositionedResourceConfiguration : ConfigurationBase { - #region Constructors + #region Methods - public DispositionedResourceConfiguration() + public override void Configure(EntityTypeBuilder builder) { - Property(dr => dr.OperationId).HasColumnName("operation_id").IsRequired(); - Property(dr => dr.Timestamp).HasColumnName("timestamp").IsRequired(); - Property(dr => dr.EmkResourceId).HasColumnName("emkresourceid").IsRequired(); - } - - #endregion + base.Configure(builder); - #region Methods + builder.Property(dr => dr.OperationId).HasColumnName("operation_id").IsRequired(); + builder.Property(dr => dr.Timestamp).HasColumnName("timestamp").IsRequired(); + builder.Property(dr => dr.EmkResourceId).HasColumnName("emkresourceid").IsRequired(); + } protected override string GetTableName() { diff --git a/Backend/Data/Configurations/OperationConfiguration.cs b/Backend/Data/Configurations/OperationConfiguration.cs index 18d7512b..8557d4b6 100644 --- a/Backend/Data/Configurations/OperationConfiguration.cs +++ b/Backend/Data/Configurations/OperationConfiguration.cs @@ -14,68 +14,64 @@ // along with AlarmWorkflow. If not, see . using AlarmWorkflow.Backend.Data.Types; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace AlarmWorkflow.Backend.Data.Configurations { + // ReSharper disable once UnusedMember.Global class OperationConfiguration : ConfigurationBase { - #region Constructors + #region Methods - public OperationConfiguration() + public override void Configure(EntityTypeBuilder builder) { - Property(o => o.IsAcknowledged).HasColumnName("acknowledged").IsRequired(); - Property(o => o.Guid).HasColumnName("operationguid").IsRequired(); - Property(o => o.OperationNumber).HasColumnName("operationnumber"); - Property(o => o.IncomeAt).HasColumnName("timestampincome"); - Property(o => o.AlarmAt).HasColumnName("timestampalarm"); - Property(o => o.Messenger).HasColumnName("messenger"); - Property(o => o.Comment).HasColumnName("comment"); - Property(o => o.Plan).HasColumnName("plan"); - Property(o => o.Picture).HasColumnName("picture"); - Property(o => o.Priority).HasColumnName("priority"); + base.Configure(builder); - Property(o => o.Einsatzort.Street).HasColumnName("einsatzortstreet"); - Property(o => o.Einsatzort.StreetNumber).HasColumnName("einsatzortstreetnumber"); - Property(o => o.Einsatzort.City).HasColumnName("einsatzortcity"); - Property(o => o.Einsatzort.ZipCode).HasColumnName("einsatzortzipcode"); - Property(o => o.Einsatzort.Intersection).HasColumnName("einsatzortintersection"); - Property(o => o.Einsatzort.Property).HasColumnName("einsatzortproperty"); - Property(o => o.Einsatzort.Location).HasColumnName("einsatzortlocation"); - Property(o => o.Einsatzort.GeoLatLng).HasColumnName("einsatzortlatlng"); + builder.Property(o => o.IsAcknowledged).HasColumnName("acknowledged").IsRequired(); + builder.Property(o => o.Guid).HasColumnName("operationguid").IsRequired(); + builder.Property(o => o.OperationNumber).HasColumnName("operationnumber"); + builder.Property(o => o.IncomeAt).HasColumnName("timestampincome"); + builder.Property(o => o.AlarmAt).HasColumnName("timestampalarm"); + builder.Property(o => o.Messenger).HasColumnName("messenger"); + builder.Property(o => o.Comment).HasColumnName("comment"); + builder.Property(o => o.Plan).HasColumnName("plan"); + builder.Property(o => o.Picture).HasColumnName("picture"); + builder.Property(o => o.Priority).HasColumnName("priority"); - Property(o => o.Zielort.Street).HasColumnName("zielortstreet"); - Property(o => o.Zielort.StreetNumber).HasColumnName("zielortstreetnumber"); - Property(o => o.Zielort.City).HasColumnName("zielortcity"); - Property(o => o.Zielort.ZipCode).HasColumnName("zielortzipcode"); - Property(o => o.Zielort.Intersection).HasColumnName("zielortintersection"); - Property(o => o.Zielort.Property).HasColumnName("zielortproperty"); - Property(o => o.Zielort.Location).HasColumnName("zielortlocation"); - Property(o => o.Zielort.GeoLatLng).HasColumnName("zielortlatlng"); + builder.OwnsOne(o => o.Einsatzort).Property(o => o.Street).HasColumnName("einsatzortstreet"); + builder.OwnsOne(o => o.Einsatzort).Property(o => o.StreetNumber).HasColumnName("einsatzortstreetnumber"); + builder.OwnsOne(o => o.Einsatzort).Property(o => o.City).HasColumnName("einsatzortcity"); + builder.OwnsOne(o => o.Einsatzort).Property(o => o.ZipCode).HasColumnName("einsatzortzipcode"); + builder.OwnsOne(o => o.Einsatzort).Property(o => o.Intersection).HasColumnName("einsatzortintersection"); + builder.OwnsOne(o => o.Einsatzort).Property(o => o.Property).HasColumnName("einsatzortproperty"); + builder.OwnsOne(o => o.Einsatzort).Property(o => o.Location).HasColumnName("einsatzortlocation"); + builder.OwnsOne(o => o.Einsatzort).Property(o => o.GeoLatLng).HasColumnName("einsatzortlatlng"); - Property(o => o.Keywords.Keyword).HasColumnName("keyword"); - Property(o => o.Keywords.EmergencyKeyword).HasColumnName("keywordmisc"); - Property(o => o.Keywords.B).HasColumnName("keywordb"); - Property(o => o.Keywords.R).HasColumnName("keywordr"); - Property(o => o.Keywords.S).HasColumnName("keywords"); - Property(o => o.Keywords.T).HasColumnName("keywordt"); + builder.OwnsOne(o => o.Zielort).Property(o => o.Street).HasColumnName("zielortstreet"); + builder.OwnsOne(o => o.Zielort).Property(o => o.StreetNumber).HasColumnName("zielortstreetnumber"); + builder.OwnsOne(o => o.Zielort).Property(o => o.City).HasColumnName("zielortcity"); + builder.OwnsOne(o => o.Zielort).Property(o => o.ZipCode).HasColumnName("zielortzipcode"); + builder.OwnsOne(o => o.Zielort).Property(o => o.Intersection).HasColumnName("zielortintersection"); + builder.OwnsOne(o => o.Zielort).Property(o => o.Property).HasColumnName("zielortproperty"); + builder.OwnsOne(o => o.Zielort).Property(o => o.Location).HasColumnName("zielortlocation"); + builder.OwnsOne(o => o.Zielort).Property(o => o.GeoLatLng).HasColumnName("zielortlatlng"); - Property(o => o.Loops).HasColumnName("loopscsv"); - Property(o => o.CustomData).HasColumnName("customdatajson"); + builder.OwnsOne(o => o.Keywords).Property(o => o.Keyword).HasColumnName("keyword"); + builder.OwnsOne(o => o.Keywords).Property(o => o.EmergencyKeyword).HasColumnName("keywordmisc"); + builder.OwnsOne(o => o.Keywords).Property(o => o.B).HasColumnName("keywordb"); + builder.OwnsOne(o => o.Keywords).Property(o => o.R).HasColumnName("keywordr"); + builder.OwnsOne(o => o.Keywords).Property(o => o.S).HasColumnName("keywords"); + builder.OwnsOne(o => o.Keywords).Property(o => o.T).HasColumnName("keywordt"); - HasMany(o => o.Resources) - .WithRequired(r => r.Operation) - .HasForeignKey(r => r.OperationId) - .WillCascadeOnDelete(); + builder.Property(o => o.Loops).HasColumnName("loopscsv"); + builder.Property(o => o.CustomData).HasColumnName("customdatajson"); - HasMany(o => o.DispositionedResources) - .WithRequired(dr => dr.Operation) - .HasForeignKey(dr => dr.OperationId) - .WillCascadeOnDelete(); - } + builder.HasMany(o => o.Resources).WithOne(r => r.Operation).IsRequired().OnDelete(DeleteBehavior.Cascade); - #endregion + builder.HasMany(o => o.DispositionedResources).WithOne(r => r.Operation).IsRequired().OnDelete(DeleteBehavior.Cascade); + } - #region Methods protected override string GetTableName() { diff --git a/Backend/Data/Configurations/OperationResourceConfiguration.cs b/Backend/Data/Configurations/OperationResourceConfiguration.cs index a456d19a..835b95c5 100644 --- a/Backend/Data/Configurations/OperationResourceConfiguration.cs +++ b/Backend/Data/Configurations/OperationResourceConfiguration.cs @@ -14,24 +14,25 @@ // along with AlarmWorkflow. If not, see . using AlarmWorkflow.Backend.Data.Types; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace AlarmWorkflow.Backend.Data.Configurations { + // ReSharper disable once UnusedMember.Global class OperationResourceConfiguration : ConfigurationBase { - #region Constructors + #region Methods - public OperationResourceConfiguration() + public override void Configure(EntityTypeBuilder builder) { - Property(or => or.OperationId).HasColumnName("operation_id").IsRequired(); - Property(or => or.Timestamp).HasColumnName("timestamp"); - Property(or => or.FullName).HasColumnName("fullname"); - Property(or => or.Equipment).HasColumnName("equipmentcsv"); - } - - #endregion + base.Configure(builder); - #region Methods + builder.Property(or => or.OperationId).HasColumnName("operation_id").IsRequired(); + builder.Property(or => or.Timestamp).HasColumnName("timestamp"); + builder.Property(or => or.FullName).HasColumnName("fullname"); + builder.Property(or => or.Equipment).HasColumnName("equipmentcsv"); + } protected override string GetTableName() { diff --git a/Backend/Data/Configurations/SettingConfiguration.cs b/Backend/Data/Configurations/SettingConfiguration.cs index cff91725..b538a6e4 100644 --- a/Backend/Data/Configurations/SettingConfiguration.cs +++ b/Backend/Data/Configurations/SettingConfiguration.cs @@ -14,24 +14,24 @@ // along with AlarmWorkflow. If not, see . using AlarmWorkflow.Backend.Data.Types; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace AlarmWorkflow.Backend.Data.Configurations { + // ReSharper disable once UnusedMember.Global class SettingConfiguration : ConfigurationBase { - #region Constructors + #region Methods - public SettingConfiguration() - : base() + public override void Configure(EntityTypeBuilder builder) { - Property(s => s.Identifier).IsRequired().HasColumnName("identifier"); - Property(s => s.Name).IsRequired().HasColumnName("name"); - Property(s => s.Value).HasColumnName("value"); - } - - #endregion + base.Configure(builder); - #region Methods + builder.Property(s => s.Identifier).IsRequired().HasColumnName("identifier"); + builder.Property(s => s.Name).IsRequired().HasColumnName("name"); + builder.Property(s => s.Value).HasColumnName("value"); + } protected override string GetTableName() { diff --git a/Backend/Data/Contexts/ContextCreationOptions.cs b/Backend/Data/Contexts/ContextCreationOptions.cs index baaad94d..f4acb6e6 100644 --- a/Backend/Data/Contexts/ContextCreationOptions.cs +++ b/Backend/Data/Contexts/ContextCreationOptions.cs @@ -14,14 +14,27 @@ // along with AlarmWorkflow. If not, see . using AlarmWorkflow.Backend.ServiceContracts.Communication; +using AlarmWorkflow.Shared.Core; +using AlarmWorkflow.Shared.Diagnostics; +using System; +using System.Linq; +using System.Threading; namespace AlarmWorkflow.Backend.Data.Contexts { sealed class ContextCreationOptions { + #region Enums + + public enum DatabaseEngine { MySQL, SQLite } + + #endregion + #region Constants - private const string ConnectionStringTemplate = "server={0};Port={1};User Id={2};Password={3};database={4};Persist Security Info=True"; + private const string ConnectionStringTemplateMySQL = "server={0};Port={1};User Id={2};Password={3};database={4};Persist Security Info=True"; + + private const string ConnectionStringTemplateSQLite = "Data Source={0};"; #endregion @@ -32,6 +45,10 @@ sealed class ContextCreationOptions /// public string HostName { get; set; } /// + /// Gets/sets the host name of the server to connect to. + /// + public DatabaseEngine Engine { get; set; } + /// /// Gets/sets the port of the server to connect to. /// public int Port { get; set; } @@ -47,30 +64,31 @@ sealed class ContextCreationOptions /// Gets/sets the name of the database to connect to. /// public string DatabaseName { get; set; } + /// + /// Gets/sets the name of the database path to connect to. + /// + public string SQLiteDatabase { get; set; } #endregion - #region Constructors + #region Methods /// - /// Initializes a new instance of the class. + /// Returns the connection string from the values of this instancefor a MySQL database. /// - public ContextCreationOptions() + /// + public string GetMySqlConnectionString() { - + return string.Format(ConnectionStringTemplateMySQL, HostName, Port, UserId, Password, DatabaseName); } - #endregion - - #region Methods - /// - /// Returns the connection string from the values of this instance. + /// Returns the connection string from the values of this instance for a sqlite database. /// /// - public string GetConnectionString() + public string GetSQLiteConnectionString() { - return string.Format(ConnectionStringTemplate, HostName, Port, UserId, Password, DatabaseName); + return string.Format(ConnectionStringTemplateSQLite, SQLiteDatabase); } #endregion @@ -83,15 +101,34 @@ public string GetConnectionString() /// An instance of which uses the values from the backend configuration. public static ContextCreationOptions CreateFromSettings() { - ContextCreationOptions options = new ContextCreationOptions(); - options.HostName = ServiceFactory.BackendConfigurator.Get("Server.DB.HostName"); - options.Port = int.Parse(ServiceFactory.BackendConfigurator.Get("Server.DB.Port")); - options.UserId = ServiceFactory.BackendConfigurator.Get("Server.DB.UserId"); - options.Password = ServiceFactory.BackendConfigurator.Get("Server.DB.Password"); - options.DatabaseName = ServiceFactory.BackendConfigurator.Get("Server.DB.DatabaseName"); + var options = new ContextCreationOptions + { + Engine = GetEngine(), + HostName = ServiceFactory.BackendConfigurator.Get("Server.DB.HostName"), + Port = int.Parse(ServiceFactory.BackendConfigurator.Get("Server.DB.Port")), + UserId = ServiceFactory.BackendConfigurator.Get("Server.DB.UserId"), + Password = ServiceFactory.BackendConfigurator.Get("Server.DB.Password"), + DatabaseName = ServiceFactory.BackendConfigurator.Get("Server.DB.DatabaseName"), + SQLiteDatabase = Utilities.GetLocalAppDataFolderFileName(ServiceFactory.BackendConfigurator.Get("Server.DB.SQLite.Database")) + }; return options; } + private static DatabaseEngine GetEngine() + { + string engineName = ServiceFactory.BackendConfigurator.Get("Server.DB.Engine"); + + try + { + return (DatabaseEngine)Enum.Parse(typeof(DatabaseEngine), engineName, true); + } + catch (ArgumentException) + { + Logger.Instance.LogFormat(LogType.Warning, typeof(ContextCreationOptions), Properties.Resources.EngineNotFound, engineName, "MySQL"); + return DatabaseEngine.MySQL; + } + } + #endregion } } diff --git a/Backend/Data/Contexts/MainDbContext.cs b/Backend/Data/Contexts/MainDbContext.cs index adc69012..874ccf29 100644 --- a/Backend/Data/Contexts/MainDbContext.cs +++ b/Backend/Data/Contexts/MainDbContext.cs @@ -13,17 +13,14 @@ // You should have received a copy of the GNU General Public License // along with AlarmWorkflow. If not, see . +using Microsoft.EntityFrameworkCore; using System; using System.Collections.Concurrent; -using System.Data.Common; -using System.Data.Entity; using System.Diagnostics; -using AlarmWorkflow.Shared.Core; -using MySql.Data.MySqlClient; +using System.Threading.Tasks; namespace AlarmWorkflow.Backend.Data.Contexts { - [DbConfigurationType(typeof(MainDbContextConfiguration))] class MainDbContext : DbContext, IUnitOfWork { #region Constants @@ -44,17 +41,12 @@ class MainDbContext : DbContext, IUnitOfWork #region Constructors - static MainDbContext() - { - Database.SetInitializer(new MainDbContextInitializer()); - } - /// /// Initializes a new instance of the class /// creates a connection to either the local server (when EF uses it) or the configured server (otherwise) and owns it. /// public MainDbContext() - : base(CreateConnection(), true) + : base() { _workCache = new ConcurrentDictionary(); } @@ -63,44 +55,36 @@ public MainDbContext() #region Methods - private static DbConnection CreateConnection() - { - /* Hint: The MainDbContext is currently hard-wired to use MySQL. - * However, with very little effort it is perfectly possible of using any EF provider (PostgreSQL, SQLite etc.). - */ - - return new MySqlConnection(CreateConnectionString()); - } - - private static string CreateConnectionString() + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + base.OnConfiguring(optionsBuilder); + try { - return ContextCreationOptions.CreateFromSettings().GetConnectionString(); + var options = ContextCreationOptions.CreateFromSettings(); + switch (options.Engine) + { + case ContextCreationOptions.DatabaseEngine.MySQL: + optionsBuilder.UseMySql(options.GetMySqlConnectionString()); + break; + case ContextCreationOptions.DatabaseEngine.SQLite: + optionsBuilder.UseSqlite(options.GetSQLiteConnectionString()); + break; + } } catch (Exception ex) { Debug.WriteLine("MainDbContext.CreateConnectionString() failed: {0}", ex.Message); - } - return FallbackConnectionString; + optionsBuilder.UseMySql(FallbackConnectionString); + } } - /// - /// Overridden. - /// - /// - protected override void OnModelCreating(DbModelBuilder modelBuilder) + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - modelBuilder.Configurations.AddFromAssembly(GetType().Assembly); - - /* Hint: We have to ignore certain properties from being mapped to the database. - * In favor of a clean design, we will in future clean up the database schema, i.e. split the lat/lng column in two. - */ - modelBuilder.ComplexType().Ignore(pl => pl.GeoLatitude); - modelBuilder.ComplexType().Ignore(pl => pl.GeoLongitude); + modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly); } #endregion @@ -119,6 +103,11 @@ void IUnitOfWork.Commit() SaveChanges(); } + public Task CommitAsync() + { + return SaveChangesAsync(); + } + #endregion } } diff --git a/Backend/Data/Contexts/MainDbContextConfiguration.cs b/Backend/Data/Contexts/MainDbContextConfiguration.cs deleted file mode 100644 index 7cdf038f..00000000 --- a/Backend/Data/Contexts/MainDbContextConfiguration.cs +++ /dev/null @@ -1,41 +0,0 @@ -// This file is part of AlarmWorkflow. -// -// AlarmWorkflow is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// AlarmWorkflow is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with AlarmWorkflow. If not, see . - -using System.Data.Entity; -using MySql.Data.Entity; -using MySql.Data.MySqlClient; - -namespace AlarmWorkflow.Backend.Data.Contexts -{ - /// - /// Implementation of to set up the required classes for the Entity Framework. - /// - class MainDbContextConfiguration : DbConfiguration - { - #region Constructors - - internal MainDbContextConfiguration() - { - SetDefaultConnectionFactory(new MySqlConnectionFactory()); - - SetProviderFactory("MySql.Data.MySqlClient", new MySqlClientFactory()); - SetProviderServices("MySql.Data.MySqlClient", new MySqlProviderServices()); - SetHistoryContext("MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema)); - SetMigrationSqlGenerator("MySql.Data.MySqlClient", () => new MySqlMigrationSqlGenerator()); - } - - #endregion - } -} diff --git a/Backend/Data/Contexts/MainDbContextInitializer.cs b/Backend/Data/Contexts/MainDbContextInitializer.cs deleted file mode 100644 index beb3aebd..00000000 --- a/Backend/Data/Contexts/MainDbContextInitializer.cs +++ /dev/null @@ -1,25 +0,0 @@ -// This file is part of AlarmWorkflow. -// -// AlarmWorkflow is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// AlarmWorkflow is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with AlarmWorkflow. If not, see . - -using System.Data.Entity; -using AlarmWorkflow.Backend.Data.Migrations; - -namespace AlarmWorkflow.Backend.Data.Contexts -{ - class MainDbContextInitializer : MigrateDatabaseToLatestVersion - { - - } -} diff --git a/Backend/Data/Contexts/MySqlHistoryContext.cs b/Backend/Data/Contexts/MySqlHistoryContext.cs deleted file mode 100644 index 0279bf80..00000000 --- a/Backend/Data/Contexts/MySqlHistoryContext.cs +++ /dev/null @@ -1,53 +0,0 @@ -// This file is part of AlarmWorkflow. -// -// AlarmWorkflow is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// AlarmWorkflow is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with AlarmWorkflow. If not, see . - -using System.Data.Common; -using System.Data.Entity; -using System.Data.Entity.Migrations.History; - -namespace AlarmWorkflow.Backend.Data.Contexts -{ - /// - /// Required class to support EF for MySQL. - /// - class MySqlHistoryContext : HistoryContext - { - #region Constructors - - internal MySqlHistoryContext(DbConnection connection, string defaultSchema) - : base(connection, defaultSchema) - { - - } - - #endregion - - #region Methods - - /// - /// Overridden. Downsizes some columns which are too large for MySQL... - /// - /// - protected override void OnModelCreating(DbModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity().Property(h => h.MigrationId).HasMaxLength(100).IsRequired(); - modelBuilder.Entity().Property(h => h.ContextKey).HasMaxLength(200).IsRequired(); - } - - #endregion - } -} diff --git a/Backend/Data/Data.csproj b/Backend/Data/Data.csproj index 0bf9d131..33ba83ce 100644 --- a/Backend/Data/Data.csproj +++ b/Backend/Data/Data.csproj @@ -18,18 +18,20 @@ pdbonly - - - + + + + + all + runtime; build; native; contentfiles; analyzers + + - - - + + + - - 201510221659525_Initial.cs - True True @@ -37,14 +39,15 @@ - - 201510221659525_Initial.cs - + - + - + + ResXFileCodeGenerator + Resources.Designer.cs + \ No newline at end of file diff --git a/Backend/Data/Data/IUnitOfWork.cs b/Backend/Data/Data/IUnitOfWork.cs index 49bcde28..0e00f054 100644 --- a/Backend/Data/Data/IUnitOfWork.cs +++ b/Backend/Data/Data/IUnitOfWork.cs @@ -15,6 +15,7 @@ using System; using AlarmWorkflow.Backend.Data.Types; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace AlarmWorkflow.Backend.Data { @@ -30,6 +31,8 @@ public interface IUnitOfWork : IDisposable /// IRepository For() where TEntity : EntityBase, new(); + DatabaseFacade Database { get; } + /// /// Commits all changes and persists them to the data context. /// diff --git a/Backend/Data/Data/Repository.cs b/Backend/Data/Data/Repository.cs index f5d8cc86..6638b0c4 100644 --- a/Backend/Data/Data/Repository.cs +++ b/Backend/Data/Data/Repository.cs @@ -13,9 +13,9 @@ // You should have received a copy of the GNU General Public License // along with AlarmWorkflow. If not, see . -using System.Data.Entity; using System.Linq; using AlarmWorkflow.Backend.Data.Types; +using Microsoft.EntityFrameworkCore; namespace AlarmWorkflow.Backend.Data { @@ -23,7 +23,7 @@ namespace AlarmWorkflow.Backend.Data { #region Fields - private DbContext _context; + private readonly DbContext _context; #endregion @@ -38,19 +38,13 @@ internal Repository(DbContext context) #region IRepository Members - IQueryable IRepository.Query - { - get { return _context.Set(); } - } + IQueryable IRepository.Query => _context.Set(); - TEntity IRepository.Create() - { - return new TEntity(); - } + TEntity IRepository.Create() => new TEntity(); TEntity IRepository.Insert(TEntity entity) { - return _context.Set().Add(entity); + return _context.Set().Add(entity).Entity; } void IRepository.Delete(TEntity entity) diff --git a/Backend/Data/Migrations/201510221659525_Initial.Designer.cs b/Backend/Data/Migrations/201510221659525_Initial.Designer.cs deleted file mode 100644 index ef31f672..00000000 --- a/Backend/Data/Migrations/201510221659525_Initial.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -namespace AlarmWorkflow.Backend.Data.Migrations -{ - using System.CodeDom.Compiler; - using System.Data.Entity.Migrations; - using System.Data.Entity.Migrations.Infrastructure; - using System.Resources; - - [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] - public sealed partial class Initial : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Initial)); - - string IMigrationMetadata.Id - { - get { return "201510221659525_Initial"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/Backend/Data/Migrations/201510221659525_Initial.cs b/Backend/Data/Migrations/201510221659525_Initial.cs deleted file mode 100644 index 8ee34751..00000000 --- a/Backend/Data/Migrations/201510221659525_Initial.cs +++ /dev/null @@ -1,112 +0,0 @@ -namespace AlarmWorkflow.Backend.Data.Migrations -{ - using System.Data.Entity.Migrations; - - /// - /// Represents the initial migration. - /// - public partial class Initial : DbMigration - { - /// - /// - /// - public override void Up() - { - CreateTable( - "dbo.dispresource", - c => new - { - id = c.Int(nullable: false, identity: true), - operation_id = c.Int(nullable: false), - timestamp = c.DateTime(nullable: false, precision: 0), - emkresourceid = c.String(nullable: false, unicode: false), - }) - .PrimaryKey(t => t.id) - .ForeignKey("dbo.operation", t => t.operation_id, cascadeDelete: true) - .Index(t => t.operation_id); - - CreateTable( - "dbo.operation", - c => new - { - id = c.Int(nullable: false, identity: true), - acknowledged = c.Boolean(nullable: false), - operationguid = c.Guid(nullable: false), - operationnumber = c.String(unicode: false), - timestampincome = c.DateTime(nullable: false, precision: 0), - timestampalarm = c.DateTime(nullable: false, precision: 0), - messenger = c.String(unicode: false), - comment = c.String(unicode: false), - plan = c.String(unicode: false), - picture = c.String(unicode: false), - priority = c.String(unicode: false), - einsatzortlocation = c.String(unicode: false), - einsatzortzipcode = c.String(unicode: false), - einsatzortcity = c.String(unicode: false), - einsatzortstreet = c.String(unicode: false), - einsatzortstreetnumber = c.String(unicode: false), - einsatzortintersection = c.String(unicode: false), - einsatzortproperty = c.String(unicode: false), - einsatzortlatlng = c.String(unicode: false), - zielortlocation = c.String(unicode: false), - zielortzipcode = c.String(unicode: false), - zielortcity = c.String(unicode: false), - zielortstreet = c.String(unicode: false), - zielortstreetnumber = c.String(unicode: false), - zielortintersection = c.String(unicode: false), - zielortproperty = c.String(unicode: false), - zielortlatlng = c.String(unicode: false), - keyword = c.String(unicode: false), - keywordmisc = c.String(unicode: false), - keywordb = c.String(unicode: false), - keywordr = c.String(unicode: false), - keywords = c.String(unicode: false), - keywordt = c.String(unicode: false), - loopscsv = c.String(unicode: false), - customdatajson = c.String(unicode: false), - }) - .PrimaryKey(t => t.id); - - CreateTable( - "dbo.operationresource", - c => new - { - id = c.Int(nullable: false, identity: true), - operation_id = c.Int(nullable: false), - timestamp = c.String(unicode: false), - fullname = c.String(unicode: false), - equipmentcsv = c.String(unicode: false), - }) - .PrimaryKey(t => t.id) - .ForeignKey("dbo.operation", t => t.operation_id, cascadeDelete: true) - .Index(t => t.operation_id); - - CreateTable( - "dbo.usersetting", - c => new - { - id = c.Int(nullable: false, identity: true), - identifier = c.String(nullable: false, unicode: false), - name = c.String(nullable: false, unicode: false), - value = c.String(unicode: false), - }) - .PrimaryKey(t => t.id); - - } - - /// - /// - /// - public override void Down() - { - DropForeignKey("dbo.operationresource", "operation_id", "dbo.operation"); - DropForeignKey("dbo.dispresource", "operation_id", "dbo.operation"); - DropIndex("dbo.operationresource", new[] { "operation_id" }); - DropIndex("dbo.dispresource", new[] { "operation_id" }); - DropTable("dbo.usersetting"); - DropTable("dbo.operationresource"); - DropTable("dbo.operation"); - DropTable("dbo.dispresource"); - } - } -} diff --git a/Backend/Data/Migrations/201510221659525_Initial.resx b/Backend/Data/Migrations/201510221659525_Initial.resx deleted file mode 100644 index 4038d374..00000000 --- a/Backend/Data/Migrations/201510221659525_Initial.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 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 - - - dbo - - \ No newline at end of file diff --git a/Backend/Data/Migrations/20181230152708_Initial.Designer.cs b/Backend/Data/Migrations/20181230152708_Initial.Designer.cs new file mode 100644 index 00000000..53a6bb3d --- /dev/null +++ b/Backend/Data/Migrations/20181230152708_Initial.Designer.cs @@ -0,0 +1,276 @@ +// +using System; +using AlarmWorkflow.Backend.Data.Contexts; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace AlarmWorkflow.Backend.Data.Migrations +{ + [DbContext(typeof(MainDbContext))] + [Migration("20181230152708_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.DispositionedResourceData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + b.Property("EmkResourceId") + .IsRequired() + .HasColumnName("emkresourceid"); + + b.Property("OperationId") + .HasColumnName("operation_id"); + + b.Property("Timestamp") + .HasColumnName("timestamp"); + + b.HasKey("Id"); + + b.HasIndex("OperationId"); + + b.ToTable("dispresource"); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.OperationData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + b.Property("AlarmAt") + .HasColumnName("timestampalarm"); + + b.Property("Comment") + .HasColumnName("comment"); + + b.Property("CustomData") + .HasColumnName("customdatajson"); + + b.Property("Guid") + .HasColumnName("operationguid"); + + b.Property("IncomeAt") + .HasColumnName("timestampincome"); + + b.Property("IsAcknowledged") + .HasColumnName("acknowledged"); + + b.Property("Loops") + .HasColumnName("loopscsv"); + + b.Property("Messenger") + .HasColumnName("messenger"); + + b.Property("OperationNumber") + .HasColumnName("operationnumber"); + + b.Property("Picture") + .HasColumnName("picture"); + + b.Property("Plan") + .HasColumnName("plan"); + + b.Property("Priority") + .HasColumnName("priority"); + + b.HasKey("Id"); + + b.ToTable("operation"); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.OperationResourceData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + b.Property("Equipment") + .HasColumnName("equipmentcsv"); + + b.Property("FullName") + .HasColumnName("fullname"); + + b.Property("OperationId") + .HasColumnName("operation_id"); + + b.Property("Timestamp") + .HasColumnName("timestamp"); + + b.HasKey("Id"); + + b.HasIndex("OperationId"); + + b.ToTable("operationresource"); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.SettingData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + b.Property("Identifier") + .IsRequired() + .HasColumnName("identifier"); + + b.Property("Name") + .IsRequired() + .HasColumnName("name"); + + b.Property("Value") + .HasColumnName("value"); + + b.HasKey("Id"); + + b.ToTable("usersetting"); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.DispositionedResourceData", b => + { + b.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData", "Operation") + .WithMany("DispositionedResources") + .HasForeignKey("OperationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.OperationData", b => + { + b.OwnsOne("AlarmWorkflow.Shared.Core.OperationKeywords", "Keywords", b1 => + { + b1.Property("OperationDataId"); + + b1.Property("B") + .HasColumnName("keywordb"); + + b1.Property("EmergencyKeyword") + .HasColumnName("keywordmisc"); + + b1.Property("Keyword") + .HasColumnName("keyword"); + + b1.Property("R") + .HasColumnName("keywordr"); + + b1.Property("S") + .HasColumnName("keywords"); + + b1.Property("T") + .HasColumnName("keywordt"); + + b1.HasKey("OperationDataId"); + + b1.ToTable("operation"); + + b1.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData") + .WithOne("Keywords") + .HasForeignKey("AlarmWorkflow.Shared.Core.OperationKeywords", "OperationDataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + b.OwnsOne("AlarmWorkflow.Shared.Core.PropertyLocation", "Einsatzort", b1 => + { + b1.Property("OperationDataId"); + + b1.Property("City") + .HasColumnName("einsatzortcity"); + + b1.Property("GeoLatLng") + .HasColumnName("einsatzortlatlng"); + + b1.Property("GeoLatitude"); + + b1.Property("GeoLongitude"); + + b1.Property("Intersection") + .HasColumnName("einsatzortintersection"); + + b1.Property("Location") + .HasColumnName("einsatzortlocation"); + + b1.Property("Property") + .HasColumnName("einsatzortproperty"); + + b1.Property("Street") + .HasColumnName("einsatzortstreet"); + + b1.Property("StreetNumber") + .HasColumnName("einsatzortstreetnumber"); + + b1.Property("ZipCode") + .HasColumnName("einsatzortzipcode"); + + b1.HasKey("OperationDataId"); + + b1.ToTable("operation"); + + b1.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData") + .WithOne("Einsatzort") + .HasForeignKey("AlarmWorkflow.Shared.Core.PropertyLocation", "OperationDataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + b.OwnsOne("AlarmWorkflow.Shared.Core.PropertyLocation", "Zielort", b1 => + { + b1.Property("OperationDataId"); + + b1.Property("City") + .HasColumnName("zielortcity"); + + b1.Property("GeoLatLng") + .HasColumnName("zielortlatlng"); + + b1.Property("GeoLatitude"); + + b1.Property("GeoLongitude"); + + b1.Property("Intersection") + .HasColumnName("zielortintersection"); + + b1.Property("Location") + .HasColumnName("zielortlocation"); + + b1.Property("Property") + .HasColumnName("zielortproperty"); + + b1.Property("Street") + .HasColumnName("zielortstreet"); + + b1.Property("StreetNumber") + .HasColumnName("zielortstreetnumber"); + + b1.Property("ZipCode") + .HasColumnName("zielortzipcode"); + + b1.HasKey("OperationDataId"); + + b1.ToTable("operation"); + + b1.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData") + .WithOne("Zielort") + .HasForeignKey("AlarmWorkflow.Shared.Core.PropertyLocation", "OperationDataId") + .OnDelete(DeleteBehavior.Cascade); + }); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.OperationResourceData", b => + { + b.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData", "Operation") + .WithMany("Resources") + .HasForeignKey("OperationId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/Data/Migrations/20181230152708_Initial.cs b/Backend/Data/Migrations/20181230152708_Initial.cs new file mode 100644 index 00000000..e77d3a74 --- /dev/null +++ b/Backend/Data/Migrations/20181230152708_Initial.cs @@ -0,0 +1,149 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace AlarmWorkflow.Backend.Data.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "operation", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + .Annotation("Sqlite:Autoincrement", true), + acknowledged = table.Column(nullable: false), + operationguid = table.Column(nullable: false), + operationnumber = table.Column(nullable: true), + timestampincome = table.Column(nullable: false), + timestampalarm = table.Column(nullable: false), + messenger = table.Column(nullable: true), + comment = table.Column(nullable: true), + plan = table.Column(nullable: true), + picture = table.Column(nullable: true), + priority = table.Column(nullable: true), + einsatzortlocation = table.Column(nullable: true), + einsatzortzipcode = table.Column(nullable: true), + einsatzortcity = table.Column(nullable: true), + einsatzortstreet = table.Column(nullable: true), + einsatzortstreetnumber = table.Column(nullable: true), + einsatzortintersection = table.Column(nullable: true), + Einsatzort_GeoLatitude = table.Column(nullable: true), + Einsatzort_GeoLongitude = table.Column(nullable: true), + einsatzortproperty = table.Column(nullable: true), + einsatzortlatlng = table.Column(nullable: true), + zielortlocation = table.Column(nullable: true), + zielortzipcode = table.Column(nullable: true), + zielortcity = table.Column(nullable: true), + zielortstreet = table.Column(nullable: true), + zielortstreetnumber = table.Column(nullable: true), + zielortintersection = table.Column(nullable: true), + Zielort_GeoLatitude = table.Column(nullable: true), + Zielort_GeoLongitude = table.Column(nullable: true), + zielortproperty = table.Column(nullable: true), + zielortlatlng = table.Column(nullable: true), + keyword = table.Column(nullable: true), + keywordmisc = table.Column(nullable: true), + keywordb = table.Column(nullable: true), + keywordr = table.Column(nullable: true), + keywords = table.Column(nullable: true), + keywordt = table.Column(nullable: true), + loopscsv = table.Column(nullable: true), + customdatajson = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_operation", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "usersetting", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + .Annotation("Sqlite:Autoincrement", true), + identifier = table.Column(nullable: false), + name = table.Column(nullable: false), + value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_usersetting", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "dispresource", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + .Annotation("Sqlite:Autoincrement", true), + operation_id = table.Column(nullable: false), + timestamp = table.Column(nullable: false), + emkresourceid = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_dispresource", x => x.id); + table.ForeignKey( + name: "FK_dispresource_operation_operation_id", + column: x => x.operation_id, + principalTable: "operation", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "operationresource", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + .Annotation("Sqlite:Autoincrement", true), + operation_id = table.Column(nullable: false), + timestamp = table.Column(nullable: true), + fullname = table.Column(nullable: true), + equipmentcsv = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_operationresource", x => x.id); + table.ForeignKey( + name: "FK_operationresource_operation_operation_id", + column: x => x.operation_id, + principalTable: "operation", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_dispresource_operation_id", + table: "dispresource", + column: "operation_id"); + + migrationBuilder.CreateIndex( + name: "IX_operationresource_operation_id", + table: "operationresource", + column: "operation_id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "dispresource"); + + migrationBuilder.DropTable( + name: "operationresource"); + + migrationBuilder.DropTable( + name: "usersetting"); + + migrationBuilder.DropTable( + name: "operation"); + } + } +} diff --git a/Backend/Data/Migrations/Configuration.cs b/Backend/Data/Migrations/Configuration.cs deleted file mode 100644 index 7c8064cb..00000000 --- a/Backend/Data/Migrations/Configuration.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Data.Entity.Migrations; -using AlarmWorkflow.Backend.Data.Contexts; - -namespace AlarmWorkflow.Backend.Data.Migrations -{ - sealed class Configuration : DbMigrationsConfiguration - { - public Configuration() - { - AutomaticMigrationsEnabled = false; - } - - protected override void Seed(MainDbContext context) - { - // This method will be called after migrating to the latest version. - - // You can use the DbSet.AddOrUpdate() helper extension method - // to avoid creating duplicate seed data. E.g. - // - // context.People.AddOrUpdate( - // p => p.FullName, - // new Person { FullName = "Andrew Peters" }, - // new Person { FullName = "Brice Lambson" }, - // new Person { FullName = "Rowan Miller" } - // ); - // - } - } -} diff --git a/Backend/Data/Migrations/MainDbContextModelSnapshot.cs b/Backend/Data/Migrations/MainDbContextModelSnapshot.cs new file mode 100644 index 00000000..cac1f434 --- /dev/null +++ b/Backend/Data/Migrations/MainDbContextModelSnapshot.cs @@ -0,0 +1,274 @@ +// +using System; +using AlarmWorkflow.Backend.Data.Contexts; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace AlarmWorkflow.Backend.Data.Migrations +{ + [DbContext(typeof(MainDbContext))] + partial class MainDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.DispositionedResourceData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + b.Property("EmkResourceId") + .IsRequired() + .HasColumnName("emkresourceid"); + + b.Property("OperationId") + .HasColumnName("operation_id"); + + b.Property("Timestamp") + .HasColumnName("timestamp"); + + b.HasKey("Id"); + + b.HasIndex("OperationId"); + + b.ToTable("dispresource"); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.OperationData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + b.Property("AlarmAt") + .HasColumnName("timestampalarm"); + + b.Property("Comment") + .HasColumnName("comment"); + + b.Property("CustomData") + .HasColumnName("customdatajson"); + + b.Property("Guid") + .HasColumnName("operationguid"); + + b.Property("IncomeAt") + .HasColumnName("timestampincome"); + + b.Property("IsAcknowledged") + .HasColumnName("acknowledged"); + + b.Property("Loops") + .HasColumnName("loopscsv"); + + b.Property("Messenger") + .HasColumnName("messenger"); + + b.Property("OperationNumber") + .HasColumnName("operationnumber"); + + b.Property("Picture") + .HasColumnName("picture"); + + b.Property("Plan") + .HasColumnName("plan"); + + b.Property("Priority") + .HasColumnName("priority"); + + b.HasKey("Id"); + + b.ToTable("operation"); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.OperationResourceData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + b.Property("Equipment") + .HasColumnName("equipmentcsv"); + + b.Property("FullName") + .HasColumnName("fullname"); + + b.Property("OperationId") + .HasColumnName("operation_id"); + + b.Property("Timestamp") + .HasColumnName("timestamp"); + + b.HasKey("Id"); + + b.HasIndex("OperationId"); + + b.ToTable("operationresource"); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.SettingData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + b.Property("Identifier") + .IsRequired() + .HasColumnName("identifier"); + + b.Property("Name") + .IsRequired() + .HasColumnName("name"); + + b.Property("Value") + .HasColumnName("value"); + + b.HasKey("Id"); + + b.ToTable("usersetting"); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.DispositionedResourceData", b => + { + b.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData", "Operation") + .WithMany("DispositionedResources") + .HasForeignKey("OperationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.OperationData", b => + { + b.OwnsOne("AlarmWorkflow.Shared.Core.OperationKeywords", "Keywords", b1 => + { + b1.Property("OperationDataId"); + + b1.Property("B") + .HasColumnName("keywordb"); + + b1.Property("EmergencyKeyword") + .HasColumnName("keywordmisc"); + + b1.Property("Keyword") + .HasColumnName("keyword"); + + b1.Property("R") + .HasColumnName("keywordr"); + + b1.Property("S") + .HasColumnName("keywords"); + + b1.Property("T") + .HasColumnName("keywordt"); + + b1.HasKey("OperationDataId"); + + b1.ToTable("operation"); + + b1.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData") + .WithOne("Keywords") + .HasForeignKey("AlarmWorkflow.Shared.Core.OperationKeywords", "OperationDataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + b.OwnsOne("AlarmWorkflow.Shared.Core.PropertyLocation", "Einsatzort", b1 => + { + b1.Property("OperationDataId"); + + b1.Property("City") + .HasColumnName("einsatzortcity"); + + b1.Property("GeoLatLng") + .HasColumnName("einsatzortlatlng"); + + b1.Property("GeoLatitude"); + + b1.Property("GeoLongitude"); + + b1.Property("Intersection") + .HasColumnName("einsatzortintersection"); + + b1.Property("Location") + .HasColumnName("einsatzortlocation"); + + b1.Property("Property") + .HasColumnName("einsatzortproperty"); + + b1.Property("Street") + .HasColumnName("einsatzortstreet"); + + b1.Property("StreetNumber") + .HasColumnName("einsatzortstreetnumber"); + + b1.Property("ZipCode") + .HasColumnName("einsatzortzipcode"); + + b1.HasKey("OperationDataId"); + + b1.ToTable("operation"); + + b1.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData") + .WithOne("Einsatzort") + .HasForeignKey("AlarmWorkflow.Shared.Core.PropertyLocation", "OperationDataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + b.OwnsOne("AlarmWorkflow.Shared.Core.PropertyLocation", "Zielort", b1 => + { + b1.Property("OperationDataId"); + + b1.Property("City") + .HasColumnName("zielortcity"); + + b1.Property("GeoLatLng") + .HasColumnName("zielortlatlng"); + + b1.Property("GeoLatitude"); + + b1.Property("GeoLongitude"); + + b1.Property("Intersection") + .HasColumnName("zielortintersection"); + + b1.Property("Location") + .HasColumnName("zielortlocation"); + + b1.Property("Property") + .HasColumnName("zielortproperty"); + + b1.Property("Street") + .HasColumnName("zielortstreet"); + + b1.Property("StreetNumber") + .HasColumnName("zielortstreetnumber"); + + b1.Property("ZipCode") + .HasColumnName("zielortzipcode"); + + b1.HasKey("OperationDataId"); + + b1.ToTable("operation"); + + b1.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData") + .WithOne("Zielort") + .HasForeignKey("AlarmWorkflow.Shared.Core.PropertyLocation", "OperationDataId") + .OnDelete(DeleteBehavior.Cascade); + }); + }); + + modelBuilder.Entity("AlarmWorkflow.Backend.Data.Types.OperationResourceData", b => + { + b.HasOne("AlarmWorkflow.Backend.Data.Types.OperationData", "Operation") + .WithMany("Resources") + .HasForeignKey("OperationId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/Data/Properties/Resources.Designer.cs b/Backend/Data/Properties/Resources.Designer.cs index 3a1b86f3..457ecdbd 100644 --- a/Backend/Data/Properties/Resources.Designer.cs +++ b/Backend/Data/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace AlarmWorkflow.Backend.Data.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -59,5 +59,14 @@ internal Resources() { resourceCulture = value; } } + + /// + /// Looks up a localized string similar to The database engine '{0}' could not be found. Use {1} instead .... + /// + internal static string EngineNotFound { + get { + return ResourceManager.GetString("EngineNotFound", resourceCulture); + } + } } } diff --git a/Backend/Data/Properties/Resources.resx b/Backend/Data/Properties/Resources.resx index 4fdb1b6a..587fe910 100644 --- a/Backend/Data/Properties/Resources.resx +++ b/Backend/Data/Properties/Resources.resx @@ -1,101 +1,123 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The database engine '{0}' could not be found. Use {1} instead ... + \ No newline at end of file diff --git a/Backend/Service/Service.csproj b/Backend/Service/Service.csproj index d112fb4d..98f8f12c 100644 --- a/Backend/Service/Service.csproj +++ b/Backend/Service/Service.csproj @@ -24,6 +24,7 @@ + diff --git a/Backend/ServiceContracts/Backend.config b/Backend/ServiceContracts/Backend.config index b4ae6a3e..e210bad1 100644 --- a/Backend/ServiceContracts/Backend.config +++ b/Backend/ServiceContracts/Backend.config @@ -8,11 +8,15 @@ + + + + diff --git a/BackendServices/Management/DataExtensions.cs b/BackendServices/Management/DataExtensions.cs index 63f66a9a..d656aed2 100644 --- a/BackendServices/Management/DataExtensions.cs +++ b/BackendServices/Management/DataExtensions.cs @@ -26,23 +26,24 @@ static class DataExtensions internal static OperationData ToData(this Operation operation) { - OperationData data = new OperationData(); - - data.Guid = operation.OperationGuid; - data.IsAcknowledged = operation.IsAcknowledged; - data.OperationNumber = operation.OperationNumber; - data.IncomeAt = operation.TimestampIncome; - data.AlarmAt = operation.Timestamp; - data.Comment = operation.Comment; - data.Messenger = operation.Messenger; - data.Plan = operation.OperationPlan; - data.Picture = operation.Picture; - data.Priority = operation.Priority; - data.Loops = operation.Loops.ToString(); - data.Einsatzort = operation.Einsatzort; - data.Zielort = operation.Zielort; - data.Keywords = operation.Keywords; - data.CustomData = JsonHelper.ToJson(operation.CustomData); + var data = new OperationData + { + Guid = operation.OperationGuid, + IsAcknowledged = operation.IsAcknowledged, + OperationNumber = operation.OperationNumber, + IncomeAt = operation.TimestampIncome, + AlarmAt = operation.Timestamp, + Comment = operation.Comment, + Messenger = operation.Messenger, + Plan = operation.OperationPlan, + Picture = operation.Picture, + Priority = operation.Priority, + Loops = operation.Loops.ToString(), + Einsatzort = operation.Einsatzort, + Zielort = operation.Zielort, + Keywords = operation.Keywords, + CustomData = JsonHelper.ToJson(operation.CustomData) + }; foreach (OperationResource item in operation.Resources) { @@ -54,7 +55,7 @@ internal static OperationData ToData(this Operation operation) internal static Operation ToOperation(this OperationData data) { - Operation operation = new Operation() + var operation = new Operation() { Id = data.Id, IsAcknowledged = data.IsAcknowledged, diff --git a/BackendServices/System/DatabaseChecker.cs b/BackendServices/System/DatabaseChecker.cs index c845a720..c9b75ee8 100644 --- a/BackendServices/System/DatabaseChecker.cs +++ b/BackendServices/System/DatabaseChecker.cs @@ -13,13 +13,15 @@ // You should have received a copy of the GNU General Public License // along with AlarmWorkflow. If not, see . -using System; -using System.Linq; -using System.Threading; using AlarmWorkflow.Backend.Data; using AlarmWorkflow.Backend.Data.Types; using AlarmWorkflow.Shared.Core; using AlarmWorkflow.Shared.Diagnostics; +using Microsoft.EntityFrameworkCore; +using MySql.Data.MySqlClient; +using System; +using System.Linq; +using System.Threading; namespace AlarmWorkflow.BackendService.System.Data { @@ -56,14 +58,35 @@ private static bool CheckDatabaseReachable(IServiceProvider services) */ using (IUnitOfWork work = services.GetService().Get().Create()) { + // Migrate to new database versions + work.Database.Migrate(); + work.For().Query.FirstOrDefault(); } return true; } - catch (Exception) + catch (MySqlException ex) + { + if (ex.Number == 1050) + { + // Table already exists. + // This happens when the user upgrade from the entity framework to entity framework core. + // The script tries to execute the migration, but the table already exist. So we have to add the migration by hand + // into the table. This is really dirty and should be changed .. + using (IUnitOfWork work = services.GetService().Get().Create()) + { + work.Database.ExecuteSqlCommand(@"REPLACE INTO `__efmigrationshistory` (`MigrationId`, `ProductVersion`) VALUES ('20181230152708_Initial', '2.2.0-rtm-35687');"); + } + } + else + { + Logger.Instance.LogException("DatabaseChecker", ex); + } + } + catch (Exception ex) { - // Intentionally left blank --> database not reachable or other error. + Logger.Instance.LogException("DatabaseChecker", ex); } return false;