Это довольно распространенная проблема. Вы используете | DataDirectory | строка подстановки. Это означает, что при отладке вашего приложения в среде Visual Studio база данных, используемая вашим приложением, находится в папке подпапки BIN\DEBUG
(или версии x86) вашего проекта. И это хорошо работает, так как у вас нет какой-либо ошибки, связанной с базой данных и выполняющей операции обновления.
Но затем вы выходите из сеанса отладки и просматриваете свою базу данных через Visual Studio Server Explorer. Это окно имеет другую строку подключения (возможно, указывая на копию вашей базы данных в папке проекта). Вы просматриваете свои таблицы, и вы не видите изменений.
Затем проблема ухудшается. Вы перезапускаете VS, чтобы искать ошибку в своем приложении, но у вас есть файл базы данных, указанный между вашими файлами проекта, а для свойства Copy to Output directory
установлено значение Copy Always
. На этом этапе Visual Studio копирует исходный файл базы данных из папки проекта в выходную папку (BIN \ DEBUG), и поэтому ваши предыдущие изменения теряются. Теперь ваше приложение снова вставляет / обновляет целевую таблицу и вы не можете найти никаких ошибок в коде. (Теперь я не знаю, являетесь ли вы таким парнем, но после двух или трех раз этого цикла я слышал разные уродливые слова)
Вы можете остановить эту проблему, изменив свойство Copy To Output Directory
- Copy If Newer
или Never Copy
. Также вы можете обновить строку соединения в проводнике сервера, чтобы посмотреть на рабочую копию базы данных или создать второе соединение. Первый по-прежнему указывает на базу данных в папке проекта, а второй указывает на базу данных в папке BIN \ DEBUG. Таким образом, вы можете сохранить исходную базу данных в целях развертывания и изменения схемы, в то время как со вторым соединением вы можете посмотреть на эффективные результаты ваших усилий по кодированию.
EDIT Специальное предупреждение для базы данных MS-Access пользователи. Простой взгляд на таблицу изменяет дату изменения базы данных ТАКЖЕ, если вы ничего не пишете и не меняете. Таким образом, флаг Copy if Newer
запускается, и файл базы данных копируется в выходной каталог. С Access лучше использовать Copy Never
.
Основным отличием между непосредственным вызовом (который является сокращением для Invoke(...)
) и использованием DynamicInvoke
является производительность; по моей оценке, в 700 с лишним раз (ниже).
При прямом / Invoke
подходе аргументы уже предварительно проверены через сигнатуру метода, и уже существует код для прямой передачи этих аргументов в метод (я бы сказал «как IL», но я, кажется, напомню что среда выполнения обеспечивает это напрямую, без никакого IL). С помощью DynamicInvoke
необходимо проверить их из массива с помощью отражения (то есть все ли они подходят для этого вызова; нужны ли они для распаковки и т. Д.); это медленно (если вы используете его в узком цикле), и его следует избегать, где это возможно.
Пример; Сначала результаты (я увеличил число LOOP
из предыдущего редактирования, чтобы дать разумное сравнение):
Direct: 53ms
Invoke: 53ms
DynamicInvoke (re-use args): 37728ms
DynamicInvoke (per-cal args): 39911ms
С кодом:
static void DoesNothing(int a, string b, float? c) { }
static void Main() {
Action<int, string, float?> method = DoesNothing;
int a = 23;
string b = "abc";
float? c = null;
const int LOOP = 5000000;
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method(a, b, c);
}
watch.Stop();
Console.WriteLine("Direct: " + watch.ElapsedMilliseconds + "ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.Invoke(a, b, c);
}
watch.Stop();
Console.WriteLine("Invoke: " + watch.ElapsedMilliseconds + "ms");
object[] args = new object[] { a, b, c };
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.DynamicInvoke(args);
}
watch.Stop();
Console.WriteLine("DynamicInvoke (re-use args): "
+ watch.ElapsedMilliseconds + "ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.DynamicInvoke(a,b,c);
}
watch.Stop();
Console.WriteLine("DynamicInvoke (per-cal args): "
+ watch.ElapsedMilliseconds + "ms");
}
На самом деле функциональной разницы между ними нет. если вы откроете реализацию в отражателе, вы заметите, что DynamicInvoke просто вызывает DynamicInvokeImpl с тем же набором аргументов. Никакой дополнительной проверки не выполняется, и это не виртуальный метод, поэтому его поведение не может быть изменено производным классом. DynamicInvokeImpl - это виртуальный метод, в котором выполняется вся фактическая работа.