C# Fluent API:引用类型相互获取值

C# Fluent API:引用类型相互获取值,c#,asp.net-mvc,html-helper,fluent-interface,C#,Asp.net Mvc,Html Helper,Fluent Interface,我目前正在为MVC编写一个网格,它是一个HtmlHelper扩展,我遇到了一个奇怪的问题 首先,这是我正在使用的构建网格的代码: RenderedOutput = HtmlHelper.GridFor(Model) .WithColumns(column => { column.Bind(x => x.Name) .WithCss("inline"); column.Bind(x => x.Age) .WithCss("inlin

我目前正在为MVC编写一个网格,它是一个HtmlHelper扩展,我遇到了一个奇怪的问题

首先,这是我正在使用的构建网格的代码:

RenderedOutput = HtmlHelper.GridFor(Model)
.WithColumns(column =>
{
    column.Bind(x => x.Name)
        .WithCss("inline");
    column.Bind(x => x.Age)
        .WithCss("inline fixed right");
})
.Render();
现在,整个fluent API是通过使用接口构建的,为了使复制过去的工作变得更小,我不在这里发布接口,但我将发布实现:

首先,HtmlHelper:

public static IGridBuilder<TEntity> GridFor<TModel, TEntity>(this HtmlHelper<TModel> htmlHelper,
    IEnumerable<TEntity> dataSource)
{
    return new GridBuilder<TEntity>(htmlHelper, dataSource);
}
publicstaticigridbuildergridfor(此HtmlHelper HtmlHelper,
IEnumerable数据源)
{
返回新的GridBuilder(htmlHelper,数据源);
}
这将返回一个GridBuilder,其实现如下:

#region Constructors

public GridBuilder(HtmlHelper helper, IEnumerable<TModel> data)
{
    HtmlHelper = helper;
    DataSource = data;

    ColumnBuilders = new List<IColumnBuilder<TModel>>();
}

#endregion

#region Properties

private string Id { get; set; }

#endregion

#region IGridBuilder Members

public IList<IColumnBuilder<TModel>> ColumnBuilders { get; protected set; }

public IEnumerable<TModel> DataSource { get; private set; }

public HtmlHelper HtmlHelper { get; private set; }

public IGridBuilder<TModel> WithId(string id)
{
    Id = id;
    return this;
}

public IGridBuilder<TModel> WithColumns(Action<IColumnBuilder<TModel>> bindAllColumns)
{
    bindAllColumns(new ColumnBuilder<TModel>(this));

    return this;
}

public HtmlString Render()
{
    var outputBuilder = new StringBuilder();

    var headerMember = new TagBuilder("div");
    headerMember.MergeAttribute("class", "gridHolder v-scroll");

    if (!string.IsNullOrEmpty(Id))
    {
        headerMember.GenerateId(Id);
    }

    outputBuilder.AppendLine(headerMember.ToString(TagRenderMode.StartTag));

    // Process all the available columns.
    var rowBuilder = new TagBuilder("div");
    rowBuilder.MergeAttribute("class", "row");
    outputBuilder.AppendLine(rowBuilder.ToString(TagRenderMode.StartTag));

    foreach (var column in ColumnBuilders)
    {
        var columnBuilder = new TagBuilder("div");
        outputBuilder.AppendLine(columnBuilder.ToString(TagRenderMode.StartTag));
        outputBuilder.AppendLine(columnBuilder.ToString(TagRenderMode.EndTag));
    }

    outputBuilder.AppendLine(rowBuilder.ToString(TagRenderMode.EndTag));

    outputBuilder.AppendLine(headerMember.ToString(TagRenderMode.EndTag));

    return new HtmlString(outputBuilder.ToString());
}

#endregion
#区域构造函数
公共网格生成器(HtmlHelper助手,IEnumerable数据)
{
HtmlHelper=助手;
数据源=数据;
ColumnBuilders=新列表();
}
#端区
#区域属性
私有字符串Id{get;set;}
#端区
#区域构建器成员
公共IList列生成器{get;protected set;}
公共IEnumerable数据源{get;private set;}
公共HtmlHelper HtmlHelper{get;私有集;}
id为(字符串id)的公共IGridBuilder
{
Id=Id;
归还这个;
}
带列的公共IGridBuilder(操作bindAllColumns)
{
bindAllColumns(新ColumnBuilder(this));
归还这个;
}
公共HtmlString Render()
{
var outputBuilder=新的StringBuilder();
var headerMember=新标记生成器(“div”);
headerMember.MergeAttribute(“类”、“网格持有者v形卷轴”);
如果(!string.IsNullOrEmpty(Id))
{
生成Id(Id)的校长成员;
}
outputBuilder.AppendLine(headerMember.ToString(TagRenderMode.StartTag));
//处理所有可用列。
var rowBuilder=新标记生成器(“div”);
合并属性(“类”、“行”);
AppendLine(rowBuilder.ToString(TagRenderMode.StartTag));
foreach(ColumnBuilders中的var列)
{
var columnBuilder=新标记生成器(“div”);
AppendLine(columnBuilder.ToString(TagRenderMode.StartTag));
AppendLine(columnBuilder.ToString(TagRenderMode.EndTag));
}
AppendLine(rowBuilder.ToString(TagRenderMode.EndTag));
AppendLine(headerMember.ToString(TagRenderMode.EndTag));
返回新的HtmlString(outputBuilder.ToString());
}
#端区
}

