【问题标题】:Custom mediatypeformatter not working on inherited classes自定义 mediatypeformatter 不适用于继承的类
【发布时间】:2013-01-29 09:46:31
【问题描述】:

我有这行:

GlobalConfiguration.Configuration.Formatters.Add(New ExcelMediaTypeFormatter(Of Classification)(Function(t) New ExcelRow(ExcelCell.Map(t.ChemicalAbstractService), ExcelCell.Map(t.Substance), ExcelCell.Map(t.Columns("Classifidcation")), ExcelCell.Map(t.Columns("Classification"))), Function(format) "excel"))

它工作正常并从我的 web api 创建一个 excelfile。

我有几个继承这个分类类的子类,我想为每个子类制作一个媒体格式化程序,以获取 excelformatter 中的特定列。 问题是,如果我这样做:

GlobalConfiguration.Configuration.Formatters.Add(New ExcelMediaTypeFormatter(Of CustomClassification)(Function(t) New ExcelRow(ExcelCell.Map(t.ChemicalAbstractService), ExcelCell.Map(t.Substance), ExcelCell.Map(t.Columns("Classifidcation")), ExcelCell.Map(t.Columns("Classification"))), Function(format) "excel"))

然后它根本不起作用。它只是从标准格式化程序生成 xml。当 web api 返回一个

时,如何让它对子类做出反应
IQueryable(Of Classification)

格式化程序:

public class ExcelMediaTypeFormatter<T> : BufferedMediaTypeFormatter 
    {
    private const string ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    private readonly Func<T, ExcelRow> builder;

    public ExcelMediaTypeFormatter(Func<T, ExcelRow> value)
    {
        builder = value;
        SupportedMediaTypes.Add(new MediaTypeHeaderValue(ContentType));
    }

    public ExcelMediaTypeFormatter(Func<T, ExcelRow> value, params Func<object, string>[] type)
        : this(value)
    {
        foreach (var mediaTypeMapping in type) {
            this.MediaTypeMappings.Add(Map(mediaTypeMapping));
        }
    }

    public override bool CanWriteType(Type type)
    {
        return type == typeof(IQueryable<T>) || type == typeof(T);
    }

    public override bool CanReadType(Type type)
    {
        return false;
    }
public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content)
    {            
        using (Stream ms = new MemoryStream()) {
            using (var book = new ClosedXML.Excel.XLWorkbook()) {
                var sheet = book.Worksheets.Add("sample");

                ICollection<T> rows = type == typeof(IQueryable<T>) ? ((IQueryable<T>)value).ToList() : new List<T>() { (T)value };

                for (var r = 0; r < rows.Count; r++) {
                    var result = builder((T)rows.ElementAt(r));

                    for (var c = 0; c < result.Count(); c++) {
                        if (result.ElementAt(c) != null)
                            sheet.Cell(r + 2, c + 1).Value = result.ElementAt(c).Value.ToString();
                    }
                }

                sheet.Columns().AdjustToContents();

                book.SaveAs(ms);

                byte[] buffer = new byte[ms.Length];

                ms.Position = 0;
                ms.Read(buffer, 0, buffer.Length);

                writeStream.Write(buffer, 0, buffer.Length);
            }
        }
    }

