Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/338.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# 在ASP.NET MVC解决方案中,从我的域对象重构表示代码的最佳方法是什么?_C#_Html_Asp.net Mvc_Refactoring_Domain Object - Fatal编程技术网

C# 在ASP.NET MVC解决方案中,从我的域对象重构表示代码的最佳方法是什么?

C# 在ASP.NET MVC解决方案中,从我的域对象重构表示代码的最佳方法是什么?,c#,html,asp.net-mvc,refactoring,domain-object,C#,Html,Asp.net Mvc,Refactoring,Domain Object,我刚刚接手了一个ASP.NETMVC项目,需要进行一些重构,但我想获得一些关于最佳实践的想法/建议 该站点有一个SQLServer后端,下面是对解决方案中项目的回顾: 域对象(每个数据库表一个类) 域orm(将代码从对象映射到数据库) 模型(业务逻辑) MVC(常规ASP.NET MVC web设置) ----控制器 ----视图模型 ----观点 ----剧本 我看到的第一个“问题”是,虽然域对象类在计算字段周围有一些额外的“get”属性,但域对象中有一些表示代码。例如,在DomainOb

我刚刚接手了一个ASP.NETMVC项目,需要进行一些重构,但我想获得一些关于最佳实践的想法/建议

该站点有一个SQLServer后端,下面是对解决方案中项目的回顾:

  • 域对象(每个数据库表一个类)
  • 域orm(将代码从对象映射到数据库)
  • 模型(业务逻辑)
  • MVC(常规ASP.NET MVC web设置) ----控制器 ----视图模型 ----观点 ----剧本
我看到的第一个“问题”是,虽然域对象类在计算字段周围有一些额外的“get”属性,但域对象中有一些表示代码。例如,在DomainObjects项目中,有一个Person对象,我在该类上看到此属性:

 public class Person
 {

    public virtual string NameIdHTML
    {
        get
        {
           return "<a href='/People/Detail/" + Id + "'>" + Name + "</a> (" + Id + ")";
        }
    }
 }
公共类人物
{
公共虚拟字符串名称IDHTML
{
得到
{
返回“(“+Id+”)”;
}
}
}
显然,在域对象中包含HTML生成的内容似乎是错误的

