Использовать параметризованный SQL.
Примеры
(Эти примеры приведены в C #, см. ниже для версии VB.NET.)
Замените свои конкатенации строк на @...
заполнители, а затем добавьте значения в свой SqlCommand. Вы можете свободно выбирать имя заполнителей, просто убедитесь, что они начинаются с знака @
. Ваш пример будет выглядеть так:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES (@someValue, @someOtherValue);";
using (var cmd = new SqlCommand(sql, myDbConnection))
{
cmd.Parameters.AddWithValue("@someValue", someVariable);
cmd.Parameters.AddWithValue("@someOtherValue", someTextBox.Text);
cmd.ExecuteNonQuery();
}
Тот же шаблон используется для других видов операторов SQL:
var sql = "UPDATE myTable SET myField1 = @newValue WHERE myField2 = @someValue;";
// see above, same as INSERT
или
var sql = "SELECT myField1, myField2 FROM myTable WHERE myField3 = @someValue;";
using (var cmd = new SqlCommand(sql, myDbConnection))
{
cmd.Parameters.AddWithValue("@someValue", someVariable);
using (var reader = cmd.ExecuteReader())
{
...
}
// Alternatively: object result = cmd.ExecuteScalar();
// if you are only interested in one value of one row.
}
Предупреждение: AddWithValue
является хорошей отправной точкой и прекрасно работает в большинстве случаев. Однако, что значение, которое вы передаете, должно точно соответствовать типу данных соответствующего поля базы данных. В противном случае вы можете оказаться в ситуации, когда преобразование предотвращает ваш запрос из с помощью индекса . Обратите внимание, что некоторые типы данных SQL Server, такие как char / varchar (без предшествующих «n») или даты, не имеют соответствующего типа данных .NET. В этих случаях вместо следует использовать Add
с правильным типом данных.
Зачем мне это делать?
O'Brien
не будет разбивать ваше приложение только потому, что он настаивает на сохранении своего странного имени. Другие библиотеки доступа к базе данных
?
вместо @...
в качестве заполнителя в SQL. В этом случае первый параметр AddWithValue
не имеет значения; вместо этого вам нужно добавить параметры в правильном порядке. То же самое верно для OdbcCommand . Небольшая заметка - этот подход:
Task<Customer> task = GetCustomers();
task.Wait()
работает для WinRT.
Позвольте мне объяснить:
private void TestMethod()
{
Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
task.Wait(); // wait executing the method
var customer = task.Result; // get's result.
Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
public Customer()
{
new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
}
public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
return Task.Run(() => new Customer
{
Name = "MyName"
});
}
Кроме того, этот подход работает только для решений для Windows Store!
Примечание. Этот способ не является потокобезопасным, если вы вызываете свой метод внутри другого метода асинхронизации (согласно комментариям @Servy)
Это хорошо работает для меня
public static class TaskHelper
{
public static void RunTaskSynchronously(this Task t)
{
var task = Task.Run(async () => await t);
task.Wait();
}
public static T RunTaskSynchronously<T>(this Task<T> t)
{
T res = default(T);
var task = Task.Run(async () => res = await t);
task.Wait();
return res;
}
}
Я узнаю об async / await и столкнулся с ситуацией, когда мне нужно синхронно вызывать метод async. Как я могу это сделать?
blockquote>Лучший ответ: нет, детали зависят от того, что такое «ситуация».
Это свойство getter / сеттер? В большинстве случаев лучше иметь асинхронные методы, чем «асинхронные свойства». (Для получения дополнительной информации см. мое сообщение в блоге о асинхронных свойствах ).
Является ли это MVVM-приложением и вы хотите выполнить привязку асинхронных данных? Затем используйте что-то вроде my
NotifyTask
, как описано в моей статье MSDN об асинхронной привязке данных .Является ли это конструктором? Тогда вы, вероятно, захотите рассмотреть асинхронный заводский метод. (Для получения дополнительной информации см. Сообщение в блоге в асинхронных конструкторах ).
Почти всегда есть лучший ответ, чем делать sync-over-async.
Если это невозможно для вашей ситуации (и вы знаете это, задав здесь вопрос , описывающий ситуацию ), я бы рекомендовал просто использовать синхронный код. Асинк всего наилучшего; синхронизация полностью наилучшая. Sync-over-async не рекомендуется.
Однако существует несколько ситуаций, когда требуется синхронизация по-асинхронному. В частности, вы ограничены вызывающим кодом, чтобы вы имели синхронизацию (и не имеете абсолютно никакого способа переосмыслить или переструктурировать свой код, чтобы разрешить асинхронность), и вы имеете для вызова асинхронного кода. Это очень редкая ситуация, но время от времени она появляется.
В этом случае вам нужно будет использовать один из хаков, описанный в моей статье о brownfield
async
development , в частности:
- Блокировка (например,
GetAwaiter().GetResult()
). Обратите внимание, что это может вызвать взаимоблокировки (как я описываю в своем блоге).- Запуск кода в потоке пула потоков (например,
Task.Run(..).GetAwaiter().GetResult()
). Обратите внимание, что это будет работать, только если асинхронный код может быть запущен в потоке пула потоков (т. Е. Не зависит от контекста пользовательского интерфейса или ASP.NET).- Вложенные петли сообщений. Обратите внимание, что это будет работать, только если асинхронный код принимает только однопоточный контекст, а не специфический контекст (много UI и ASP.NET-кода ожидают определенный контекст).
Вложенные петли сообщений являются наиболее опасными из всех хаков, потому что это вызывает повторное включение . Re-entrancy чрезвычайно сложно понять, и (IMO) является причиной большинства ошибок приложений в Windows. В частности, если вы находитесь в потоке пользовательского интерфейса и блокируете рабочую очередь (ожидая завершения работы async), тогда CLR на самом деле выполняет некоторую пересылку сообщений для вас - на самом деле это будет обрабатывать некоторые сообщения Win32 из вашего кода . О, и вы не знаете, какие сообщения - когда Крис Брумме говорит: «Было бы здорово точно знать, что будет накачано? К сожалению, накачка - это черное искусство, которое находится за пределами смертного понимание. , тогда мы действительно не надеемся узнать.
Итак, когда вы блокируете это в потоке пользовательского интерфейса, вы просите о проблемах. Другая цитата из одной статьи: «Время от времени клиенты внутри или вне компании обнаруживают, что мы перекачиваем сообщения во время управляемой блокировки в STA [поток пользовательского интерфейса]. Это законная проблема, потому что они знают, что это очень сложно для написания кода, который является надежным перед лицом повторной установки ».
Да, это так. Очень сложно написать код, который является надежным в плане повторного размещения. И вложенные петли сообщений force вы должны написать код, который является надежным перед лицом повторной установки. Вот почему принятый (и наиболее востребованный) ответ на этот вопрос является чрезвычайно опасным на практике.
Если вы полностью вне всех других опции - вы не можете переконфигурировать свой код, вы не можете реструктурировать его как асинхронный - вы вынуждены неизменным кодом вызова для синхронизации - вы не можете изменить нисходящий код для синхронизации - вы не можете блокировать - вы не может запускать асинхронный код в отдельном потоке - тогда и только после этого вы должны рассмотреть возможность включения reentrancy.
Если вы окажетесь в этом углу, я бы рекомендовал использовать что-то например
Dispatcher.PushFrame
для приложений WPF , зацикливание с помощьюApplication.DoEvents
для приложений WinForm и для общего случая, мой собственныйAsyncContext.Run
.
Этот ответ предназначен для всех, кто использует WPF для .NET 4.5.
Если вы попытаетесь выполнить Task.Run()
в потоке GUI, тогда task.Wait()
будет вешать бесконечно, если вы не имеют ключевое слово async
в определении вашей функции.
Этот метод расширения решает проблему, проверяя, есть ли мы в потоке графического интерфейса пользователя, и если да, выполняем задачу в потоке диспетчера WPF.
Этот класс может действовать как клей между миром async / await и неасинхронным / ожидающим миром в ситуациях, когда это неизбежно, например свойства MVVM или зависимости от других API, которые не используют async / ожидают.
/// <summary>
/// Intent: runs an async/await task synchronously. Designed for use with WPF.
/// Normally, under WPF, if task.Wait() is executed on the GUI thread without async
/// in the function signature, it will hang with a threading deadlock, this class
/// solves that problem.
/// </summary>
public static class TaskHelper
{
public static void MyRunTaskSynchronously(this Task task)
{
if (MyIfWpfDispatcherThread)
{
var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
result.Wait();
if (result.Status != DispatcherOperationStatus.Completed)
{
throw new Exception("Error E99213. Task did not run to completion.");
}
}
else
{
task.Wait();
if (task.Status != TaskStatus.RanToCompletion)
{
throw new Exception("Error E33213. Task did not run to completion.");
}
}
}
public static T MyRunTaskSynchronously<T>(this Task<T> task)
{
if (MyIfWpfDispatcherThread)
{
T res = default(T);
var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
result.Wait();
if (result.Status != DispatcherOperationStatus.Completed)
{
throw new Exception("Error E89213. Task did not run to completion.");
}
return res;
}
else
{
T res = default(T);
var result = Task.Run(async () => res = await task);
result.Wait();
if (result.Status != TaskStatus.RanToCompletion)
{
throw new Exception("Error E12823. Task did not run to completion.");
}
return res;
}
}
/// <summary>
/// If the task is running on the WPF dispatcher thread.
/// </summary>
public static bool MyIfWpfDispatcherThread
{
get
{
return Application.Current.Dispatcher.CheckAccess();
}
}
}
В вашем коде ваше первое ожидание выполнения задачи, но вы не запустили ее, так что она ждет бесконечно. Попробуйте следующее:
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Изменить:
Вы говорите, что получаете исключение. Пожалуйста, напишите более подробную информацию, включая трассировку стека. Mono содержит следующий тестовый пример:
[Test]
public void ExecuteSynchronouslyTest ()
{
var val = 0;
Task t = new Task (() => { Thread.Sleep (100); val = 1; });
t.RunSynchronously ();
Assert.AreEqual (1, val);
}
Проверьте, работает ли это для вас. Если это не так, но очень маловероятно, у вас может быть некоторая нечетная сборка Async CTP. Если это сработает, вам может потребоваться изучить, что именно генерирует компилятор, и как Task
экземпляр отличается от этого образца.
Редактировать # 2:
Я проверил с Reflector, что описанное вами исключение возникает, когда m_action
- null
. Это странно, но я не эксперт в Async CTP. Как я уже сказал, вы должны декомпилировать свой код и посмотреть, как именно создается Task
, каким образом его m_action
является null
.
P.S. В чем дело с случайными сокращениями? Учиться разрабатывать?
Это работает для меня
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
public static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static void RunSync(Func<Task> func)
{
_myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
}
class SomeClass
{
public async Task<object> LoginAsync(object loginInfo)
{
return await Task.FromResult(0);
}
public object Login(object loginInfo)
{
return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
//return this.LoginAsync(loginInfo).Result.Content;
}
}
class Program
{
static void Main(string[] args)
{
var someClass = new SomeClass();
Console.WriteLine(someClass.Login(1));
Console.ReadLine();
}
}
}
Почему бы не создать такой вызов, как:
Service.GetCustomers();
, который не является асинхронным.
Я думаю, что следующий вспомогательный метод также может решить проблему.
private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
{
TResult result = default(TResult);
var autoResetEvent = new AutoResetEvent(false);
Task.Run(async () =>
{
try
{
result = await func();
}
catch (Exception exc)
{
mErrorLogger.LogError(exc.ToString());
}
finally
{
autoResetEvent.Set();
}
});
autoResetEvent.WaitOne();
return result;
}
Может использоваться следующим образом:
InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
Я столкнулся с этим несколько раз, в основном в модульном тестировании или в разработке служб Windows. В настоящее время я всегда использую эту функцию:
var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
{
Trace.WriteLine("Task runSync Start");
await TaskEx.Delay(2000); // Simulates a method that returns a task and
// inside it is possible that there
// async keywords or anothers tasks
Trace.WriteLine("Task runSync Completed");
})).Unwrap();
Trace.WriteLine("Before runSync Wait");
runSync.Wait();
Trace.WriteLine("After runSync Waited");
Это просто, легко, и у меня не было проблем.
Удивленный никто не упомянул об этом:
public Task<int> BlahAsync()
{
// ...
}
int result = BlahAsync().GetAwaiter().GetResult();
Не так хорошо, как некоторые из других методов здесь, но он имеет следующие преимущества:
AggregateException
(например Result
) Task
, так и для Task<T>
( попробуйте сами! ) Кроме того, поскольку GetAwaiter
утиный, это должно работать для любого объекта, который возвращается из async (например, ConfiguredAwaitable
или YieldAwaitable
), а не только Задачи.
edit: Обратите внимание, что этот подход (или использование .Result
) возможен в тупик, если вы не убедитесь добавлять .ConfigureAwait(false)
каждый раз, когда вы ожидаете, для всех асинхронных методов, которые могут быть достигнуты с BlahAsync()
(а не только с теми, которые он вызывает напрямую). Объяснение .
// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
// all its descendants use ConfigureAwait(false)
// too. Then you can be sure that
// BlahAsync().GetAwaiter().GetResult()
// won't deadlock.
Если вы слишком ленивы, чтобы добавить .ConfigureAwait(false)
повсюду, и вы не заботитесь о производительности, вы также можете сделать
Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
Вы можете использовать CoRoutines . См. Реализацию Caliburn.Micro . У меня есть пользовательская реализация здесь .
private int GetSync()
{
try
{
ManualResetEvent mre = new ManualResetEvent(false);
int result = null;
Parallel.Invoke(async () =>
{
result = await SomeCalcAsync(5+5);
mre.Set();
});
mre.WaitOne();
return result;
}
catch (Exception)
{
return null;
}
}
использовать ниже код snip
Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
Попробуйте использовать следующий код для меня:
public async void TaskSearchOnTaskList (SearchModel searchModel)
{
try
{
List<EventsTasksModel> taskSearchList = await Task.Run(
() => MakeasyncSearchRequest(searchModel),
cancelTaskSearchToken.Token);
if (cancelTaskSearchToken.IsCancellationRequested
|| string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
{
return;
}
if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
{
RunOnUiThread(() => {
textViewNoMembers.Visibility = ViewStates.Visible;
taskListView.Visibility = ViewStates.Gone;
});
taskSearchRecureList = null;
return;
}
else
{
taskSearchRecureList = TaskFooterServiceLayer
.GetRecurringEvent(taskSearchList);
this.SetOnAdapter(taskSearchRecureList);
}
}
catch (Exception ex)
{
Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
}
}
Гораздо проще запустить задачу в пуле потоков, а не пытаться обмануть планировщика для его синхронного запуска. Таким образом, вы можете быть уверены, что это не затормозит. Эффект зависит от контекстного переключателя.
Task<MyResult> DoSomethingAsync() { ... }
// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());
// Will block until the task is completed...
MyResult result = task.Result;
Просто вызов .Result;
или .Wait()
- это риск блокировок, о котором многие говорили в комментариях. Поскольку большинство из нас, как oneliners, вы можете использовать их для .Net 4.5<
Приобретение значения с помощью метода async:
var result = Task.Run(() => asyncGetValue()).Result;
Синхронно вызывать метод асинхронизации
Task.Run(() => asyncMethod()).Wait();
Никаких проблем с блокировкой не произойдет из-за использования Task.Run
.
Источник:
Самый простой способ, который я нашел для запуска задачи синхронно и без блокировки потока пользовательского интерфейса, - использовать RunSynchronously () как:
Task t = new Task(() =>
{
//.... YOUR CODE ....
});
t.RunSynchronously();
В моем случае у меня есть событие, которое срабатывает, когда что-то происходит. Я не знаю, сколько раз это произойдет. Таким образом, я использую код выше в моем случае, поэтому всякий раз, когда он срабатывает, он создает задачу. Задачи выполняются синхронно, и это отлично работает для меня. Я был просто удивлен, что мне потребовалось столько времени, чтобы выяснить, насколько это просто. Обычно рекомендации намного сложнее и подвержены ошибкам. Это было просто и чисто.
Если я правильно читаю ваш вопрос - код, который хочет, чтобы синхронный вызов метода асинхронизации выполнялся в приостановленном потоке диспетчера. И вы хотите фактически синхронизировать этот поток до тех пор, пока не будет завершен метод async.
Асинхронные методы на C # 5 приводятся в действие путем эффективного измельчения метода на куски под капотом и возвращения Task
, который может отслеживать общее завершение всего шабанга. Однако, как выполняются методы прерывания, может зависеть от типа выражения, переданного оператору await
.
В большинстве случаев вы будете использовать await
для выражения типа Task
. Выполнение задачи await
шаблона является «умным», поскольку оно отбрасывает SynchronizationContext
, что в основном приводит к следующему:
await
, включен поток потока сообщений диспетчера или WinForms, он гарантирует, что куски асинхронного метода выполняются как часть обработки очереди сообщений. await
, находится в потоке пула потоков, то оставшиеся куски асинхронного метода встречаются в любом месте пула потоков. Вот почему вы, вероятно, сталкиваетесь с проблемами - реализация метода асинхронного тестирования пытается запустить остальные на диспетчере - даже если он приостановлен.
.... резервное копирование! ....
Я должен задать вопрос, , почему вы пытаетесь синхронно блокировать метод async? Это может привести к тому, что метод будет вызван асинхронно. В общем случае, когда вы начинаете использовать await
в методе Dispatcher или UI, вам нужно будет перевернуть весь ваш асинхронный поток пользовательского интерфейса. Например, если ваш столбец был примерно таким:
WebRequest.GetResponse()
YourCode.HelperMethod()
YourCode.AnotherMethod()
YourCode.EventHandlerMethod()
[UI Code].Plumbing()
- WPF
или WinForms
Код WPF
или WinForms
Message Loop Затем, как только код был преобразован для использования async, вы, как правило, получите
WebRequest.GetResponseAsync()
YourCode.HelperMethodAsync()
YourCode.AnotherMethodAsync()
YourCode.EventHandlerMethodAsync()
[UI Code].Plumbing()
- WPF
или WinForms
Код WPF
или WinForms
Message Loop Фактически Ответ
Класс AsyncHelpers выше фактически работает, потому что он ведет себя как вложенный цикл сообщений, но он устанавливает свой собственный параллельный механизм в диспетчер, вместо того, чтобы пытаться выполнить сам Диспетчер. Это один из способов решения вашей проблемы.
Другим обходным решением является выполнение вашего асинхронного метода в потоке threadpool, а затем дождаться его завершения. Делать это легко - вы можете сделать это со следующим фрагментом:
var customerList = TaskEx.RunEx(GetCustomers).Result;
Конечным API будет Task.Run (...), но с CTP вам понадобятся суффиксы Ex ( здесь ).
Рекомендовать этому ответу три года. Я написал его, основываясь главным образом на опыте с .Net 4.0 и очень мало с 4.5, особенно с async-await
. Вообще говоря, это приятное простое решение, но иногда это ломает ситуацию. Пожалуйста, прочитайте обсуждение в комментариях.
Просто используйте это:
// For Task<T>: will block until the task is completed...
var result = task.Result;
// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();
См.: TaskAwaiter , Task.Result , Task.RunSynchronously
Используйте это:
var x = (IAsyncResult)task;
task.Start();
x.AsyncWaitHandle.WaitOne();
... или это:
task.Start();
task.Wait();
Или вы могли бы просто пойти с:
customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;
Для этого для компиляции убедитесь, что вы ссылаетесь на сборку:
System.Net.Http.Formatting
В wp8:
Оберните его:
Task GetCustomersSynchronously()
{
Task t = new Task(async () =>
{
myCustomers = await GetCustomers();
}
t.RunSynchronously();
}
Вызовите его:
GetCustomersSynchronously();
Я нашел этот код в компоненте Microsoft.AspNet.Identity.Core, и он работает.
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None, TaskCreationOptions.None,
TaskContinuationOptions.None, TaskScheduler.Default);
// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap<TResult>().GetAwaiter().GetResult();
}