Deutsch
Germany.ruФорумы → Архив Досок→ Программирование

Снова запутался - строка подключения.

424  
Murr патриот25.06.19 12:16
Murr
25.06.19 12:16 

Снова запутался - строка подключения.


Дано:

Более-менее стандартный код получения данных из базы (тупо заполняем DataTable):

public DataTable FillDataTable(String sql)

{
Debug.Print(sql);


OracleConnection oraCN = new OracleConnection();
DataTable dt = new DataTable();


oraCN.ConnectionString = ConnectionString;


oraCN.Open();


OracleDataAdapter oraDA = new OracleDataAdapter(sql, oraCN);
oraDA.Fill(dt);
oraDA.Dispose();


oraCN.Close();
oraCN.Dispose();
return dt;
}



Код - один и тот же для вин- и веб- приложений.

Есть примерно такая же имплементация для Ms SQL & PostgreeSql.

Есть наработки с использованием всех трех баз попеременно и одновременно.


Строка подключения получется так:


internal TMapNameToConnectionModel.Key Key { get; set; }
private string connectionString = null;

internal string ConnectionString
{
get
{
if (connectionString == null)
{
string temp = String.Empty;
try
{
temp = TConnectionStringBuilder.GetConnectionString(Key);
}
catch (Exception ex)
{
string name = TMapNameToConnectionModel.Instance[Key];

TExceptionHandler.Error(ex, "Connection named '" + name + "' can't be obtained from config file");

}
if (temp != null && temp != String.Empty)
connectionString = temp;
}
return connectionString;
}
set
{
connectionString = value;
}
}


Т.е. если не известна - строим и кешируем.

Методика построения самой строки - не существенна - там несколько способов получения информации - несколько разных типов конфигов... Ну наследие, пока не починил...


Теперь - проблема.


Для тестирования в части тестов необходимо заменять строку подключеня - поинтить на тестовую базу.

Отсюда растут ноги у кеширования - поменял кеш - запоинтился на тестовую базу.

В данном случае ошибок быть не должно - часть тестов - деструктивные - дропают таблицы.

Выполнить все тесты на тестовой базе - пока не могу - надо долго готовить данные, а ни времени, ни желания это делать нет.

Т.е. от кеширования строки отказаться пока не могу.


Второй момент - веб-приложения.

Веб-приложение работает с тремя разными (не тестовыми) базами.

Доступ к базам определяется содержимым web.config в папке текущей страницы.

Имена строк подключения - одинаковые - просто другое содержимое строки.

Но. после первого подключения строка уже закеширована и не загружается из конфига.

Т.е. кешировать строку мне нельзя.


Т.е. с одной стороны - не могу без кеширования, с другой - не могу с кешированием.


Ну или упрощенно - как кешировать некешируемое некешируя кешируемое? смущ


Будут идеи как обойти проблему?


#1 
Программист коренной житель25.06.19 12:31
NEW 25.06.19 12:31 
в ответ Murr 25.06.19 12:16

Делаешь проперти ConnectionString виртуальной и в тестах, где тебе надо заменить БД на тестовую, просто меняешь возвращаемое значение.

Можешь это сделать сам (унаследовавшись), а можешь воспользоваться готовым фреймворком, например NSubstitute.

#2 
Murr патриот25.06.19 13:09
Murr
NEW 25.06.19 13:09 
в ответ Программист 25.06.19 12:31

Делаешь проперти ConnectionString виртуальной и в тестах, где тебе надо заменить БД на тестовую, просто меняешь возвращаемое значение.

-----

Ну это Я делаю и без виртуальности.

Ааа... кажется понял что ты имеешь в виду - сделать несколько имплементаций калассов с FillDataTable & virtual ConnectionString и подставлять нужный в каждый отдельный момент.

Увы, но - не решение - просто проблема выбора нужной коннектион стринг заменена проблемой выбора нужного класса.

Сама проблема - осталась.


Поясню:

app.config

<connectionStrings>

<add name="dbConnection" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=Nenagh)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED).../>

</connectionStrings>