GridBuilder执行IColumnBuilder的操作来构建列,因此如下所示:

public class ColumnBuilder<TModel> : IColumnBuilder<TModel>
{
    #region Constructors

    public ColumnBuilder(IGridBuilder<TModel> gridBuilder)
    {
        GridBuilderReference = gridBuilder;
    }

    #endregion

    #region IColumnBuilder Members

    public IGridBuilder<TModel> GridBuilderReference { get; private set; }

    public string CssClass { get; private set; }

    public IColumnBuilder<TModel> Bind<TItem>(Expression<Func<TModel, TItem>> propertySelector)
    {
        // Reset the properties. This is needed because they are not cleared automatticly. It's not a new instance which is created.
        CssClass = null;

        GridBuilderReference.ColumnBuilders.Add(this);

        return this;
    }

    public IColumnBuilder<TModel> WithCss(string className)
    {
        CssClass = className;

        return this;
    }

    #endregion
}
公共类ColumnBuilder:IColumnBuilder
{
#区域构造函数
公共ColumnBuilder(IGridBuilder gridBuilder)
{
GridBuilderReference=gridBuilder;
}
#端区
#区域IColumnBuilder成员
公共IGridBuilder GridBuilderReference{get;private set;}
公共字符串CssClass{get;private set;}
公共IColumnBuilder绑定(表达式属性选择器)
{
//重置属性。这是必需的,因为它们不是自动清除的。它不是创建的新实例。
CssClass=null;
GridBuilderReference.ColumnBuilders.Add(此);
归还这个;
}
带有CSS的公共IColumnBuilder(字符串类名称)
{
CssClass=className;
归还这个;
}
#端区
}
首先,这是我的第一个流畅的界面实现,所以如果这不是一个好方法,请告诉我正确的方向

情况:

  • 当我构建网格时,我传递了一个绑定列的动作,这些列也很流畅(例如,我可以在列上添加一个类)。现在,为了渲染这些,我认为在我的IGridBuilder中保存对所有IColumnBuilder实例的引用是一个好主意,这样在我的GridBuilder渲染方法中,我可以执行如下操作

    //在列之前进行渲染

    foreach(菌落中的c型变种) { c、 Render(); }

    //在列之后进行渲染

因此,我在GridBuilder中创建了一个包含所有ColumnBuilder的列表。 当我执行WithColumns时,我的GridBuilder对象(this)被传递,然后在ColumnBuilder中,在每个Bind()函数上,我将ColumnBuilder对象添加到传递的引用中


但这有一个奇怪的行为(例如,包含ColumnBuilder的列表确实与上次执行的ColumnBuilder的属性匹配)。

方法1

public class ColumnBuilderFactory<TModel> : IColumnBuilderFactory<TModel>
{
    #region Constructors

    public ColumnBuilderFactory(IGridBuilder<TModel> gridBuilder)
    {
        gridBuilderReference = gridBuilder;
    }

    #endregion

    #region IColumnBuilderFactory Members

    private IGridBuilder<TModel> gridBuilderReference { get; private set; }

    internal IList<IColumnBuilder<TModel>> Columns {get; private set; }

    public IColumnBuilder<TModel> New()
    {
        var column = new ColumnBuilder(gridBuilderReference);
        Columns.Add(column);
        return column;
    }

    #endregion
}
这是有用法的

RenderedOutput = HtmlHelper.GridFor(Model)
.WithColumns(columnFactory =>
{
    columnFactory.New().Bind(x => x.Name)
        .WithCss("inline");
    columnFactory.New().Bind(x => x.Age)
        .WithCss("inline fixed right");
})
.Render();
方法2

另一种方法通过欺骗和忽略这一点(使用FluentAPI替换它)实现了大致相同的效果,基本上它与您当前的用法相同,但是
Bind
变得

