实体框架4 w / MVC 2 - 反对EF4无法在表单字段中允许空字符串

时间:2011-01-16 00:45:25

标签: entity-framework-4

最终编辑:成功!有一个数据注释规则可以阻止空字符串被设置为null。我能够在这里使用解决方案(Server-side validation of a REQUIRED String Property in MVC2 Entity Framework 4 does not work)。现在就像一个魅力。如果有人可以从中学习,我会保留其余的帖子。

决定重写这篇文章,尽我所能。很长的帖子,考虑到所有代码,所以请耐心等待。

我正在编写一个使用EF4作为基础的MVC 2项目。我的所有数据库列都是不可为空的。

就像我之前说的那样,当我测试一个空表单的情况时,我遇到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();
        }
    }

我已经尝试了几种方法来验证工作。第一部是Steven Sanderson的“让模特处理它”,正如他的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 });
    }
}

那不起作用,所以我决定尝试使用Chris Sells使用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 个答案:

答案 0 :(得分:2)

我个人认为在数据库中存储它没有意义:

Id   Description
1    Foo
2    ''
3    Bar

对我来说似乎很傻。

不是存储空白字段,而是让它可以为空。

如果您不同意,那么可以在数据库中设置默认值,并将 StoreGeneratedPattern 设置为 Computed

或者您可以拥有实体的ctor,它设置值。