Синхронная регистрация пользователей во время заполнения контекста

В моем проекте ASP.Net MVC 5 у меня есть собственный класс инициализатора:

class CustomInitializer : DropCreateDatabaseIfModelChanges<GhazanetContext>

Я хочу засеять контекст несколькими пользователями. Проблема в том, что новый MVC 5 — это async, и все, что я пытаюсь сделать, терпит неудачу. Вот мой код, который пытается создать пользователя:

    private void AddRestaurant(RegisterRestaurantViewModel model, DBContext db)
    {
        User user = new User(model.UserName) { Addresses = new List<Address>(), Role = "Restaurant" };
        user.Addresses.Add(model.Address);

        Task.Factory.StartNew(async () =>
            {
                await Users.Create(user); // GETS STUCK HERE

                var newUser = db.Users.Where(u => u.UserName == user.UserName).FirstOrDefault();
                var restaurant = new Restaurant(model) { User = newUser };
                db.Restaurants.Add(restaurant);
                db.SaveChanges();
                await Secrets.Create(new UserSecret(model.UserName, model.Password));
                await Logins.Add(new UserLogin(user.Id, IdentityConfig.LocalLoginProvider, model.UserName));

                const string role = "Restaurant";
                if (!await Roles.RoleExists(role))
                    await Roles.CreateRole(new Role(role));

                await Roles.AddUserToRole(role, user.Id);
            }).RunSynchronously();
    }
}

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


person Alireza Noori    schedule 04.09.2013    source источник
comment
у вас опечатка в слове "застрял", это не "сток"   -  person DarthVader    schedule 04.09.2013
comment
@ДартВейдер, лол. Спасибо. Вот что бывает, когда задаешь вопрос между двумя частями сна :D Кстати, в то время я был в акциях, может быть поэтому!   -  person Alireza Noori    schedule 04.09.2013
comment
Это действительно компилируется? Встроенные классы Users/Roles не имеют методов async.   -  person Stephen Cleary    schedule 04.09.2013
comment
@StephenCleary Да, это так. Я использую ASP.Net MVC 5. Новая версия async не имеет версии синхронизации. :(   -  person Alireza Noori    schedule 04.09.2013


Ответы (2)


Вы используете Task.Factory.StartNew и ExecuteSynchronously, ни один из которых не должен использоваться в коде async.

Лучший способ вызвать код async — это вызвать его из кода async следующим образом:

private async Task AddRestaurantAsync(RegisterRestaurantViewModel model, DBContext db)
{
    User user = new User(model.UserName) { Addresses = new List<Address>(), Role = "Restaurant" };
    user.Addresses.Add(model.Address);
    await Users.Create(user);
    var newUser = db.Users.Where(u => u.UserName == user.UserName).FirstOrDefault();
    var restaurant = new Restaurant(model) { User = newUser };
    db.Restaurants.Add(restaurant);
    db.SaveChanges();
    await Secrets.Create(new UserSecret(model.UserName, model.Password));
    await Logins.Add(new UserLogin(user.Id, IdentityConfig.LocalLoginProvider, model.UserName));

    const string role = "Restaurant";
    if (!await Roles.RoleExists(role))
        await Roles.CreateRole(new Role(role));

    await Roles.AddUserToRole(role, user.Id);
}

Это имеет каскадный эффект в вашей кодовой базе (вызывающие AddRestaurantAsync сами должны быть async и т. д.), но это самый простой и чистый способ вызова кода async. Существуют различные хаки для вызова асинхронного кода синхронным способом, но легко попасть в тупиковую ситуацию, и даже если вы все сделаете правильно, это повлияет на масштабируемость вашего сайта.

person Stephen Cleary    schedule 04.09.2013
comment
Спасибо. Проблема была не в том, что я не знал, как пользоваться async. Я пытался запустить методы async, такие как Users.Create(user);, синхронно. Однако, когда я прочитал ваш ответ, мне пришло в голову, что, поскольку тип возвращаемого значения функции обработки событий Seed равен void, я мог бы пометить его как async без изменения его подписи. Оно работает. Однако по какой-то причине я получаю сообщение об ошибке после инициализации базы данных. Он полностью функционален, так что особых проблем не возникает. Спасибо еще раз. - person Alireza Noori; 06.09.2013
comment
@AlirezaNoori: вам также следует избегать async void, если в этом нет крайней необходимости. Я рекомендую вам связаться с командой EF и спросить их, какова рекомендуемая практика. - person Stephen Cleary; 06.09.2013
comment
Хм. Знаете ли вы метод прямого контакта, такой как твиттер некоторых разработчиков? Каков ваш рекомендуемый способ связаться с ними? - person Alireza Noori; 06.09.2013
comment
@AlirezaNoori: я бы сначала проверил страницы проекта EF на наличие контактной информации. Если там ничего нет, попробуйте форумы MSDN. - person Stephen Cleary; 06.09.2013

Вы можете запускать асинхронную задачу синхронно: var result = Users.Create(user).Result

person graycrow    schedule 05.09.2013