c# - 设计指南 - 来自数据库中松散类型键/值的动态 ViewModel

标签 c# asp.net-mvc nhibernate razor architecture

我有一个让我头疼的场景,我认为我的方向是正确的,现在我发现了另一个问题。这很复杂,让我们从数据库开始:

我有一个表,其中包含一个键、一个值、一个 typeId 和一些其他属性。我所关心的只是键和值。我的 View 模型包含我从 nhibernate 在存储库层中使用的数据对象映射的域对象的列表。在这里:

public class DomainModel
{
    public string Key { get; set; }
    public string Value { get; set; }
    public TypeEnum Type { get; set; }
}

**注意,TypeEnum 与 Key 的 Value 的强类型无关。这只是对键/值进行分类的一种不同方式,以便我可以按该类型从数据库中提取它们。

简单。这是我的 View 模型:

public class ViewModel
{
    public List<DomainModel> Models { get; set; }
}

我遇到的问题是键的值是不同的数据类型。有时它们是 bool 值,我想要一个 Html.CheckBoxFor(model => model.Value),有时它们是字符串,一个 Html.TextBoxFor(model => model.Value) 就足够了。

这是我的 Razor View :

@foreach (var setting in Model.Models)
{
    <tr>
        <td>@Html.Label(setting.Key)</td>
        <td>@Html.TextBox(setting.Value)</td>
    </tr>
}

在此处切分应用程序的最佳区域是什么,并进行一些类型检查或其他操作,以便我在页面上具有适当的 Html 元素?我什至会怎么做呢?我在这里错过了一些非常明显和简单的东西吗?此外,如何根据键的值获取键的显示名称属性?它们目前只是 PacalCasedBunchedDescriptionNames。我是不是离设计太远了还是怎么的?

最佳答案

我最终所做的只是让 View 和 View 模型保持平坦,而不是尝试在那里进行一些动态类型暗示。这也让我能够保持我的验证和其他属性整洁。

域模型仍然是一样的,我实际上最终删除了类型,并在我的业务层中创建了一个 SaveDomainModel,因为每次我保存/更新一个集合时,它只针对一种类型的设置:

public class SaveDomainModel
{
    public List<DomainModel> DomainModels { get; set; }
    public SettingTypeEnum SettingType { get; set; }
}

我将我的 DomainModel 更改为:

public class DomainModel
{
    [Required]
    public string Key { get; set; }
    [Required]
    public string Value { get; set; }
}

并展平我的 ViewModel:

public class EditViewModel
{
    [DisplayName("Display Name:")]
    [Required]
    public int AnIntProp { get; set; }

    [DisplayName("Another Display Name:")]
    [Required]
    public string HereIsAString { get; set; }

    [DisplayName("Bool Display:")]
    [Required]
    public bool ImABool{ get; set; }
}

所以现在我的 Controller 对于 POST 看起来像这样:

[HttpPost]
public virtual ActionResult Edit(EditViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        SaveSettings(viewModel);
        return RedirectToAction(MVC.Settings.Edit());
    }
    return View(viewModel);
}

private void SaveSettings(EditViewModel viewModel)
{
    var settings = MapEditViewModelToDomainModels(viewModel);
    var saveDomainModel = new SaveDomainModel
    {
        DomainModels = settings,
        SettingType = SettingTypeEnum.Application
    };
    _settingsService.SaveSettings(saveDomainModel);
}

这确实是我之前没有发现的缺失链接,我在这篇文章中偶然发现了它:Enumerating through an object's properties (string) in C#

然后为了从平面 View 模型映射到域对象,我在 SaveSettings() 中使用了 Map... 函数。

private static List<DomainModel> MapEditViewModelToDomainModels(EditViewModel viewModel)
{
    var settings = new List<DomainModel>();

    var stringPropertyNamesAndValues = viewModel.GetType().GetProperties().Where(p => p.CanRead).Select(p => new {Name = p.Name, Value = p.GetValue(viewModel, null)});
    foreach (var pair in stringPropertyNamesAndValues)
    {
        var model= new DomainModel
        {
            Key = pair.Name,
            Value = pair.Value.ToString()
        };
        settings.Add(model);
    }

    return settings;
}

然后我就可以像这样保持我的视野平坦:

<tr>
    <td>@Html.LabelFor(model => model.SomeString)</td>
    <td>@Html.TextBoxFor(model => model.SomeString)</td>
</tr>
<tr>
    <td>@Html.LabelFor(model => model.SomeBoolean)</td>
    <td>@Html.CheckBoxFor(model => model.SomeBoolean)</td>
</tr>
...>

然后为了完成它,我在我的存储库中添加了一个 UpdateCollection(),显然,在从 DomainObj -> DataObj 映射之后,它在服务层中被调用。

public void SaveSettings(SaveDomainModel model)
{
    var settings = MapDomainModelToList(model).AsQueryable();
    _repository.UpdateCollection(settings);
}
private IEnumerable<DataObj> MapDomainModelToList(SaveDomainModel saveDomainModel)
{
    var settings = new List<Setting>();

    foreach (var domainModel in saveDomainModel.DomainModels)
    {
        var setting = GetSetting(domainModel.Key, saveDomainModel.SettingType);

        if (!String.Equals(setting.Value, domainModel.Value, StringComparison.CurrentCultureIgnoreCase))
        {
            setting.Value = domainModel.Value;
            setting.LastUpdated = DateTime.Now;
            settings.Add(setting);
        }
    }

    return settings;
}
public bool UpdateCollection(IQueryable<T> entities)
{
    using (var transaction = _session.BeginTransaction())
    {
        foreach (var entity in entities)
        {
            _session.Update(entity);
        }

        transaction.Commit();
    }
    return true;
}

关于c# - 设计指南 - 来自数据库中松散类型键/值的动态 ViewModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20863855/

相关文章:

c# - 使用未分配的变量 - for 循环

javascript - 如何解决 ASP.NET 使用子目录时的相对 url 问题?

nhibernate - 使用 session 每个请求时,如何让 NHibernate 重试死锁事务?

c# - 使用 RSACryptoServiceProvider 进行公钥加密

c# - 无法在 Visual Studio 2017 中复制文件 .exe

c# - 模拟 Controller 上下文和 UrlHelper

c# - .NET 中的单元测试和 UI 测试

c# - NHibernate vs Entity Framework 6 对大量用户的性能

asp.net - NHibernate - 访问同一个数据库的两个 SessionFactories

c# - 附加属性更改事件?