в части тестов строка подключеня заменяется на

<connectionStrings>

<!-- DO NOT IN ANY CIRCUMSTANCES POINT Tests TO LIVE DATABASE - TESTS ARE DESTRUCTIVE -->

<add name="dbConnection" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=SandBox)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED) .../>
</connectionStrings>

Строка лежит в другом конфиге и читается отдельно.

Это - работает без проблем - в нужных тестах могу принудительно заменить одно на другое. И кешь мне в этом помогает - просто грубо меняю значение в кеше и все остальное работает без изменений.


На вебе немного по-другому.


web.config 1:

<connectionStrings>

<remove name="dbConnection" />
<add name="dbConnection" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=Nenagh)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED) .../>

</connectionStrings>

web.config 2:

<connectionStrings>
<remove name="dbConnection" />
<add name="dbConnection" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=Lurgan)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED) .../>
</connectionStrings>

Здесь - строка заменяется - делается автоматически веб-сервером, но нет (не знаю, не нашел) нотификации. Если Я буду читать конфиг - получу нужное значение.

А читать Я не могу т.к. значение уже - требование подмены в тестах - кешировано - могу только переписать.

А проверять, как в тестах, где именно Я нахожусь - возможности нет - страница одна и та же, просто копия лежит в папке с другим конфигом и менять это не желательно. Да и дотятуться туда не просто - дизайнил чтобы было нельзя.

Т.е. Я не знаю нужно мне пользовать кеш или нужно читать конфиг. И возможности установить это вроде нет.

Хотя... принудительно перечитать конфиг и переписать коннектион в бизнес-объекте веб-страницы? Ну может быть... жал' что работа дублируется...


#3 
Murr патриот25.06.19 13:15
Murr
NEW 25.06.19 13:15 
в ответ Murr 25.06.19 13:09

принудительно перечитать конфиг и переписать коннектион в бизнес-объекте веб-страницы?

-----

Не, нельзя - можно поменять ТОЛЬКО с текущего на SandBox.

Все остальное блокируется - подстраховался от шаловливых пальчиков коллег-собратьеv...

#4 
Программист коренной житель25.06.19 13:54
NEW 25.06.19 13:54 
в ответ Murr 25.06.19 13:09
Ааа... кажется понял что ты имеешь в виду - сделать несколько имплементаций калассов с FillDataTable & virtual ConnectionString и подставлять нужный в каждый отдельный момент.

Не надо никаких нескольких имплетентаций. Одной имплементации достаточно.

public class SomeClass
{
   public DataTable FillDataTable(String sql)
   {
     Debug.Print(sql);
...
     oraCN.ConnectionString = ConnectionString;
...
     return dt;
  }   
  public virtual string ConnectionString
  {
...
  }
}


Дальше есть 2 варианта:

1) делаешь свой фейк:

public class FakeSomeClass : SomeClass
{
   public string TestConnectionString { get private set; }
   public FakeSomeClass (string testConnectionString)
   {
      TestConnectionString = testConnectionString;
   }
   public override string ConnectionString
   {
      get
      {
        return TestConnectionString;
      }
   }
}

Далее в тесте используешь соответственно FakeSomeClass там, где нужно тестовая ДБ или оригинал, так где тестовая ДБ не нужна.


2) Используешь NSubstitute

В тесте просто пишешь:

[TestMethod]
public void MyTest() {
  var someClass = Substitute.ForPartsOf<SomeClass>();
  someClass.ConnectionString.Returns("My test connection string");
...
}

Ничего читать вообще не надо :)

#5 
Murr патриот25.06.19 16:20
Murr
NEW 25.06.19 16:20 
в ответ Программист 25.06.19 13:54

Далее в тесте используешь соответственно FakeSomeClass там, где нужно тестовая ДБ или оригинал, так где тестовая ДБ не нужна.

-----

Тест (деструктивный, с дропом таблицы):

