我正在开发我的第一个ASP.NET MVC应用程序,而且我对创建/更新某些数据感到困惑。
我有一个数据库表User
,一个LinqToSql生成的部分类User
,以及我自己的自定义部分类User
。
我在[Bind(Exclude = "Id, InsertDateTime, UpdateDateTime")]
版本上使用User
,因为我不希望用户编辑这些字段。
我还有一个PhoneNumber
字段,我做希望用户编辑,但需要转换。我将其作为10个数字存储在数据库中,但当我通过视图将其显示给用户时,我将其转换为视图中的可读电话号码,如下所示:
string.Format("{0:(###) ###-####}", Convert.ToInt64(Model.User.PhoneNumber)).
问题是,当用户点击“保存”时,电话号码将始终采用错误的格式。某处,我需要删除所有非数字字符(括号,短划线,斜线和空格)。
问题
对于下面列出的每个字段,如何处理创建和编辑操作?
Id
- 我相信这是由SQL-Server自动处理的,因为我的所有Id字段都设置为IDENTITY (1, 1)
。我没有进行过广泛的测试,但这似乎“只是工作”。请确认。
InsertDateTime
- 我希望将此设置为DateTime.Now
仅用于创建操作,而不是用于编辑操作。那么,设置此值的适当位置在哪里:User,UserController,UserFormViewModel或其他什么?
UpdateDateTime
- 我希望对于“创建”和“编辑”操作都设置为DateTime.Now
,但同样,我应该在哪里放置执行此分配的代码?
PhoneNumber
- 与上述三个字段不同,此字段可由用户编辑,但需要在更新发生之前从(888) 123-4567
转换为8881234567
。这里有几个问题:(1)这种转变的适当位置在哪里?我在视图中将电话号码转换为“用户可读”格式,我应该将其转换回“数据库存储”格式? (2)我应该在PhoneNumber
属性中添加[Bind(Exclude...)]
吗?
更新
从答案到目前为止,我认为澄清一些事情,至少对我自己来说。
首先,这里是User
数据发生事件的位置列表:
User
表 - 处理Id赋值。可以为InsertDateTime和UpdateDateTime提供默认值。
User
类 - 使用GetRuleViolations()
方法处理验证。
UserRepository
class - 抽象出数据持久性函数(get,get all,add,delete和save)。
UserController
class - 处理用户请求和发布尝试(索引,详细信息,编辑,发布编辑,创建,发布创建和删除)。
UserFormViewModel
class - 提供要查看的强类型数据(User
对象以及下拉菜单的后备数据。)
Views/User/Create.aspx
和Views/User/Edit.aspx
- 生成html,通过将静态数据与动态数据(存储在视图模型中)相结合,向用户显示UI。
我目前的想法是,在概念上设置Id
,UpdateDateTime
和InsertDateTime
的责任在于模型。数据库肯定负责在插入时设置Id
,但我仍然不清楚应该在哪里设置日期时间字段。似乎有两种选择:存储库(由@Aaronaught建议)或User
类(已经处理验证)。
至于在##########和(###)### - ####之间转换PhoneNumber
的问题,这在概念上更像是一个“视图”功能。我喜欢@Aaronaught关于拥有一个专门的PhoneNumberConverter
课程的想法,我可能会考虑到这一点,但仍有一个问题是谁在这个课程上调用这些方法。为此,我倾向于我的UserFormViewModel
课程。
这引出了两个后续问题......
跟进问题
是否应在UpdateDateTime
班级或InsertDateTime
班级中分配UserRepository
和User
字段?
从PhoneNumberConverter
拨打电话号码转换方法(UserFormViewModel
类)是否有意义?
答案 0 :(得分:1)
这是我的答案:
1-是的,你是对的,它使用SQL服务器标识规范列“自动增量列”
完成 2-您可以在DB中将此字段的默认值设置为getdate()
sql函数,以便它首次在db中插入该值,并获取服务器日期时间值
3-这对于默认值也可以是相同的,但是在函数中你可以在ex中保存数据。如果您调用的行提交更改,请将此值设置为Datetime.Now。
4-第一部分我认为合适的地方将在[Post]方法版本上,我不认为你应该排除它。
答案 1 :(得分:1)
通常你会有一个业务逻辑层,MVCers称之为存储库 - 介于你的控制器和DAL之间。这通常是处理时间戳和数据转换的理想位置。
public class UserRepository : IUserRepository
{
private IDatabase db;
public UserRepository(IDatabase db)
{
if (db == null)
{
throw new ArgumentNullException("db");
}
this.db = db;
}
public void SaveUser(User user)
{
int userID = user.ID;
DateTime createDate = user.CreatedOn;
DateTime updateDate = DateTime.Now;
long phoneNumber = PhoneNumberConverter.Parse(user.PhoneNumber);
using (TransactionScope tsc = new TransactionScope())
{
if (user.ID == 0)
{
createDate = updateDate;
userID = db.InsertUser(user.Name, phoneNumber, createDate,
updateDate);
}
else
{
db.UpdateUser(user.ID, user.Name, phoneNumber, updateDate);
}
tsc.Complete();
}
user.ID = userID;
user.CreatedOn = createDate;
user.LastModified = updateDate;
}
}
请注意,我在这里做了一堆“假设”,比如使用TransactionScope
和某种名为IDatabase
的精简CRUD图层类型。 这些并不重要,它们只是用来说明工作流程:
有一些处理“业务逻辑”的“存储库”类 - 即在用户点击“保存”和实际进入数据库之间发生的所有事情。您可以像我一样实施单独的Add
和Update
方法,或单个Save
方法。
您需要在Add
/ Update
/ Save
方法中进行任何数据转换。这样做不取代了在UI级别进行验证的需要;我之前提到PhoneNumberConverter
的原因是您可能希望同时公开Validate
和Convert
方法,这样您的存储库和UI都可以依赖于相同的中央验证/转换逻辑。当然,在MVC 2.0中,您可以使用数据注释 - PhoneNumber
枚举中实际上有一个DataType
成员。
您的Save
方法(或Add
或Update
方法)采用未附加的实体并将其保存到数据库中。此方法检查ID并选择是否根据该ID插入或更新。无论哪种方式,它都会在数据库事务成功后更新传递给它的原始User
对象 。这也应该回答您的问题#1 - 如果您的数据库中有IDENTITY
列,那么您的数据库负责生成ID;你没有在你的应用程序中生成它们。
另一种方法是Save
方法返回从实际存储在数据库中的任何内容初始化的全新User
实例。换句话说,它在SELECT
或INSERT
后执行UPDATE
。虽然这确实有点可靠,但是存在显着的性能折衷。
从技术上讲,存储库是模型的一部分,它应该从基本层面回答“where”问题,尽管我倾向于将其视为一个单独的层。
我不确定这回答你的问题有多好 - 回答这样的“设计”问题有点棘手 - 但我发现这个设计在大多数情况下都能很好地工作,它遵循DRY原则并且很容易维持。