c# – 对于在ASP.NET MVC中放置内容的地方有点困惑

我正在开发我的第一个ASP.NET MVC应用程序,而且我对创建/更新某些数据感到困惑.

我有一个数据库表User,一个LinqToSql生成的部分类User,以及我自己的自定义部分类User.

我在我的用户版本上使用[Bind(Exclude =“Id,InsertDateTime,UpdateDateTime”)],因为我不希望用户编辑这些字段.

我还有一个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 …)]属性中吗?

更新

从目前为止的答案中,我认为澄清一些事情,至少对我自己而言.

首先,这是一个用户数据发生的位置列表:

>数据库中的用户表 – 处理Id分配.可以为InsertDateTime和UpdateDateTime提供默认值.
>用户类 – 使用GetRuleViolations()方法处理验证.
> UserRepository类 – 抽象出数据持久性函数(get,get all,add,delete和save).
> UserController类 – 处理用户请求和发布尝试(索引,详细信息,编辑,发布编辑,创建,发布创建和删除).
> UserFormViewModel类 – 提供要查看的强类型数据(用户对象以及下拉菜单的后备数据).
> Views / User / Create.aspx和Views / User / Edit.aspx – 生成html,通过将静态数据与动态数据(存储在视图模型中)相结合,向用户显示UI.

我目前的想法是,在概念上设置Id,UpdateDateTime和InsertDateTime的责任在于模型.数据库肯定负责在插入时设置Id,但对于我应该设置日期时间字段的位置仍然有点不清楚.似乎有两种选择:存储库(由@Aaronaught建议)或User类(已经处理验证).

至于在##########和(###)### – ####之间转换PhoneNumber的问题,这在概念上看起来更像是一个“视图”功能.我喜欢@Aaronaught关于拥有一个专门的PhoneNumberConverter类的想法,我可能会继续使用它,但仍有一个问题是谁在这个类上调用方法.为此,我倾向于我的UserFormViewModel类.

这引出了两个后续问题……

后续问题

>是否应在UserRepository类或User类中分配UpdateDateTime和InsertDateTime字段?
>从UserFormViewModel调用电话号码转换方法(在PhoneNumberConverter类上)是否有意义?

最佳答案 通常,您将拥有一个业务逻辑层,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中你可以使用Data Annotations – 在DataType枚举中实际上有一个PhoneNumber成员.
> Save方法(或Add或Update方法)接受一个未附加的实体并将其保存到数据库中.此方法检查ID并选择是否根据该ID插入或更新.无论哪种方式,它都会在数据库事务成功后更新传递给它的原始User对象.这也应该回答你的问题#1 – 如果你的数据库中有一个IDENTITY列,那么你的数据库负责生成ID;你没有在你的应用程序中生成它们.

另一种方法是使Save方法返回从实际存储在数据库中的任何内容初始化的全新User实例.换句话说,它在INSERT或UPDATE之后执行SELECT.虽然这确实更可靠,但是存在显着的性能折衷.

从技术上讲,存储库是模型的一部分,它应该在基本层面上回答“where”问题,尽管我倾向于将其视为一个单独的层.

我不确定这回答你的问题有多好 – 回答这样的“设计”问题有点棘手 – 但我发现这个设计在大多数情况下都能很好地工作,它遵循DRY原则并且很容易维护.

点赞