[TestClass]
public class C_AT_Table_Tests
{
#region Tests Data

private Int16 UserID = 10;
private String UrlAbsolutePath = @"http://myUrl/path/doc.dot?param";

C_AT.Table table;

#endregion

#region Initalisation

[ClassInitialize]
public static void C_AT_Init(TestContext context)
{
TEnvironmentValidator.CheckTestEnvironment("SetUp, CleanUp and tests can be run only on test environment");
}

[ClassCleanup]
public static void C_AT_CleanUp()
{
//TEnvironmentValidator.CheckTestEnvironment("SetUp, CleanUp and tests can be run only on test environment");
}

[TestInitialize]
public void Test_Init()
{
// drop existing data
Sql.C_AT.Drop drop = new Sql.C_AT.Drop();
Assert.IsNotNull(drop);

TSysDba.SysDba.ExecCmd(drop);


// create empty table for test
Sql.C_AT.Create create = new Sql.C_AT.Create();
Assert.IsNotNull(create);

TSysDba.SysDba.ExecCmd(create);

// chech if there was an error
Assert.AreEqual(TExceptionHandler.Errors, 0);
}

[TestCleanup]
public void Test_CleanUp()
{
Sql.C_AT.Drop drop = new Sql.C_AT.Drop();
Assert.IsNotNull(drop);

TSysDba.SysDba.ExecCmd(drop);
}

#endregion

#region Tests

#region Constructor Tests

[TestMethod]
public void C_AT_Table_Constructor_1()
{
table = new C_AT.Table();
Assert.IsNotNull(table);
//Assert.IsInstanceOfType(table, Type.GetType("LiOrderEx.A.C_AT+Table"));
}

[TestMethod]
public void C_AT_Table_Constructor_2()
{
//table = (C_AT.Table)TLiOrderExFactory.Create(TKnownTables.C_AT);
table = new C_AT.Table();
string tn = table.GetType().ToString();
Assert.IsNotNull(table);
//Assert.IsInstanceOfType(table, Type.GetType("LiOrderEx.A.C_AT+Table,"));
}

#endregion

#region Methods Tests

[TestMethod]
public void C_AT_Table_Insert_LogAccess()
{
//table = (C_AT.Table)TLiOrderExFactory.Create(TKnownTables.C_AT);
table = new C_AT.Table();
Assert.IsNotNull(table);

table.Load(UserID);
Assert.IsFalse(table.HasData);

table.Insert_LogAccess(UserID, UrlAbsolutePath);

table.Load(UserID);
Assert.IsTrue(table.HasData);

//Assert.IsInstanceOfType(table, Type.GetType("LiOrderEx.A.C_AT+Table,"));
}

#endregion

#region Properties Tests
#endregion

#endregion

#region Support

private void InsertTestRecord()
{
C_AT.Table table = new C_AT.Table();
table.FillInitialData_1();
foreach (C_AT.Row row in table)
{
table.Save(row);
}
}

#endregion
}


Куда предлагается подставить FakeSomeClass?


Понимаю - будет вопрос по CheckTestEnvironment


public class TEnvironmentValidator
{
public static void CheckTestEnvironment()
{
CheckTestEnvironment("Thoose tests can be run ONLY on the test environment.");
}

public static void CheckTestEnvironment(string pMessage)
{
DbExec.Interfaces.ITestConnection connection = TExec.Instance as DbExec.Interfaces.ITestConnection;
Assert.IsNotNull(connection);

if (!connection.IsTestEnvironment)
Assert.Fail(pMessage);
}
}

Отсюда нельзя поменять ничего, Специально делал с невозможностью - защищался от возможных ошибок. Только проверка можно ли их раннать.

Если копать глубже, то упрусь в что-то закешенное - инстансе SomeClass или потомка - та же ситуация что и со строкой, но на другом уровне.


Подумал... хммм... в принципе именно твое предложение и реализовано. Только не наследованием, а имплементацией определенного интерфейса.

Но проблема - смена строки подключения - не ресолвится...


Видимо надо модифицировать TMapNameToConnectionModel.Key - добавлять признак - win-, web-, test-...

и модифицировать построитель строки...

#6 
Программист коренной житель25.06.19 17:01
NEW 25.06.19 17:01 
в ответ Murr 25.06.19 16:20