public IGridBuilder<TModel> WithColumns(Action<IColumnBuilderFactory<TModel>> bindAllColumns)
{
    var factory = new ColumnBuilderFactory<TModel>(this);
    bindAllColumns(factory);

    foreach(var column in factory)
    {
        this.ColumnBuilders.Add(column );
    }        

    return this;
}
public IColumnBuilder<TModel> Bind<TItem>(Expression<Func<TModel, TItem>> propertySelector)
{
    var builder = new ColumnBuilder<TModel>(GridBuilderReference);

    GridBuilderReference.ColumnBuilders.Add(builder);

    return builder;
}
显示方法2的控制台应用程序示例

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            var master = FluentHelper.GridFor(new Model())
            .WithColumns(column =>
                {
                    column.Bind(x => x.Name)
                        .WithCSS("inline");
                    column.Bind(x => x.Age)
                        .WithCSS("inline fixed right");
                });

            foreach(var c in master.children)
            {
                Console.WriteLine(c.Binding.ToString());
                Console.WriteLine(c.CSS);
            }

            Console.ReadKey(true);
        }
    }

    class Model
    {
        public string Name { get; set; }
        public string Age { get; set; }
    }

    class GridBuilder<T>
    {
        public GridBuilder<T> WithColumns(Action<ColumnBuilder<T>> bindAllColumns)
        {
            bindAllColumns(new ColumnBuilder<T>(this));

            return this;
        }

        public List<ColumnBuilder<T>> children = new List<ColumnBuilder<T>>();
    }

    class ColumnBuilder<T>
    {
        private GridBuilder<T> grid;

        public string Binding;
        public string CSS;

        public ColumnBuilder(GridBuilder<T> grid)
        {
            // TODO: Complete member initialization
            this.grid = grid;
        }

        public void WithCSS(string css)
        {
            this.CSS = css;
        }

        public ColumnBuilder<T> Bind<TItem>(Expression<Func<T, TItem>> propertySelector)
        {
            var builder = new ColumnBuilder<T>(grid);

            builder.Binding = (propertySelector.Body as MemberExpression).Member.Name;

            grid.children.Add(builder);

            return builder;
        }
    }

    static class FluentHelper
    {

        internal static GridBuilder<T> GridFor<T>(T model)
        {
            return new GridBuilder<T>();
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq.Expressions;
命名空间控制台应用程序3
{
班级计划
{
静态void Main(字符串[]参数)
{
var master=FluentHelper.GridFor(新模型())
.WithColumns(列=>
{
column.Bind(x=>x.Name)
.WithCSS(“内联”);
column.Bind(x=>x.Age)
.WithCSS(“内联固定权利”);
});
foreach(主辅变量c)
{
Console.WriteLine(c.Binding.ToString());
Console.WriteLine(c.CSS);
}
Console.ReadKey(true);
}
}
类模型
{
公共字符串名称{get;set;}
公共字符串年龄{get;set;}
}
类网格生成器
{
带列的公共GridBuilder(操作bindAllColumns)
{
bindAllColumns(新ColumnBuilder(this));
归还这个;
}
public List children=new List();
}
类列生成器
{
私有网格生成器网格;
公共字符串绑定;
公共字符串CSS;
公共列生成器(GridBuilder网格)
{
//TODO:完成成员初始化
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            var master = FluentHelper.GridFor(new Model())
            .WithColumns(column =>
                {
                    column.Bind(x => x.Name)
                        .WithCSS("inline");
                    column.Bind(x => x.Age)
                        .WithCSS("inline fixed right");
                });

            foreach(var c in master.children)
            {
                Console.WriteLine(c.Binding.ToString());
                Console.WriteLine(c.CSS);
            }

            Console.ReadKey(true);
        }
    }

    class Model
    {
        public string Name { get; set; }
        public string Age { get; set; }
    }

    class GridBuilder<T>
    {
        public GridBuilder<T> WithColumns(Action<ColumnBuilder<T>> bindAllColumns)
        {
            bindAllColumns(new ColumnBuilder<T>(this));

            return this;
        }

        public List<ColumnBuilder<T>> children = new List<ColumnBuilder<T>>();
    }

    class ColumnBuilder<T>
    {
        private GridBuilder<T> grid;

        public string Binding;
        public string CSS;

        public ColumnBuilder(GridBuilder<T> grid)
        {
            // TODO: Complete member initialization
            this.grid = grid;
        }

        public void WithCSS(string css)
        {
            this.CSS = css;
        }

        public ColumnBuilder<T> Bind<TItem>(Expression<Func<T, TItem>> propertySelector)
        {
            var builder = new ColumnBuilder<T>(grid);

            builder.Binding = (propertySelector.Body as MemberExpression).Member.Name;

            grid.children.Add(builder);

            return builder;
        }
    }

    static class FluentHelper
    {

        internal static GridBuilder<T> GridFor<T>(T model)
        {
            return new GridBuilder<T>();
        }
    }
}