использование свойств зависимостей вместе с асинхронным ctp

Я создал образец ViewModel для проверки использования DP с asyncCtp:

public class SampleVm : DependencyObject
{

    public static readonly DependencyProperty SampleDependencyPropertyProperty =
        DependencyProperty.Register("SampleDependencyProperty", typeof (int), typeof (SampleVm), new PropertyMetadata(default(int)));

    public int SampleDependencyProperty
    {
        get { return (int) GetValue(SampleDependencyPropertyProperty); }
        set { SetValue(SampleDependencyPropertyProperty, value); }
    }
    public ISampleModel _model;
    public SampleVm(ISampleModel model)
    {
        _model = model;
    }

    public async Task SetDependencyProperty()
    {
        var modelData = await TaskEx.Run(() => _model.GetSomeIntegerValue());
        SampleDependencyProperty = modelData;
    }
}

и модель, введенная в ViewModel:

public interface ISampleModel
{
    int GetSomeIntegerValue();
}

public class SampleModel : ISampleModel
{
    public int GetSomeIntegerValue()
    {
        return 10;
    }
}

когда я запускаю приложение WPF, проблем нет, но когда я хочу протестировать его со следующим кодом:

[Fact]
public async Task CheckValueSetting()
{
    var model = new Mock<ISampleModel>();
    model.Setup(x => x.GetSomeIntegerValue()).Returns(5);
    var viewModel =new SampleVm(model.Object);

    await viewModel.SetDependencyProperty();

    Assert.Equal(5, viewModel.SampleDependencyProperty);
}

Я получил следующую ошибку:

System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.

Server stack trace: 

    at System.Windows.Threading.Dispatcher.VerifyAccess()
    at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
    at WpfApplication4.SampleVm.set_SampleDependencyProperty(Int32 value) in SampleVM.cs: line 19
    at WpfApplication4.SampleVm.<SetDependencyProperty>d__1.MoveNext() in SampleVM.cs: line 30

    Exception rethrown at [0]: 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
    at WpfApplication4.SampleVmFixture.<CheckValueSetting>d__0.MoveNext() in SampleVmFixture.cs: line 16 

так какое решение?


person mehdi.loa    schedule 09.12.2012    source источник
comment
Могу я проверить: почему вы все еще используете асинхронную CTP? Разве теперь это не все в ядре .NET?   -  person Marc Gravell    schedule 09.12.2012
comment
Мне приходится использовать .Net framework 4 из-за ограничений в компании.   -  person mehdi.loa    schedule 09.12.2012
comment
это точно не имеет смысла. По определению CTP полностью не поддерживается и в данном случае уже устарел. Нет такой вселенной, в которой использование CTP было бы лучше, чем обновление. ИМО: сделайте обновление (даже если это означает некоторую внутреннюю работу) или забудьте об асинхронности. И наконец: большая часть асинхронных вещей все равно есть в 4.0!!!   -  person Marc Gravell    schedule 09.12.2012
comment
Ориентация на .NET 4 имеет смысл, особенно потому, что обновление 4.5 на месте не поддерживает XP. Вероятно, он просто не знает о Microsoft.Bcl.Async.   -  person Stephen Cleary    schedule 09.12.2012
comment
спасибо @MarcGravell и StephenCleary за их предложения. после прочтения этого вопрос и этот блог на MSDN Я думаю, что решение не в том, чтобы продолжать использовать AsyncCtp.   -  person mehdi.loa    schedule 09.12.2012


Ответы (1)


Во-первых, я рекомендую вам перейти на VS2012 с помощью Пакет Microsoft.Bcl.Async. Это позволит вам ориентироваться на .NET 4.0 с помощью новейших инструментов. В асинхронной CTP есть известные ошибки, которые не будут исправлены, а также проблемы с установкой (которые не будут исправлены), которые усложнят настройку новых машин для разработки.

Но прежде чем удалять асинхронную CTP, проверьте свой каталог (C# Testing) Unit Testing в каталоге асинхронной CTP. Вы найдете несколько типов, которые помогут в модульном тестировании, например GeneralThreadAffineContext:

[Fact]
public async Task CheckValueSetting()
{
    var model = new Mock<ISampleModel>();
    model.Setup(x => x.GetSomeIntegerValue()).Returns(5);
    SampleVm viewModel;
    await GeneralThreadAffineContext.Run(async () =>
    {
        viewModel = new SampleVm(model.Object);
        await viewModel.SetDependencyProperty();
    });

    Assert.Equal(5, viewModel.SampleDependencyProperty);
}
person Stephen Cleary    schedule 09.12.2012
comment
Пример из ОСАГО нигде больше не доступен? Кроме того, я думаю, вы забыли await результат Run(). - person svick; 09.12.2012
comment
Я не знаю официального источника дополнений из Async CTP; AsyncContext в моей библиотеке AsyncEx довольно близко соответствует GeneralThreadAffineContext, но я не знаю ничего эквивалентного WindowsFormsContext или WpfContext. Кроме того, Run возвращает void (он не завершается до тех пор, пока не будут завершены все методы async). - person Stephen Cleary; 10.12.2012
comment
У моего GeneralThreadAffineContext есть несколько перегрузок Run(): void Run(Action asyncAction) и Task Run(Func<Task> asyncMethod), и, поскольку вы используете второй, я думаю, вам нужно его await. - person svick; 10.12.2012
comment
@svick: Вы правы; Я модифицировал свою копию. Технически, эта Run перегрузка будет ждать завершения метода async, но await ничего страшного в этом нет. - person Stephen Cleary; 10.12.2012