Мда, ну и тестики у тебя :) Жуть просто.


Ладно, я так понимаю, что проблема твоя в том, что при инициализации и при отчистке у тебя вызывается TSysDba.SysDba.ExecCmd(...), где ExecCmd - это FillDataTable из первого поста, SysDba - соответственно SomeClass из моего ответа.

Я так понимаю, что TSysDba.SysDba - это реализация синглтона. Соответственно тебе надо заинжектить фейк в этот синглтон.


Видимо надо модифицировать TMapNameToConnectionModel.Key - добавлять признак - win-, web-, test-...
и модифицировать построитель строки...

Для начала было бы неплохо прочитать книжку по тестированию :) Рекомендую вот эту: The Art of Unit Testing (в сети есть бесплатно в pdf)

#7 
Murr патриот25.06.19 17:15
Murr
NEW 25.06.19 17:15 
в ответ Murr 25.06.19 16:20

Хммм... кажется нащупал на чем прошлый раз прервали...


Делается так - пишется стандартный конфиг в нужном варианте - для простых тестов - подключение к живым базам, для деструктивных - к тестовым, для веба - как и было - по конфигу в нужной папке.

Кеширование в текущей имплементации - надо удалять и читать инфу из текущего конфига. Это решит проблему с доступом к актуальной строке подключения. Сложные обертки будут лишними, останется только кроссировка - модель(завод+база) => имя подключения. Должно быть dостаточно гибко для управления выбором строки.


Проблема с деструктивными тестами решается помещением оных в отдельную дллку (можно - много дллок, но не смешивать с тестами требующими живой базы).


Теперь надо придумать как жить с двумя app.config в конечной папке тестов.

Мелкомягкие не шибко любят раскиданные по папкам дллки.

Может заменить имя конфига?

Надо смотреть как это будет в тестах.

#8 
Программист коренной житель25.06.19 17:30
NEW 25.06.19 17:30 
в ответ Murr 25.06.19 17:15

А может быть ты просто примешь, что БД уже протестирована и не нуждается в дополнительном тестировании? Кроме того, даже если в БД и есть какая-то ошибка, ты все равно не сможешь ее исправить.


В результате ты придешь к нормальным юнит-тестам, которые будут тестировать исключительно твой код и не будут зависеть от сторонних библиотек.

#9 
Murr патриот25.06.19 17:41
Murr
NEW 25.06.19 17:41 
в ответ Программист 25.06.19 17:01

Жуть просто.

-----

Хорошо, что хоть какие-то есть - до меня вообще ничего не было.

Вот покрытие мне не нравится. Мало - тесты пишутся только на то, где возникают проблемы.



Соответственно тебе надо заинжектить фейк в этот синглтон.

-----

Неа... не надо инжектить фака никуда. Не будет фака. С факом Я все одно запутаюсь и помру на поиске ошибки.

Ну не Я - так пришедший мне на смену - пока разберется - угробит базы несколько раз.

Нужно "топорное" решение.

Топорным будет - подмена не строки - пусть она читается как читается стандартным методом, но замена имени строки которую надо читать - ее возьму из мапа.

Как страховку от дурака - оставлю проверку куда запоинтено - у серверов статические ИП - в деструктивных тестах.

Плохо - часть хардкожена, но все в маленькой кучке и прозрачно - один раз при смене ипишников можно поменять.

так будет поменьше писанины по изменению текущего кода.

Сейчас перепроверю как ужится с двумя app.config и буду писать...

Хотя... можно и без второго... вроде... не помню... устал...



Рекомендую

-----

Есть и даже читана.

Просто уже не надо - осталось 13 месяцев работы - не успею поправить даже 10% наличного кода.

#10 
Murr патриот25.06.19 18:01
Murr
NEW 25.06.19 18:01 
в ответ Программист 25.06.19 17:30

Так Я не базу тестирую.

Я из базы беру данные для тестирования.


Можно - мокать, но написание моков на базу в 700+ таблиц и весом в 22 Гб... хммм...