【问题讨论】:

    标签: c# vb.net func mediatypeformatter


    【解决方案1】:

    CanWriteType 将返回false,因为TCustomClassificationtypeClassification。如果返回 false,您的格式化程序将不会被使用。

    因为Classification 不一定是CustomClassification,所以这是行不通的。

    要实现你想要的,你需要稍微改变你的实现。

    您的ExcelMediaTypeFormatter 将不再是通用的。它不会通过一个Func,而是一个IRowsBuilder 实例列表。这些将在WriteToStream 方法中用于选择正确的构建器:

    public interface IRowsBuilder
    {
        bool CanBuildFor(Type type);
        IEnumerable<Type> SupportedTypes { get; }
        ExcelRow BuildRow(object value);
    }
    
    public class RowsBuilder<T> : IRowsBuilder where T : Classification
    {
        Func<T, ExcelRow> _builder;
    
        public RowsBuilder(Func<T, ExcelRow> builder)
        {
            if(builder == null) throw new ArgumentNullException("builder");
    
            _builder = builder;
        }
    
        public bool CanBuildFor(Type type)
        {
            return type.IsSubclassOf(typeof(T));
        }
    
        public IEnumerable<Type> SupportedTypes
        {
            get { yield return typeof(T); }
        }
    
        public ExcelRow BuildRow(object value)
        {
            if(!CanBuildFor(value.GetType()))
                throw new ArgumentException();
    
            return _builder((T)value);
        }
    }
    
    public class ExcelMediaTypeFormatter : BufferedMediaTypeFormatter 
    {
        private readonly ILookup<Type, IRowsBuilder> _builder;
    
        public ExcelMediaTypeFormatter(IEnumerable<IRowsBuilder> builder)
        {
            _builder = builder.SelectMany(x => builder.SupportedTypes
                                                      .Select(y => new
                                                              {
                                                                  Type = y,
                                                                  Builder = x
                                                              }))
                              .ToLookup(x => x.Type, x => x.Builder); 
        }
    
        public override bool CanWriteType(Type type)
        {
            return type == typeof(IQueryable<Classification>) ||
                   type == typeof(Classification);
        }
    
        // ...
    
        public override void WriteToStream(Type type, object value,
                                           Stream writeStream, HttpContent content)
        {
    
            // ...
    
            List<Classification> rows;
            if(type == typeof(IQueryable<Classification>))
                rows = ((IQueryable<Classification>)value).ToList();
            else
                rows = new List<Classification> { (Classification)value };
    
            for (var r = 0; r < rows.Count; r++)
            {
                var value = rows.ElementAt(r);
                var builder = _builder[value.GetType()];
                var result = builder(value);
                // ...
            }
        }
    }
    

    您现在只需向所有构建者注册一个ExcelMediaTypeFormatter

    var customBuilder = new RowsBuilder<CustomClassification>(
                            t => new ExcelRow(ExcelCell.Map(t.ChemicalAbstractService),
                                              ExcelCell.Map(t.Substance), 
                                              ExcelCell.Map(t.Columns("Classifidcation")),
                                              ExcelCell.Map(t.Columns("Classification"))));
    var builders = new List<IRowsBuilder>();
    builder.Add(customBuilder);
    builder.Add(someOtherBuilder);
    var excelFormatter = new ExcelMediaTypeFormatter(builders, format => "excel");
    GlobalConfiguration.Configuration
                       .Formatters
                       .Add(excelFormatter);
    

    【讨论】:

    • 我有大约 11 个分类子类,它们有自己的列,Excel 工作簿需要有与分类子类匹配的不同列。我不能将 IList og 列或类似的东西直接放在单元格中,所以我不知道如何获取特定子类的所有列(和属性),而不为每个子类制作 mediatypeformatter
    • @MichaelKireHansen:你认为这些专栏是从哪里来的?您的 Web API 返回 Classification 而不是 CustomClassification。这些列根本不保证存在。
    • 我知道如果它是一个特定的子类,那么它有一些特定的列 编辑:因为我可以只使用分类并将所有可能的列放入,但这会留下很多空列excel
    • @MichaelKireHansen:但是你怎么知道它是一个特定的子类?同样:您的 Web API 仅返回基类。你怎么知道它是某个派生类?
    • 这就是问题所在 :) 通常可以使用 typeof 来检查它。同样在创建对象的地方,它作为 CustomClassification 被创建,然后作为分类传递,就像你可以有一个 List 但它作为 IList 传递一样
    猜你喜欢
    • 2017-01-24
    • 2016-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-07
    • 1970-01-01
    • 2015-06-20
    • 2017-02-08
    相关资源
    最近更新 更多