Kategorie szkoleń | Egzaminy | Kontakt
  • 1
  • 3
  • 61

Aplikacja, której dotyczy problem stworzona została z wykorzystaniem technologi: Asp.net v4.5, MVC, Oracle 11g, Entity Framework 6 – database first.

Jest to aplikacja intranetowa, której użytkowanie rozpoczyna podstrona z listą schematów bazy danych znajdujących się na serwerze Oracle.

Parametry połączenia określone są w zmiennych aplikacji.

Użytkownik wybiera interesujący go schemat bazy danych i kontynuuje pracę w kontekście wybranego przez siebie schematu.

Wszystkie schematy mają taką samą strukturę/model, różnią się jedynie danymi.

 

Chciałbym dowiedzieć się jak poradzić sobie z problemem dynamicznych parametrów połączeniowych w Entity Framework 6.

Dotychczasowe rozwiązanie umożliwia oczekiwane działanie, jednakże jest ono związane z dużymi niedogodnościami, co spróbuję zobrazować na przykładzie.

Użytkownik A – wybiera z listy, schemat bazy danych X i rozpoczyna dalsze korzystanie z aplikacji.

W tym momencie użytkownik B wybiera z listy schemat Y.

Powoduje to, że użytkownikowi A przełącza się schemat bazy danych na schemat Y.

Wobec tego zaproponowane przeze mnie rozwiązanie umożliwia dynamiczne definiowanie połączenia z bazą danych ale jedynie w kontekście całej aplikacji a nie pojedynczego użytkownika aplikacji - co jest błędem.

 

Definicja dynamicznych parametrów połączeniowych nie stanowi problemu.

Kłopotem jest informacja o schemacie bazy danych wykorzystywanej przez model EF, która znajduje się w plikach dll generowanych przez Entity Framework.

 

Kluczowe fragmenty mojego rozwiązania:

Kod wygenerowany przez EF6, wraz ze zmianami wprowadzonymi w celu umożliwienia pracy w zmiennym środowisku bazodanowym:

 

public partial class Entities : DbContext

    {

        public Entities()

            : base("metadata=res://*/Models.Model1.csdl|" + @Assembly.GetExecutingAssembly().Location.Replace(@"WebApplication10.dll", string.Empty) + "\\Models.Model1a.ssdl|res://*/Models.Model1.msl;provider=Oracle.ManagedDataAccess.Client ;provider connection string=';Data Source=" + Models.ReportConnectionModel.hostname + ":" + Models.ReportConnectionModel.port + "/" + Models.ReportConnectionModel.sid + ";password=" + Models.ReportConnectionModel.password + ";PERSIST SECURITY INFO=True;user id=" + Models.ReportConnectionModel.username + "';")

        {

        }

 

        protected override void OnModelCreating(DbModelBuilder modelBuilder)

        {

            throw new UnintentionalCodeFirstException();

        }

   

        public virtual DbSet<GROUND_UNIT> GROUND_UNIT { get; set; }

        public virtual DbSet<TACT_UNIT_PROTO> TACT_UNIT_PROTO { get; set; }

        public virtual DbSet<FARP> FARP { get; set; }

        public virtual DbSet<AIRBASE> AIRBASE { get; set; }

        public virtual DbSet<HIGH_RESOLUTION_UNIT> HIGH_RESOLUTION_UNIT { get; set; }

        public virtual DbSet<NAVAL_UNIT> NAVAL_UNIT { get; set; }

        public virtual DbSet<SQUADRON> SQUADRON { get; set; }

        public virtual DbSet<SUPPORT_UNIT> SUPPORT_UNIT { get; set; }

        public virtual DbSet<HUP_CS> HUP_CS { get; set; }

        public virtual DbSet<HUP_SC> HUP_SC { get; set; }

        public virtual DbSet<HUP_TARGET_TYPE> HUP_TARGET_TYPE { get; set; }

        public virtual DbSet<SUP_CS> SUP_CS { get; set; }

        public virtual DbSet<SUP_POT> SUP_POT { get; set; }

        public virtual DbSet<SUP_SC> SUP_SC { get; set; }

        public virtual DbSet<TUP_CS> TUP_CS { get; set; }

        public virtual DbSet<TUP_DIR> TUP_DIR { get; set; }

        public virtual DbSet<TUP_POT> TUP_POT { get; set; }

        public virtual DbSet<TUP_SC> TUP_SC { get; set; }

        public virtual DbSet<HIGHRES_UNIT_PROTOTYPE> HIGHRES_UNIT_PROTOTYPE { get; set; }

        public virtual DbSet<SHIP_UNIT_PROTO> SHIP_UNIT_PROTO { get; set; }

        public virtual DbSet<FACTION_COUNTRY> FACTION_COUNTRY { get; set; }

        public virtual DbSet<FORCE_SIDE> FORCE_SIDE { get; set; }

    }

}

 

