最终编辑:成功!有一个数据注释规则可以阻止空字符串被设置为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捕获。我完全失去了。
答案 0 :(得分:2)
我个人认为在数据库中存储它没有意义:
Id Description
1 Foo
2 ''
3 Bar
对我来说似乎很傻。
不是存储空白字段,而是让它可以为空。
如果您不同意,那么可以在数据库中设置默认值,并将 StoreGeneratedPattern 设置为 Computed 。
或者您可以拥有实体的ctor,它设置值。