我正在写一个必须始终具有某些值的对象。最值得注意的是,它必须始终具有Name
属性的值。
public class User
{
public string Name { get; set; }
public User(string name)
{
Name = name;
}
}
现在,我需要在这个类中实现一些业务规则。其中之一是Name
属性必须是唯一的名称。所以,我认为这个对象的初始化器看起来像这样:
public User(string name, IQueryable<User> allUsers)
{
var matches = allUsers.Where(q => q.Name == name).ToList();
if(matches.Any())
{
// abort object initialization
}
Name = name;
}
但我不确定如何中止对象初始化。事实上,甚至可能吗?
有没有办法中止对象初始化(即:将对象设置为null)还是有更好的方法来实现它?
答案 0 :(得分:6)
嗯,你只是抛出异常。但我根本不喜欢这种处理这个问题的方式。相反,您应该通过服务创建用户,并让服务检查名称是否有效。
答案 1 :(得分:4)
我想你可以在对象的构造函数或Name setter中检查并抛出异常,但是eeeehhhh可能会出现许多问题和混合问题。我说通过执行此检查的工厂创建对象并返回null(或一个名称很好的异常)。或者创建POCO对象并通过单独的类/方法进行验证。
答案 2 :(得分:3)
不要使用公共构造函数,而是使用像这样的方法和私有构造函数:
public static User CreateUser(string name)
{
// Check whether user already exists, if so, throw exception / return null
// If name didn't exist, record that this user name is now taken.
// Construct and return the user object
return new User(name);
}
private User(string name)
{
this.Name = name;
}
然后你的调用代码可以使用User myUser = User.CreateUser("Steve");
并相应地处理null return / exception。
值得补充的是,无论您使用哪种方法存储哪些用户名,都应该更新,以便在CreateUser方法中使用此名称。否则,如果您在将此对象存储在数据库或其他内容之前等待一段时间,您仍会遇到问题。我已经更新了上面的代码,以便更清楚。
答案 3 :(得分:3)
通过在构造函数中抛出异常来中止对象的初始化,并建议拒绝无效的输入。
public class User
{
public User(String name) {
if (String.IsNullOrWhiteSpace(name)) {
if (name == null) {
throw new System.ArgumentNullException("Cannot be null.", "name");
}
else {
throw new System.ArgumentException("Cannot be empty.", "name");
}
}
}
}
您希望在构造函数中定义的业务逻辑不适合那里。构造函数应该是轻量级的,并且只是实例化。查询某些数据源对于构造函数而言过于昂贵。因此,您应该使用工厂模式。对于工厂模式,调用者可能期望在创建对象时会有一些繁重的工作。
public class User
{
private User(String name) {
if (String.IsNullOrWhiteSpace(name)) {
if (name == null) {
throw new System.ArgumentNullException("Cannot be null.", "name");
}
else {
throw new System.ArgumentException("Cannot be empty.", "name");
}
}
}
public static User CreateUser(String name) {
User user = new User(name); // Lightweight instantiation, basic validation
var matches = allUsers.Where(q => q.Name == name).ToList();
if(matches.Any())
{
throw new System.ArgumentException("User with the specified name already exists.", "name");
}
Name = name;
}
public String Name {
get;
private set; // Optionally public if needed
}
}
您可以看到工厂模式更合适,并且因为它是调用者可能希望通过调用它来进行某些工作的方法。而对于构造函数,人们会期望它是轻量级的。
如果你想进入构造函数路由,那么你可能想尝试一些其他方法来强制执行业务规则,例如在尝试实际插入数据源时。
public class User
{
public User(String name) {
if (String.IsNullOrWhiteSpace(name)) {
if (name == null) {
throw new System.ArgumentNullException("Cannot be null.", "name");
}
else {
throw new System.ArgumentException("Cannot be empty.", "name");
}
}
}
}
public class SomeDataSource {
public void AddUser(User user) {
// Do your business validation here, and either throw or possibly return a value
// If business rules pass, then add the user
Users.Add(user);
}
}
答案 4 :(得分:2)
在创建用户之前,您应该检查重复的名称。
答案 5 :(得分:2)
就个人而言,我在实例化之前运行逻辑检查。例如:
if(UserLogic.PreInsertValidation(string username)){
User newUser = new User(username);
}
else{
// Handling - maybe show message on client "The username is already in use."
}
PreInsertValidation
将根据您的要求进行所有业务逻辑检查。
答案 6 :(得分:0)
您正在寻找的是identity map模式,或者某种模式。将此责任留在对象本身可能是错误的,应该在创建实体的组件中完成。当然,如果需要,地图应该是线程安全的,以避免竞争条件。
答案 7 :(得分:0)
我会在提交集合中的用户时处理这个问题。例如,如果您正在编辑用户集合,然后将其持久保存到数据库,则持久层将负责验证。我一直认为让一个对象负责维护所有其他对象就是不好的做法。当没有对象时,它会引入父对象与对象本身的关系。我建议实现一些类型的验证引擎来处理这个问题。
答案 8 :(得分:0)
不要在对象本身内部进行此验证,而是将此实体的创建,验证和保存放入服务中。当用户名不唯一时,此服务可以抛出ValidationException
,甚至可以开始事务以确保不会发生竞争条件。我使用的一个好模型是command / handler模式。这是一个例子:
public class CreateNewUserCommand
{
public string UserName { get; set; }
}
internal class CreateNewUserCommandHandler
: ICommandHandler<CreateNewUserCommand>
{
private readonly IUnitOfWork uow;
public CreateNewUserCommandHandler(
IUnitOfWork uow)
{
this.uow = uow;
}
public void Handle(CreateNewUserCommand command)
{
// TODO Validation
var user = new User { Name = command.Name };
this.uow.Users.InsertOnSubmit(user);
}
}
您甚至可以将验证添加到自己的类中。