Просто нет времени выбрать нужные данные - пользую живые базы. Для недеструктивных тестов.


Есть ситуации, которые долго ждать в живых базах, либо которые не живут долго в живых базах.

Для них делаются тестовые данные в тестовых базах. Эти тесты - деструктивные. Не мокаю чтобы не выбиваться из структуры.


И да - скорость тестов меня не беспкоит - этим CI занимается - через час-другой переварит - нормально.


ты все равно не сможешь ее исправить.

-----

От меня этого и не ждут.

От меня ждут определения наличия и характера ошибки.

Как в базе, так и в нашем самопальном софте.

Ну и устранения где можно или рекомендаций по тому как не попадать в ситуации там где нельзя.

Лечение ЦВСки с аппострофами в именах вида - O'Brain, O'Conner - лечу заменой аппострофов в своем коде.

Тоже самое, но с введенным оператором три недели назад оппострофом - запрещаю его пользовать - не могу починить не свой код.


В результате ты придешь к нормальным юнит-тестам

------

Не приду. Времени нету.

Точнее - приду, но не на этом проекте.

#11 
Murr патриот25.06.19 18:02
Murr
NEW 25.06.19 18:02 
в ответ Murr 25.06.19 18:01

Ладушки - все на сегодня. Пора отдыхать - еще три рабочих дня впереди.

#12 
AlexNek патриот25.06.19 22:09
AlexNek
NEW 25.06.19 22:09 
в ответ Murr 25.06.19 17:15

А ты не думал что конфиги можно динамически менять. Я так с этим дурацким ЕФ делал. Для десктопа работает.

#13 
Murr патриот25.06.19 22:29
Murr
NEW 25.06.19 22:29 
в ответ AlexNek 25.06.19 22:09

А на вебе - перезапускается приложение.

Мне надо чтобы исполнитель запросов был один - что вин-, что веб-.

#14 
AlexNek патриот26.06.19 23:23
AlexNek
NEW 26.06.19 23:23 
в ответ Murr 25.06.19 22:29

Пишутъ что веба тоже мона

https://www.c-sharpcorner.com/article/how-to-changeswitch-...

https://stackoverflow.com/questions/40836102/asp-net-core-...

https://entityframework.net/knowledge-base/43993918/dynamic-connection-string-for-entity-framework-code-first-for-multiple-database-providers-at-runtime-ef6---mssql--mysql--oracle--


#15 
Murr патриот27.06.19 09:55
Murr
NEW 27.06.19 09:55 
в ответ AlexNek 26.06.19 23:23

Сменить - не проблема.

Проблема - знать когда сменить.

Ну да ладно - все нужные строки идут в конфиг и читаются из него стандартным методом.

Разбирательство и управление идут через смену имени выбираемой строки. Вот там пока немного заморчно сделано. Надо смотреть.

Через имя мне показалось сделать проще - не надо разбираться с тем куда коннектится - МС СКЛ, Оракле, Постргрее...

#16 
Murr_0002 знакомое лицо03.07.19 12:30
Murr_0002
NEW 03.07.19 12:30 
в ответ Murr 27.06.19 09:55

Починил.

Починил именно способ выборки строки подключения.

Дополнительно - очистился от груды "ненужного" кода в методах выборки строки подключения.

Полегчало это безобразие существенно.


Кроме этого - перебрал одну из ДАЛ-библиотек и ее тесты. Полтора дня.

Еще не прогнал тесты - надо убедится, что ни при каких обстоятельствах эти тесты не полезут

в рабочую базу.


Осталось - перебрать еще 96 почти таких же либ... и проверить что и они не полезут в живую базу.

Потом еще под 100 бизнес-объектов с веб-страниц подогнать под "Новый Стандарт" кода... там

как раз наоборот - должны работать с живыми данными.


Итого: 150 дней + 100 дней = 250 рабочих дней... т.е. полный рабочий год.


Жаль, что потом все это пойдет в мусорник - с переходом на новую систему нихрена из этого

работать не будет - там набор таблиц и структура таблиц - другие...

#17