Entity Framework 4 с MVC 2 - борьба с неспособностью EF4 разрешить пустые строки в полях формы - PullRequest
0 голосов
/ 16 января 2011

ЗАКЛЮЧИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ: Успех! Существует правило аннотации данных, которое не допускает, чтобы пустые строки были установлены в нуль. Я смог использовать решение здесь ( Проверка на стороне сервера НЕОБХОДИМОГО свойства строки в MVC2 Entity Framework 4 не работает ). Работает как шарм сейчас. Оставшуюся часть своего поста я оставлю на всякий случай, если кто-нибудь сможет извлечь из него урок.

Решил переписать этот пост, чтобы подробно описать все, что могу. Длинный пост, учитывая весь код, поэтому, пожалуйста, потерпите меня.

Я пишу проект MVC 2, используя EF4 в качестве основы. Все мои столбцы БД не могут быть обнулены.

Как я уже говорил, у меня возникают проблемы с тем, что EF4 вызывает ConstraintException, когда я тестирую случай пустой формы. Исключение возникает из файла Designer.cs, особенно в коде, подобном следующему:

    [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
    [DataMemberAttribute()]
    public global::System.String GameTitle
    {
        get
        {
            return _GameTitle;
        }
        set
        {
            OnGameTitleChanging(value);
            ReportPropertyChanging("GameTitle");
            _GameTitle = StructuralObject.SetValidValue(value, false); // <-- this is where the exception is being thrown
            ReportPropertyChanged("GameTitle");
            OnGameTitleChanged();
        }
    }
    private global::System.String _GameTitle;
    partial void OnGameTitleChanging(global::System.String value);
    partial void OnGameTitleChanged();

Исключение НЕ перехватывается и обрабатывается MVC. Вместо этого я продолжаю получать неисследованное исключение и YSoD. Мой контроллер (извините за беспорядок - я пытался заставить работать проверку уровня репозитория, см. Ниже):

    [HttpPost]
    public ActionResult CreateReview([Bind(Prefix = "GameData")]Game newGame, int[] PlatformIDs)
    {
       /* try
        {
            _gameRepository.ValidateGame(newGame, PlatformIDs);
        }
        catch (RulesException ex)
        {
            ex.CopyTo(ModelState);
        }

        if (ModelState.IsValid)
        {
            return RedirectToAction("Index");
        }
        else
        {
            var genres = _siteDB.Genres.OrderBy(g => g.Name).ToList();
            var platforms = _siteDB.Platforms.OrderBy(p => p.PlatformID).ToList();

            List<PlatformListing> platformListings = new List<PlatformListing>();

            foreach(Platform platform in platforms)
            {
                platformListings.Add(new PlatformListing { Platform = platform, IsSelected = false });
            }

            var model = new AdminGameViewModel { GameData = newGame, AllGenres = genres, AllPlatforms = platformListings };
            return View(model);
        }*/

        if (PlatformIDs == null || PlatformIDs.Length == 0)
        {
            ModelState.AddModelError("PlatformIDs", "You need to select at least one platform for the game");
        }

        try
        {
            foreach(var p in PlatformIDs)
            {
                Platform plat = _siteDB.Platforms.Single(pl => p == pl.PlatformID);
                newGame.Platforms.Add(plat);
            }

            newGame.LastModified = DateTime.Now;

            if (ModelState.IsValid)
            {
                _siteDB.Games.AddObject(newGame);
                _siteDB.SaveChanges();

                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }
        }
        catch
        {
            return View();
        }
    }

Я пробовал несколько вещей, чтобы проверка работала. Первым был Стивен Сандерсон, «Пусть модель справится», как описано в его книге aPress MVC2. Я подумал, что лучшее место для проверки - это хранилище, так как в любом случае данные должны быть переданы туда, и я бы хотел, чтобы мой контроллер был тонким. Репо:

public class HGGameRepository : IGameRepository
{
    private HGEntities _siteDB = new HGEntities();

    public List<Game> Games
    {
        get { return _siteDB.Games.ToList(); }
    }

    public void ValidateGame(Game game, int[] PlatformIDs)
    {
        var errors = new RulesException<Game>();

        if (string.IsNullOrEmpty(game.GameTitle))
        {
            errors.ErrorFor(x => x.GameTitle, "A game must have a title");
        }

        if (string.IsNullOrEmpty(game.ReviewText))
        {
            errors.ErrorFor(x => x.ReviewText, "A review must be written");
        }

        if (game.ReviewScore <= 0 || game.ReviewScore > 5)
        {
            errors.ErrorFor(x => x.ReviewScore, "A game must have a review score, and the score must be between 1 and 5");
        }

        if (string.IsNullOrEmpty(game.Pros))
        {
            errors.ErrorFor(x => x.Pros, "Each game review must have a list of pros");
        }

        if (string.IsNullOrEmpty(game.Cons))
        {
            errors.ErrorFor(x => x.Cons, "Each game review must have a list of cons");
        }

        if (PlatformIDs == null || PlatformIDs.Length == 0)
        {
            errors.ErrorForModel("A game must belong to at least one platform");
        }

        if (game.Genre.Equals(null)  || game.GenreID == 0)
        {
            errors.ErrorFor(x => x.Genre, "A game must be associated with a genre");
        }

        if (errors.Errors.Any())
        {
            throw errors;
        }
        else
        {
            game.Platforms.Clear(); // see if there's a more elegant way to remove changed platforms

            foreach (int id in PlatformIDs)
            {
                Platform plat = _siteDB.Platforms.Single(pl => pl.PlatformID == id);
                game.Platforms.Add(plat);
            }

            SaveGame(game);
        }
    }

    public void SaveGame(Game game)
    {
        if (game.GameID == 0)
        {
            _siteDB.Games.AddObject(game);
        }

        game.LastModified = DateTime.Now;
        _siteDB.SaveChanges();
    }

    public Game GetGame(int id)
    {
        return _siteDB.Games.Include("Genre").Include("Platforms").SingleOrDefault(g => g.GameID == id);
    }

    public IEnumerable<Game> GetGame(string title)
    {
        return _siteDB.Games.Include("Genre").Include("Platforms").Where(g => g.GameTitle.StartsWith(title)).AsEnumerable<Game>();
    }

    public List<Game> GetGamesByGenre(int id)
    {
        return _siteDB.Games.Where(g => g.GenreID == id).ToList();
    }

    public List<Game> GetGamesByGenre(string genre)
    {
        return _siteDB.Games.Where(g => g.Genre.Name == genre).ToList();
    }

    public List<Game> GetGamesByPlatform(int id)
    {
        return _siteDB.Games.Where(g => g.Platforms.Any(p => p.PlatformID == id)).ToList();
    }

    public List<Game> GetGamesByPlatform(string platform)
    {
        return _siteDB.Games.Where(g => g.Platforms.Any(p => p.Name == platform)).ToList();
    }
}

}