重构方法

  • 我的第一反应是将其移动到MVC项目中的ViewModel类中,但我发现有很多视图都会用到这段代码,所以我不想在每个视图模型中重复代码

  • 第二个想法是创建PersonHTML类,该类可以是:

    2a。在构造函数或

    2b。从Person继承的类,然后具有所有这些HTML呈现方法

    视图模型将任何Person对象转换为PersonHTML对象,并将其用于所有渲染代码

  • 我只是想看看:

  • 如果这里有一个最佳实践,这似乎是一个常见的问题/模式

  • 这种当前状态有多糟糕,因为除了感觉不对劲之外,它实际上并没有在理解代码或创建任何不好的依赖关系方面造成任何重大问题。任何有助于描述为什么从实际意义上讲将代码留在这种状态是不好的论点(与理论上的关注点分离论点相比)都是有帮助的,因为团队中存在着是否值得更改的争论


  • 我喜欢TBD的评论。这是错误的,因为您将域关注点与UI关注点混为一谈。这会导致一个可以避免的耦合

    至于你建议的解决方案,我一个都不喜欢

  • 介绍一个视图模型。是的,我们应该使用视图模型,但是 我不想用HTML代码污染它们。下面是一个使用 如果您有父对象、人员类型和 希望在屏幕上显示人员类型。你会填满整个视野 使用person类型名称而不是完整person类型对象的模型 因为你只需要屏幕上的人名。或者如果 您的域模型的名字和姓氏是分开的,但是您的视图 调用FullName时,您将填充视图模型的FullName和 将其返回到视图

  • PersonHtml类。我甚至不知道那会怎么样。视图表示ASP.NET MVC应用程序中的HTML。这里有两个选项:

    a。可以为模型创建显示样板。这里有一个StackOverflow问题的链接,用于显示模板

    b。您还可以编写一个HtmlHelper方法来为您生成正确的HTML。像@Html.DisplayNameLink(…)这样的东西是最好的选择。这里有一个了解HtmlHelpers的链接


  • 要为这个问题提供一个完美的答案并不容易。尽管层的完全分离是可取的,但它通常会导致许多无用的工程问题

    尽管每个人都同意这样一个事实,即业务层对表示层/UI层一定不太了解,但我认为,让it知道这些层确实存在是可以接受的,当然没有太多的细节

    一旦声明了这一点,就可以使用一个未充分使用的接口:。这是使用的接口

    例如,您可以先定义Person类,如下所示:

    public class Person : IFormattable
    {
        public string Id { get; set; }
        public string Name { get; set; }
    
        public override string ToString()
        {
            // reroute standard method to IFormattable one
            return ToString(null, null);
        }
    
        public virtual string ToString(string format, IFormatProvider formatProvider)
        {
            if (format == null)
                return Name;
    
            if (format == "I")
                return Id;
    
            // note WebUtility is now defined in System.Net so you don't need a reference on "web" oriented assemblies
            if (format == "A")
                return string.Format(formatProvider, "<a href='/People/Detail/{0}'>{1}</a>", WebUtility.UrlEncode(Id), WebUtility.HtmlDecode(Name));
    
            // implement other smart formats
    
            return Name;
        }
    }
    
    string.Format("{0:A}", myPerson);
    
    string.Format("{0:A/People/Detail/}", person)
    
    public class Address : IFormattable
    {
        public string Recipient { get; set; }
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string ZipCode { get; set; }
        public string City { get; set; }
        public string Country { get; set; }
    
        ....
    
        public virtual string ToString(string format, IFormatProvider formatProvider)
        {
            // http://stackoverflow.com/questions/3179716/how-determine-if-application-is-web-application
            if ((format == null && InWebContext) || format == "H")
                return string.Join("<br/>", Recipient, Line1, Line2, ZipCode + " " + City, Country);
    
            return string.Join(Environment.NewLine, Recipient, Line1, Line2, ZipCode + " " + City, Country);
        }
    }
    
    @model Person
    
    @Html.ActionLink(Model.Name, "Detail", "People", new { id = Model.Id }, null) ( @Html.DisplayFor(p => p.Id) )
    
    或者使用MVC的。NET中有许多类支持IFormattable(例如StringBuilder)

    您可以优化系统,并改为执行以下操作:

        public virtual string ToString(string format, IFormatProvider formatProvider)
        {
            ...
            if (format.StartsWith("A"))
            {
                string url = format.Substring(1);
                return string.Format(formatProvider, "<a href='{0}{1}'>{2}</a>", url, WebUtility.UrlEncode(Id), WebUtility.HtmlDecode(Name));
            }
            ...
            return Name;
        }
    
    因此,您不需要在业务层中硬编码url。在web作为表示层的情况下,通常必须以这种格式传递CSS类名,以避免业务层中的硬编码样式。事实上,您可以提出非常复杂的格式。毕竟,这是对对象所做的事情,比如你仔细想想

    您甚至可以更进一步,使用一些ambiant/static属性来告诉您是否在web上下文中运行,以便它自动工作,如下所示:

    public class Person : IFormattable
    {
        public string Id { get; set; }
        public string Name { get; set; }
    
        public override string ToString()
        {
            // reroute standard method to IFormattable one
            return ToString(null, null);
        }
    
        public virtual string ToString(string format, IFormatProvider formatProvider)
        {
            if (format == null)
                return Name;
    
            if (format == "I")
                return Id;
    
            // note WebUtility is now defined in System.Net so you don't need a reference on "web" oriented assemblies
            if (format == "A")
                return string.Format(formatProvider, "<a href='/People/Detail/{0}'>{1}</a>", WebUtility.UrlEncode(Id), WebUtility.HtmlDecode(Name));
    
            // implement other smart formats
    
            return Name;
        }
    }
    
    string.Format("{0:A}", myPerson);
    
    string.Format("{0:A/People/Detail/}", person)
    
    public class Address : IFormattable
    {
        public string Recipient { get; set; }
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string ZipCode { get; set; }
        public string City { get; set; }
        public string Country { get; set; }
    
        ....
    
        public virtual string ToString(string format, IFormatProvider formatProvider)
        {
            // http://stackoverflow.com/questions/3179716/how-determine-if-application-is-web-application
            if ((format == null && InWebContext) || format == "H")
                return string.Join("<br/>", Recipient, Line1, Line2, ZipCode + " " + City, Country);
    
            return string.Join(Environment.NewLine, Recipient, Line1, Line2, ZipCode + " " + City, Country);
        }
    }
    
    @model Person
    
    @Html.ActionLink(Model.Name, "Detail", "People", new { id = Model.Id }, null) ( @Html.DisplayFor(p => p.Id) )
    
    公共类地址:可附加
    {
    公共字符串收件人{get;set;}
    公共字符串Line1{get;set;}
    公共字符串第2行{get;set;}
    公共字符串ZipCode{get;set;}
    公共字符串City{get;set;}
    公共字符串国家{get;set;}
    ....
    公共虚拟字符串到字符串(字符串格式,IFormatProvider formatProvider)
    {
    // http://stackoverflow.com/questions/3179716/how-determine-if-application-is-web-application
    if((格式==null&&InWebContext)| |格式==“H”)
    返回字符串。Join(“
    ”,收件人,第1行,第2行,ZipCode+”+城市,国家); 返回字符串.Join(Environment.NewLine,Recipient,Line1,Line2,ZipCode+“”+城市,国家); } }
    我自己也在努力解决这个问题。当视图中的代码比HTML更基于逻辑时,我创建了一个增强版的HtmlBuilder。我扩展了某些域对象,以自动打印出这个助手,其内容基于域之外的内容
    AddressViewModel.cshtml
    
    @Html.DisplayFor(m => m.Address)
    @Html.EditorFor(m => m.Address)
    
    @Html.DisplayFor(m => m.Address, new { show_property_name = false, ... })
    
    @ {
        var showPropertyName = ViewData.ContainsKey("show-property-name") ? (bool)ViewData["show-property-name] : true;
        ...
    }
    
    @if(showPropertyName)
    {
        @Html.TextBoxFor(m => m.PropertyName)
    }
    
    [UIHint("PostalAddress")]
    public AddressViewModel Address { get; set; }
    
    public static class BusinessToHtmlHelper {
        public static MvcHtmlString FromBusinessObject( this HtmlHelper html, Person person) {
            string personLink = html.ActionLink(person.Name, "Detail", "People",
                new { id = person.Id }, null).ToHtmlString();
            return new MvcHtmlString(personLink + " (" + person.Id + ")");
        }
    
        public static MvcHtmlString FromBusinessObject( this HtmlHelper html,
            Department department) {
    
            string departmentLink = html.ActionLink(department.Name, "Detail", "Departments",
                new { id = department.Id }, null).ToHtmlString();
            return new MvcHtmlString(departmentLink + " (" + department.Id + ")");
        }
    }
    
    @person.NameIdHTML
    
    @Html.FromBusinessObject(person)
    
    @model Person
    
    @Html.ActionLink(Model.Name, "Detail", "People", new { id = Model.Id }, null) ( @Html.DisplayFor(p => p.Id) )
    
    @Html.DisplayFor(m => m.Person)
    
    public static class BusinessToHtmlHelper {
        public static MvcHtmlString FromBusinessObject<T>( this HtmlHelper<T> html, Person person) {
            return html.DisplayFor( m => person );
        }
        //...
    }
    
    public static class PersonViewExtensions
    {
        const string NameAndOnePhoneFormat="{0} ({1})";
        public static string NameAndOnePhone(this Person person)
        {
            var phone = person.MobilePhone ?? person.HomePhone ?? person.WorkPhone;
            return string.Format(NameAndOnePhoneFormat, person.Name, phone);
        }
    }