none
Добавить в LoginForm DropDownList с выбором домена RRS feed

  • Вопрос

  • Здравствуйте
    Прошу помочь разобраться.

    Для простоты создал стандартное Интернет-приложение MVC3 C#
    Нужно кроме имени пользователя и пароля добавить в LoginForm DropDownList с выбором, например, домена.

    Добавил в модель:

    ...
    
    public class LogOnModel
    {
    // добавлено в accountmodels.cs
    public int DomainID { get; set; }
    public string DomainValue { get; set; }
    
    public IEnumerable<LogOnModel> DomainOptions = new List<LogOnModel>
    {
    new LogOnModel {DomainID = 0, DomainValue = "MyDomain"},
    new LogOnModel {DomainID = 1, DomainValue = "YourDomain"}
    };
    //
    
    [Required]
    [Display(Name = "Имя пользователя")]
    public string UserName { get; set; }
    ...
    
    

    ...

    [Display(Name = "Запомнить меня")] public bool RememberMe { get; set; } // [Display(Name = "Домен")] public string domain { get; set; } // } ...




    И в View

    @Html.DropDownListFor(m => m.domain, new SelectList(Model.DomainOptions, "DomainId", "DomainValue", Model.DomainOptions.First().DomainID))
    


    Visual Studio ошибок не находит, но при выполнении получаю ошибку

    NullReferenceException не обработано пользовательским кодом
    Ссылка на объект не указывает на экземпляр объекта

    Что у меня не так?

    26 сентября 2012 г. 5:53

Ответы

  • Ну я же сказал, что такое делать нельзя. Вот код

    ViewData.Model = new LogOnModel()
                      {
                        Domains = new List<Domain>(){
                      new Domain {DomainID = 0, DomainValue = "MyDomain"},
                      new Domain {DomainID = 1, DomainValue = "YourDomain"}
                      }, DomainName = "MyName"
                      };
    return View();

    Модель

    public class LogOnModel
      {
        public IEnumerable<Domain> Domains { get; set; }
        [Display(Name = "User name")]
        public string UserName { get; set; }
        public string DomainName { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
      }
      public class Domain
      {
        public int DomainID { get; set; }
        public string DomainValue { get; set; }
      }

    @model MvcApplication.Models.LogOnModel
    @Html.DropDownListFor(m => m.Domains, new SelectList(Model.Domains, "DomainId", "DomainValue", Model.Domains.First().DomainID))

    Так точно работает. Просто Вы смешиваете одно решение с другим.

    • Изменено YatajgaEditor 26 сентября 2012 г. 13:05
    • Предложено в качестве ответа YatajgaEditor 27 сентября 2012 г. 10:05
    • Помечено в качестве ответа YatajgaEditor 29 сентября 2012 г. 5:55
    26 сентября 2012 г. 12:28
    Модератор

Все ответы

  • Да, это ошибка времени выполнения. У Вас так один из объектов содержит null, вместо значения. Если исключение выпадает на DropDownList, то значит что либо Model.DomainOptions либо Model.DomainOptions.First().DomainID пустые. Посмотреть более детально маожно в отладчике или в деталях исключения. Скорее всего либо данные из базы не приходят, либо модель не заполняется.
    26 сентября 2012 г. 6:00
    Модератор
  • Спасибо за быстрый ответ

    Да, отладчик показывает, что Model.DomainOptions пустая. Но я, как мне кажется, её заполнил конкретными значениями

    public IEnumerable<LogOnModel> DomainOptions = new List<LogOnModel>
    {
    new LogOnModel {DomainID = 0, DomainValue = "MyDomain"},
    new LogOnModel {DomainID = 1, DomainValue = "YourDomain"}
    };

    И почему это может происходить - понять не могу

    26 сентября 2012 г. 6:19
  • Это Вы свойство модели заполняете, а саму модель, видимо нет? Покажите код контроллера.
    26 сентября 2012 г. 6:24
    Модератор
  • using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace MvcApplication2.Controllers
    {
        public class DomainOptionsController : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
    
        }
    }
    

    Это контроллер для этой модели. Остальные контроллеры не изменял
    26 сентября 2012 г. 6:50
  • Так Вы не передаёте там модель. Сделайте так например

    namespace MvcApplication2.Controllers
    {
        public class DomainOptionsController : Controller
        {
            public ActionResult Index()
            {
                ViewData.Model = new LogOnModel();
                return View();
            }
    
        }
    }

    26 сентября 2012 г. 6:54
    Модератор
  • Сделал. Без изменений((

    Добавил ещё

    using MvcApplication2.Models;

    иначе не узнавалось LogOnModel

    26 сентября 2012 г. 7:12
  • Так там у Вас ещё и DomainId пустой.

    ViewData.Model = new LogOnModel(){ DomainId = 1 };

    26 сентября 2012 г. 7:16
    Модератор
  • Сделал так:

    ViewData.Model = new LogOnModel() { DomainID = 1, DomainValue = "YourDomain" };

    Не помогло

    26 сентября 2012 г. 7:33
  • Так подождите. Недосмотрел я, такое делать нельзя

    public class LogOnModel
    {
    // добавлено в accountmodels.cs
    public int DomainID { get; set; }
    public string DomainValue { get; set; }
    
    public IEnumerable<LogOnModel> DomainOptions = new List<LogOnModel>
    {
    new LogOnModel {DomainID = 0, DomainValue = "MyDomain"},
    new LogOnModel {DomainID = 1, DomainValue = "YourDomain"}
    };

    стек переполнится, с выбросом исключения. Сделайте так

    ViewData.Model = new MyModel();

    public class MyModel
      {
        public IEnumerable<LogOnModel1> DomainOptions = new List<LogOnModel1>()
                      {
                      new LogOnModel1 {DomainID = 0, DomainValue = "MyDomain"},
                      new LogOnModel1 {DomainID = 1, DomainValue = "YourDomain"}
                      };
      }
      public class LogOnModel1
      {
        // добавлено в accountmodels.cs
        public int DomainID { get; set; }
        public string DomainValue { get; set; }
      }

    И переименуйте класс LogOnModel.

    26 сентября 2012 г. 7:51
    Модератор
  • А представление должно быть примерно такое:

    @model MvcApplication.Controllers.MyModel
    @Html.DropDownListFor(m => m.DomainOptions, new SelectList(Model.DomainOptions, "DomainId", "DomainValue", Model.DomainOptions.First().DomainID))

    Пространсво имён, вместо MvcApplication.Controllers должно быть вашим.

    26 сентября 2012 г. 7:54
    Модератор
  • Но так у меня отвалились LogonModel и все связанные с нею поля. Придётся втискивать их в MyModel. Не проще ли сделать наоборот?

    сейчас код такой: модель

        public class MyModel
        {
            public int DomainID { get; set; }
            public string DomainValue { get; set; }
            public IEnumerable<MyModel> DomainOptions = new List<MyModel>()
                      {
                      new MyModel {DomainID = 0, DomainValue = "MyDomain"},
                      new MyModel {DomainID = 1, DomainValue = "YourDomain"}
                      };
            [Display(Name = "Домен")]
            public string domain { get; set; }
        }
    
        public class LogOnModel
        {
           

    контрол

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using MvcApplication2.Models;
    
    namespace MvcApplication2.Controllers
    {
        public class DomainOptionsController : Controller
        {
            public ActionResult Index()
            {
                ViewData.Model = new MyModel();
                return View();
            }
    
        }
    }

    Представление

    @model MvcApplication2.Models.MyModel
    
    @{
        ViewBag.Title = "Вход";
    }
    
    ...
    //все поля связанные с LogonModel удалены
    
    @Html.DropDownListFor(m => m.domain, new SelectList(Model.DomainOptions, "DomainId", "DomainValue", Model.DomainOptions.First().DomainID))
    

    к тому же ошибка та же. ничего не изменилось

    26 сентября 2012 г. 8:54
  • "Придётся втискивать их в MyModel. Не проще ли сделать наоборот?" - сделайте наоборот, главное не делайте циклических ссылок, создающих сами себя.
    26 сентября 2012 г. 8:59
    Модератор
  • И поменяйте m => m.domain на m => m.DomainOptions.
    26 сентября 2012 г. 9:02
    Модератор
  • спасибо за то, что потратили на меня время, но проблема так и не решена...
    26 сентября 2012 г. 10:59
  • А что конкретно теперь не работает?
    26 сентября 2012 г. 11:23
    Модератор
  • Вернулся к самому первому исходнику

    И поменяйте m => m.domain на m => m.DomainOptions.

    так не работает. ошибка та же.

    Упразднил отдельный контрол

    DomainOptionsController.cs
    public ActionResult LogOn()
            {
                ViewData.Model = new LogOnModel() { DomainID = 1, DomainValue = "YourDomain" };
                return View();
            }

    и попытался как-то втиснуть модель DomainOptions в готовую модель LogonModel

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


    26 сентября 2012 г. 12:17
  • Ну я же сказал, что такое делать нельзя. Вот код

    ViewData.Model = new LogOnModel()
                      {
                        Domains = new List<Domain>(){
                      new Domain {DomainID = 0, DomainValue = "MyDomain"},
                      new Domain {DomainID = 1, DomainValue = "YourDomain"}
                      }, DomainName = "MyName"
                      };
    return View();

    Модель

    public class LogOnModel
      {
        public IEnumerable<Domain> Domains { get; set; }
        [Display(Name = "User name")]
        public string UserName { get; set; }
        public string DomainName { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
      }
      public class Domain
      {
        public int DomainID { get; set; }
        public string DomainValue { get; set; }
      }

    @model MvcApplication.Models.LogOnModel
    @Html.DropDownListFor(m => m.Domains, new SelectList(Model.Domains, "DomainId", "DomainValue", Model.Domains.First().DomainID))

    Так точно работает. Просто Вы смешиваете одно решение с другим.

    • Изменено YatajgaEditor 26 сентября 2012 г. 13:05
    • Предложено в качестве ответа YatajgaEditor 27 сентября 2012 г. 10:05
    • Помечено в качестве ответа YatajgaEditor 29 сентября 2012 г. 5:55
    26 сентября 2012 г. 12:28
    Модератор
  • Так тоже не работает

    Контрол

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using System.Web.Security;
    using MvcApplication2.Models;
    
    namespace MvcApplication2.Controllers
    {
        public class AccountController : Controller
        {
    
            //
            // GET: /Account/LogOn
    
            public ActionResult LogOn()
            {
                ViewData.Model = new List<LogOnModel>()
                      {
                      new LogOnModel {DomainID = 0, DomainValue = "MyDomain"},
                      new LogOnModel {DomainID = 1, DomainValue = "YourDomain"}
                      };
                return View();
            }
    
            //
            // POST: /Account/LogOn
    
            [HttpPost]
            public ActionResult LogOn(LogOnModel model, string returnUrl)
            {
    
    
    
                if (ModelState.IsValid)
                {
                    if (Membership.ValidateUser(model.UserName, model.Password))
                    {
                        FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                        if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                            && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                        {
                            return Redirect(returnUrl);
                        }
                        else
                        {
                            return RedirectToAction("Index", "Home");
                        }
                    }
                    else
                    {
                        ModelState.AddModelError("", "Имя пользователя или пароль указаны неверно.");
                    }
                }
    
                // Появление этого сообщения означает наличие ошибки; повторное отображение формы
                return View(model);
            }
    
            //
            // GET: /Account/LogOff
    
            public ActionResult LogOff()
            {
                FormsAuthentication.SignOut();
    
                return RedirectToAction("Index", "Home");
            }
    
            //
            // GET: /Account/Register
    
            public ActionResult Register()
            {
                return View();
            }
    
            //
            // POST: /Account/Register
    
            [HttpPost]
            public ActionResult Register(RegisterModel model)
            {
                if (ModelState.IsValid)
                {
                    // Попытка зарегистрировать пользователя
                    MembershipCreateStatus createStatus;
                    Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);
    
                    if (createStatus == MembershipCreateStatus.Success)
                    {
                        FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
                        return RedirectToAction("Index", "Home");
                    }
                    else
                    {
                        ModelState.AddModelError("", ErrorCodeToString(createStatus));
                    }
                }
    
                // Появление этого сообщения означает наличие ошибки; повторное отображение формы
                return View(model);
            }
    
            //
            // GET: /Account/ChangePassword
    
            [Authorize]
            public ActionResult ChangePassword()
            {
                return View();
            }
    
            //
            // POST: /Account/ChangePassword
    
            [Authorize]
            [HttpPost]
            public ActionResult ChangePassword(ChangePasswordModel model)
            {
                if (ModelState.IsValid)
                {
    
                    // При некоторых сценариях сбоя операция смены пароля ChangePassword вызывает исключение,
                    // а не возвращает значение false (ложь).
                    bool changePasswordSucceeded;
                    try
                    {
                        MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
                        changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
                    }
                    catch (Exception)
                    {
                        changePasswordSucceeded = false;
                    }
    
                    if (changePasswordSucceeded)
                    {
                        return RedirectToAction("ChangePasswordSuccess");
                    }
                    else
                    {
                        ModelState.AddModelError("", "Неправильный текущий пароль или недопустимый новый пароль.");
                    }
                }
    
                // Появление этого сообщения означает наличие ошибки; повторное отображение формы
                return View(model);
            }
    
            //
            // GET: /Account/ChangePasswordSuccess
    
            public ActionResult ChangePasswordSuccess()
            {
                return View();
            }
    
            #region Status Codes
            private static string ErrorCodeToString(MembershipCreateStatus createStatus)
            {
                // Полный список кодов состояния см. по адресу http://go.microsoft.com/fwlink/?LinkID=177550
                //.
                switch (createStatus)
                {
                    case MembershipCreateStatus.DuplicateUserName:
                        return "Имя пользователя уже существует. Введите другое имя пользователя.";
    
                    case MembershipCreateStatus.DuplicateEmail:
                        return "Имя пользователя для данного адреса электронной почты уже существует. Введите другой адрес электронной почты.";
    
                    case MembershipCreateStatus.InvalidPassword:
                        return "Указан недопустимый пароль. Введите допустимое значение пароля.";
    
                    case MembershipCreateStatus.InvalidEmail:
                        return "Указан недопустимый адрес электронной почты. Проверьте значение и повторите попытку.";
    
                    case MembershipCreateStatus.InvalidAnswer:
                        return "Указан недопустимый ответ на вопрос для восстановления пароля. Проверьте значение и повторите попытку.";
    
                    case MembershipCreateStatus.InvalidQuestion:
                        return "Указан недопустимый вопрос для восстановления пароля. Проверьте значение и повторите попытку.";
    
                    case MembershipCreateStatus.InvalidUserName:
                        return "Указано недопустимое имя пользователя. Проверьте значение и повторите попытку.";
    
                    case MembershipCreateStatus.ProviderError:
                        return "Поставщик проверки подлинности вернул ошибку. Проверьте введенное значение и повторите попытку. Если проблему устранить не удастся, обратитесь к системному администратору.";
    
                    case MembershipCreateStatus.UserRejected:
                        return "Запрос создания пользователя был отменен. Проверьте введенное значение и повторите попытку. Если проблему устранить не удастся, обратитесь к системному администратору.";
    
                    default:
                        return "Произошла неизвестная ошибка. Проверьте введенное значение и повторите попытку. Если проблему устранить не удастся, обратитесь к системному администратору.";
                }
            }
            #endregion
        }
    }

    Модель

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Globalization;
    using System.Web.Mvc;
    using System.Web.Security;
    
    namespace MvcApplication2.Models
    {
    
        public class ChangePasswordModel
        {
            [Required]
            [DataType(DataType.Password)]
            [Display(Name = "Текущий пароль")]
            public string OldPassword { get; set; }
    
            [Required]
            [StringLength(100, ErrorMessage = "Значение {0} должно содержать не менее {2} символов.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Новый пароль")]
            public string NewPassword { get; set; }
    
            [DataType(DataType.Password)]
            [Display(Name = "Подтверждение пароля")]
            [System.Web.Mvc.Compare("NewPassword", ErrorMessage = "Новый пароль и его подтверждение не совпадают.")]
            public string ConfirmPassword { get; set; }
        }
    
        
        public class LogOnModel
        {
            public int DomainID { get; set; }
            public string DomainValue { get; set; }
    
            [Required]
            [Display(Name = "Имя пользователя")]
            public string UserName { get; set; }
    
            [Required]
            [DataType(DataType.Password)]
            [Display(Name = "Пароль")]
            public string Password { get; set; }
    
            [Display(Name = "Запомнить меня")]
            public bool RememberMe { get; set; }
    
            
    
        }
    
        public class RegisterModel
        {
            [Required]
            [Display(Name = "Имя пользователя")]
            public string UserName { get; set; }
    
            [Required]
            [DataType(DataType.EmailAddress)]
            [Display(Name = "Адрес электронной почты")]
            public string Email { get; set; }
    
            [Required]
            [StringLength(100, ErrorMessage = "Значение {0} должно содержать не менее {2} символов.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Пароль")]
            public string Password { get; set; }
    
            [DataType(DataType.Password)]
            [Display(Name = "Подтверждение пароля")]
            [System.Web.Mvc.Compare("Password", ErrorMessage = "Пароль и его подтверждение не совпадают.")]
            public string ConfirmPassword { get; set; }
        }
    }
    

    Представление

    @model IEnumerable<MvcApplication2.Models.LogOnModel>
    
    @{
        ViewBag.Title = "Вход";
    }
    
    <h2>Вход</h2>
    <p>
        Введите имя пользователя и пароль. @Html.ActionLink("Зарегистрируйтесь", "Register"), если у вас нет учетной записи.
    </p>
    
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    
    @Html.ValidationSummary(true, "Не удалось выполнить вход. Исправьте ошибки и повторите попытку.")
    
    @using (Html.BeginForm()) {
        <div>
            <fieldset>
                <legend>Сведения учетной записи</legend>
    
                <div class="editor-label">
                    @Html.LabelFor(m => m.UserName)
                </div>
                <div class="editor-field">
                    @Html.TextBoxFor(m => m.UserName)
                    @Html.ValidationMessageFor(m => m.UserName)
                </div>
    
                <div class="editor-label">
                    @Html.LabelFor(m => m.Password)
                </div>
                <div class="editor-field">
                    @Html.PasswordFor(m => m.Password)
                    @Html.ValidationMessageFor(m => m.Password)
                </div>
    
                <div class="editor-label">
                    @Html.CheckBoxFor(m => m.RememberMe)
                    @Html.LabelFor(m => m.RememberMe)
                </div>
                
                <div>
                    
                   @Html.DropDownListFor(m => m, new SelectList(Model, "DomainId", "DomainValue", Model.First().DomainID))
    
                </div>
    
                <p>
                    <input type="submit" value="Вход" />
                </p>
            </fieldset>
        </div>
    }
    

    Ошибка компиляции

    Описание: Ошибка во время компиляции ресурса, необходимого для обслуживания этого запроса. Просмотрите следующие сведения о данной ошибке и измените соответственно исходный код.

    Сообщение об ошибке компилятора: CS1061: "System.Collections.Generic.IEnumerable<MvcApplication2.Models.LogOnModel>" не содержит определение для "UserName". Не удалось найти метод расширения "UserName", принимающий первый аргумент типа "System.Collections.Generic.IEnumerable<MvcApplication2.Models.LogOnModel>" (пропущена директива using или ссылка на сборку?)

    Ошибка источника:
    Строка 21: 
    Строка 22:             <div class="editor-label">
    Строка 23:                 @Html.LabelFor(m => m.UserName)
    Строка 24:             </div>
    Строка 25:             <div class="editor-field">

    Исходный файл: c:\Users\klintsoff\Documents\Visual Studio 2012\Projects\MvcApplication2\MvcApplication2\Views\Account\LogOn.cshtml    Строка: 23
    Отображать подробные выходные данные компилятора:
    Отображать полный исходный код для компиляции:

    26 сентября 2012 г. 12:58
  • Посмотрите изменил последнее решение, протестировал - работает. Сделайте точно так, работать будет.
    26 сентября 2012 г. 13:05
    Модератор
  • Спасибо. Заработало
    26 сентября 2012 г. 13:20
  • ...хотя DropDownList только отображается. При нажатии Вход получаем:

    System.InvalidOperationException не обработано пользовательским кодом
      HResult=-2146233079
      Message=Последовательность не содержит элементов

    26 сентября 2012 г. 14:07
  • А по подробнее? А вообще, то что Вы делаете не очень уместно для таких случаев. Помещение выпадающего списка ещё ничего не решает. Данные аутентификации передаются в конечном счёте в базу. Так, что там ещё есть что реализовать. А если всё это делается в учебных целях, тогда вперёд. А чтобы топик не разрастался, вопросы имеющие другой смысл лучше задавать в новой ветке.
    27 сентября 2012 г. 6:33
    Модератор
  • Да, цели учебные. Я работаю на php, но хочется освоить и другие технологии, в частности MVC.

    В имеющемся коде, который Вы мне помогли создать, я не очень понимаю куда именно передаётся результат  сделанного пользователем выбора DropDownList. Поэтому я сделал по аналогии с другими полями ввода:

    [Display(Name = "Domain")]
            public int Domainresult { get; set; }

     @Html.DropDownListFor(m => m.Domainresult, new SelectList(Model.Domains, "DomainId", "DomainValue", Model.Domains.First().DomainID))
    при выполнении конечного кода, который Вы помогли мне сделать вчера, всё та же ошибка, только уже после нажатия кнопки Submit (Вход)

    27 сентября 2012 г. 7:07
  • Она никуда не передаётся пока, просто отображается. А ошибка потому, что отрабатывает другой метод, модель не создаётся и значение пустым получается.
    27 сентября 2012 г. 7:12
    Модератор