Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 设计指南-数据库中松散类型键/值的动态视图模型_C#_Asp.net Mvc_Nhibernate_Razor_Architecture - Fatal编程技术网

C# 设计指南-数据库中松散类型键/值的动态视图模型

C# 设计指南-数据库中松散类型键/值的动态视图模型,c#,asp.net-mvc,nhibernate,razor,architecture,C#,Asp.net Mvc,Nhibernate,Razor,Architecture,我有一个让我头疼的场景,我认为我走的方向是正确的,现在我发现了另一个问题。这很复杂,让我们从数据库开始: 我有一个表,表中有一个键、一个值、一个typeId和一些其他属性。我关心的只是关键和价值。我的viewmodel包含我从nhibernate在存储库层中使用的数据对象映射的域对象列表。这是: public class DomainModel { public string Key { get; set; } public string Value { get; set; }

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

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

public class DomainModel
{
    public string Key { get; set; }
    public string Value { get; set; }
    public TypeEnum Type { get; set; }
}
**注意,TypeEnum与键的值的强类型无关。这只是对键/值进行分类的另一种方式,这样我就可以按该类型从数据库中提取它们

简单。这是我的viewmodel:

public class ViewModel
{
    public List<DomainModel> Models { get; set; }
}
公共类视图模型
{
公共列表模型{get;set;}
}
我的问题是键的值是不同的数据类型。有时它们是布尔值,我想要一个Html.CheckBoxFor(model=>model.Value),有时它们是字符串,一个Html.TextBoxFor(model=>model.Value)就足够了

以下是我的观点:

@foreach (var setting in Model.Models)
{
    <tr>
        <td>@Html.Label(setting.Key)</td>
        <td>@Html.TextBox(setting.Value)</td>
    </tr>
}
@foreach(Model.Models中的变量设置)
{
@Label(setting.Key)
@Html.TextBox(setting.Value)
}

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

一种方法是只使用MVC特性来实现这一点,而不涉及nhibernate的细节。我是说,nhibernate只是你的数据层,对吗?这根本不会影响您的表示层

您的模型上已经有了
TypeEnum
属性。 我猜这将定义属性是否应显示为复选框、文本框或其他任何形式。。。如果是这样,请为您的
DomainModel
类型编写一个自定义编辑器或模板,并将如何显示
DomainModel
实例的逻辑放在一个位置

如果您对MVC中的编辑器模板感到好奇,请查看或

给你一个这样的例子:

模型:

public class Entity : List<Property>
{
    public Entity()
    {
    }
}

public class Property
{
    public string Name { get; set; }
    public string Value { get; set; }
    public DisplayType DisplayType { get; set; }
}

public enum DisplayType
{
    TextBox,
    Checkbox
}
视图可能如下所示:

@using WebApplication6.Models
@model WebApplication6.Models.Entity

@{
    ViewBag.Title = "Edit Entity";
}

<h2>Edit Entity</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>Entity</h4>
        <hr />
        @Html.ValidationSummary(true)
        <div class="form-group">
            @for (var i = 0; i < Model.Count;i++ )
            {
                <div class="form-group">
                    @Html.EditorFor(m => m[i], "PropertyEditor")
                </div>
            }
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}
@model WebApplication6.Models.Property

@if (Model != null)
{
    <label for="@Model.Name" class="col-sm-2 control-label">@Model.Name</label>

    switch (Model.DisplayType)
    {
        case WebApplication6.Models.DisplayType.TextBox:
            <div class="col-sm-10">@Html.TextBox(Model.Name, Model.Value)</div>
            break;
        case WebApplication6.Models.DisplayType.Checkbox:
            <div class="col-sm-10">@Html.CheckBox(Model.Name, bool.Parse(Model.Value))</div>
            break;
    }
}

我最后做的只是将视图和viewmodel保持为平面,而不是尝试在那里进行一些动态类型暗示。这也允许我保持我的验证和其他属性的整洁

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

public class SaveDomainModel
{
    public List<DomainModel> DomainModels { get; set; }
    public SettingTypeEnum SettingType { 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; }
}
现在我的控制器在帖子中是这样的:

[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);
}
这是我在这篇文章中偶然发现的缺失环节:

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

私有静态列表映射EditViewModel到域模型(EditViewModel viewModel)
{
var设置=新列表();
var stringPropertyNamesAndValues=viewModel.GetType().GetProperties().Where(p=>p.CanRead)。选择(p=>new{Name=p.Name,Value=p.GetValue(viewModel,null)});
foreach(stringPropertyNamesAndValues中的变量对)
{
var模型=新域模型
{
Key=pair.Name,
Value=pair.Value.ToString()
};
设置。添加(模型);
}
返回设置;
}
然后我就可以像这样保持视野平坦:

<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>
...>

@LabelFor(model=>model.SomeString)
@Html.TextBoxFor(model=>model.SomeString)
@LabelFor(model=>model.SomeBoolean)
@CheckBoxFor(model=>model.SomeBoolean)
...>
然后为了完成它,我在我的存储库中添加了一个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;
}
public void保存设置(SaveDomainModel模型)
{
var settings=MapDomainModelToList(model).AsQueryable();
_repository.UpdateCollection(设置);
}
私有IEnumerable MapDomainModelToList(SaveDomainModel SaveDomainModel)
{
var设置=新列表();
foreach(saveDomainModel.DomainModels中的var domainModel)
{
var setting=GetSetting(domainModel.Key,saveDomainModel.SettingType);
如果(!String.Equals(setting.Value、domainModel.Value、StringComparison.CurrentCultureIgnoreCase))
{
setting.Value=domainModel.Value;
setting.LastUpdated=DateTime.Now;
设置。添加(设置);
}
}
返回设置;
}
公共bool UpdateCollection(IQueryable实体)
{
使用(var事务=_session.BeginTransaction())
{
foreach(实体中的var实体)
{
_更新(实体);
}
Commit();
}
返回true;
}

您需要一些元数据。这些应该由
TypeEnum
开关驱动。您可以/应该在任何地方创建一些可用的提供程序模式实现,提供对这些元数据的只读访问。有了这些,我们可以使用元数据(例如BL上的元数据)进行验证,MVC视图可以基于这些设置驱动渲染。。。(这里有一个关于NHibernate dynamic world的小概述)感谢您花时间回复,但是我应该在我的原始帖子中澄清,TypeEnum与键的值的强.net类型无关。它只是一个枚举,允许我从数据库中提取特定类型的键/值,因为它们用于应用程序的各个领域。但这是一个想法。向数据库添加另一个int
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>
...>
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;
}