Классы RulesException / Rule Violation (взятые из его книги):

public class RuleViolation
{
    public LambdaExpression Property { get; set; }
    public string Message { get; set; }
}

public class RulesException : Exception
{
    public readonly IList<RuleViolation> Errors = new List<RuleViolation>();
    private readonly static Expression<Func<object, object>> thisObject = x => x;

    public void ErrorForModel(string message)
    {
        Errors.Add(new RuleViolation { Property = thisObject, Message = message });
    }
}

public class RulesException<TModel> : RulesException
{
    public void ErrorFor<TProperty>(Expression<Func<TModel, TProperty>> property, string message)
    {
        Errors.Add(new RuleViolation { Property = property, Message = message });
    }
}

Это не сработало, поэтому я решил попробовать метод Криса Селлса с использованием IDataErrorInfo (как показано здесь: http://sellsbrothers.com/Posts/Details/12700). Мой код:

public partial class Game : IDataErrorInfo
{
    public string Error
    {
        get
        {
            if (Platforms.Count == 0)
            {
                return "A game must be associated with at least one platform";
            }

            return null;
        }
    }

    public string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "GameTitle":
                    if (string.IsNullOrEmpty(GameTitle))
                    {
                        return "Game must have a title";
                    }
                    break;

                case "ReviewText":
                    if (string.IsNullOrEmpty(ReviewText))
                    {
                        return "Game must have an associated review";
                    }
                    break;

                case "Pros":
                    if (string.IsNullOrEmpty(Pros))
                    {
                        return "Game must have a list of pros";
                    }
                    break;

                case "Cons":
                    if (string.IsNullOrEmpty(Cons))
                    {
                        return "Game must have a list of cons";
                    }
                    break;
            }

            return null;
        }
    }
}

}

Опять не получилось.

Попытка простых аннотаций данных:

[MetadataType(typeof(GameValidation))]
public partial class Game
{
    class GameValidation
    {
        [Required(ErrorMessage="A game must have a title")]
        public string GameTitle { get; set; }

        [Required(ErrorMessage = "A game must have an associated review")]
        public string ReviewText { get; set; }

        [Required(ErrorMessage="A game must have a list of pros associated with it")]
        public string Pros { get; set; }

        [Required(ErrorMessage="A game must have a set of cons associated with it")]
        public string Cons { get; set; }
    }
}

Также не сработало.

Общим знаменателем всего этого является то, что EF-конструктор создает исключение, а исключение не отслеживается MVC. Я в полной растерянности.

РЕДАКТИРОВАТЬ: Перекрестные сообщения отсюда: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/643e0267-fc7c-44c4-99da-ced643a736bf

Та же проблема с кем-то еще: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/4fee653a-4c8c-4a40-b3cf-4944923a8c8d/

1 Ответ

2 голосов
/ 16 января 2011

Лично я не вижу смысла хранить это в базе данных:

Id   Description
1    Foo
2    ''
3    Bar

Мне кажется глупым.

Вместо того, чтобы хранить пустые поля, вы делаете его обнуляемым.

Если вы не согласны, то вы можете установить значение по умолчанию в базе данных и установить для StoreGeneratedPattern значение Вычислено .

Или вы можете иметь ctor для объекта, который устанавливает значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...