Klasa udostępniająca funkcję przerabiającą pliki dll zawierające metadane modelu, a konkretniej, wpis dotyczący schematu bazy danych.

public class EntitySchemaConfigurator

    {

        public static void Create(string schema)

        {

             string[] args = { @Assembly.GetExecutingAssembly().Location.Replace(@"WebApplication10.dll", string.Empty), Assembly.GetExecutingAssembly().Location };

             if (args.Length > 0)

            {

                string folder = args.First();

                string dll = string.Empty;

                 if (System.IO.Directory.Exists(folder))

                 {

                    DirectoryInfo info = new DirectoryInfo(folder);

                     if (args.Length > 1)

                    {

                        dll = args[1];

                        Assembly assembly = Assembly.LoadFile(dll);

                        string[] resources =

                          assembly.GetManifestResourceNames();

                        string content = string.Empty;

                        foreach (var item in resources)

                        {

                            if (item.ToUpper().EndsWith("SSDL"))

                            {

                                using (Stream itemStream =

                                  assembly.

                                  GetManifestResourceStream(item))

                                {

                                    using (StreamReader reader =

                                      new StreamReader(itemStream))

                                    {

                                        content = reader.ReadToEnd();

                                        reader.Close();

                                    }

                                    itemStream.Close();

                                }

                                File.WriteAllText(

                                                string.Concat(info.FullName,

                                                @"\",

                                                item), content);

                            }

                        }

                    }

 

                    FileInfo[] files = info.GetFiles("*.ssdl",

                      System.IO.SearchOption.TopDirectoryOnly);

                    if (files != null &&

                        files.Length > 0 &&

                        files[0] != null)

                    {

                        foreach (var item in files)

                        {

                            string content = File

                                .ReadAllText(item.FullName);

                            content = content

                                .Replace(@"COEE_15",

                                  @schema);

                            File.WriteAllText(item.FullName.Replace(@"Models.Model1.ssdl",

                                  @"Models.Model1a.ssdl"), content);

                        }

                    }

                }

                else

                {

                    Console.WriteLine("Target folder is invalid");

                }

            }

            else

            {

                Console.WriteLine(@"Target folder is required

                    as command line parameter");

            }

         }

    }

 

Przykład korzystania z EF:

private Entities db = new Entities();

EntitySchemaConfigurator.Create(ReportConnectionModel.username);

 

 

Proszę o radę jak poradzić sobie z tym problemem, najlepiej bez przechodzenia z trybu database first na code first?

Krzysztof_Żwirek
  • Zapytał
  • @ Krzysztof_Żwirek | 18.05.2016
    • 4
    • 0
    • 2
Zaloguj się aby zadać pytanie
Pokrewne

Odpowiedź (1)

  • 3

Konstruktor klasy DbContext może przyjmować connection string lub obiekt DbConnection.
Zatem proponuję gdzieś przechowywać, np. w we własnym pliku konfiguracyjnym, słownik w stylu: login użytkownika - connection string.
I podczas logowania pobierać na podstawie loginu odpowiedni connection string. Następnie możesz go przekazać go w konstruktorze kontekstu.
W rezultacie dla każdego użytkownika powstanie osobna instancja kontekstu łącząca się do innej bazy danych.

Czy takie rozwiązanie Tobie odpowiada?

 

 

 

 

  • Odpowiedział
  • @ | 19.05.2016
  • TRENER ALTKOM AKADEMII