2014-10-18 66 views
1

ASP.NET MVC4應用程序使用修改的WebMatrix Dynamicrecord從ADO.NET獲取動態數據並將其顯示在WebGrid中。如何在while循環中從ADO.NET獲取動態數據

運行的應用程序將導致奇怪例外

嘗試無效時沒有數據在方法本

讀取

private object GetNonNullValue(int i) 

在線路

var value = Record[i]; 

正如https://github.com/npgsql/npgsql/issues/295中討論的那樣,使用註釋中所示的foreach在Mono中不起作用。所以應用程序使用雖然但在Windows中也不起作用。

如何在while循環中獲取動態數據?

所有解決方案可在http://wikisend.com/download/360760/invalidattemptoreadwhennodataispresent.zip

控制器:

using Eeva.Business; 
using Eeva.Erp.ViewModels; 
using Npgsql; 
using System.Collections.Generic; 
using System.Data; 
using System.Data.Common; 
using System.Web.Mvc; 

namespace Eeva.Erp.Controllers 
{ 
    public class ReportController : Controller 
    { 
     public ActionResult Test() 
     { 
      var data = TestData(); 
      return View("ReportData", new ReportDataViewModel(data, "")); 
     } 

     IEnumerable<dynamic> TestData() 
     { 
      using (var connection = new NpgsqlConnection(ConnectionString())) 
      { 
       connection.Open(); 

       DbCommand command = (DbCommand)connection.CreateCommand(); 
       command.CommandText = "select 'A' union select 'B'"; 

       using (command) 
       { 
        using (DbDataReader reader = command.ExecuteReader()) 
        { 
         IEnumerable<string> columnNames = null; 

         while (reader.Read()) 
         { 
          if (columnNames == null) 
           columnNames = GetColumnNames(reader); 
          yield return new EevaDynamicRecord(columnNames, reader); 
         } 
         //foreach (DbDataRecord record in reader) 
         //{ 
         // if (columnNames == null) 
         //  columnNames = GetColumnNames(record); 
         // yield return new EevaDynamicRecord(columnNames, record); 
         //} 
        } 
       } 
      } 
     } 

     static IEnumerable<string> GetColumnNames(IDataRecord record) 
     { 
      // Get all of the column names for this query 
      for (int i = 0; i < record.FieldCount; i++) 
       yield return record.GetName(i); 
     } 

     static string ConnectionString() 
     { 
      return new NpgsqlConnectionStringBuilder() 
      { 
       Host = "localhost", 
       UserName = "postgres", 
      }.ConnectionString; 
     } 
    } 
} 

視圖模型:

using System.Collections.Generic; 
using System.Web.Mvc; 
using Eeva.Business; 

namespace Eeva.Erp.ViewModels 
{ 
    public class ReportDataViewModel 
    { 
     public IEnumerable<dynamic> Rows { get; set; } 

     public string Source; 

     public ReportDataViewModel(IEnumerable<dynamic> rows, string source) 
     { 
      Rows = rows; 
      Source = source; 
     } 
    } 
} 

檢視:

@model Eeva.Erp.ViewModels.ReportDataViewModel 
@using System.Web.Helpers 

@{ Layout = null; 
var gd = new WebGrid(source: Model.Rows); 
} 

<!DOCTYPE html> 
<html> 
<head></head> 
<body> 
    @gd.GetHtml() 
</body> 
</html> 

Dynamicrecord使用FR OM MVC4源代碼的修改:

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. 

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Diagnostics; 
using System.Dynamic; 
using System.Globalization; 
using System.Linq; 
using System.Text; 
using WebMatrix.Data.Resources; 

namespace Eeva.Business 
{ 
    public sealed class EevaDynamicRecord : DynamicObject, ICustomTypeDescriptor 
    { 
     public EevaDynamicRecord(IEnumerable<string> columnNames, IDataRecord record) 
     { 
      Debug.Assert(record != null, "record should not be null"); 
      Debug.Assert(columnNames != null, "columnNames should not be null"); 

      Columns = columnNames.ToList(); 
      Record = record; 
     } 

     public IList<string> Columns { get; private set; } 

     private IDataRecord Record { get; set; } 

     public object this[string name] 
     { 
      get 
      { 
       for (int i = 0; i < Record.FieldCount; i++) 
       { 
        string normname = Record.GetName(i); 
        if (normname.Equals(name, StringComparison.InvariantCultureIgnoreCase)) 
         return GetNonNullValue(i); 
       } 
       throw new InvalidOperationException("No column " + name); 
      } 
     } 

     public object this[int index] 
     { 
      get 
      { 
       return GetNonNullValue(index); // GetValue(Record[index]); 
      } 
     } 

     public string Field(int fldNo) 
     { 
      return Record.GetName(fldNo).ToUpperInvariant(); 
     } 

     public override bool TryGetMember(GetMemberBinder binder, out object result) 
     { 
      result = this[binder.Name]; 
      return true; 
     } 

     private object GetNonNullValue(int i) 
     { 
      var value = Record[i]; 
      if (DBNull.Value == value || value == null) 
      { 
       var tt = Record.GetFieldType(i).Name; 
       switch (tt) 
       { 
        case "Decimal": 
        case "Int32": 
        case "Double": 
         return 0; 
        case "String": 
         return ""; 

        case "DateTime": 
         return null; 

        case "Boolean": 
         // kui seda pole, siis demos lao kartoteek kartoteegi kaart annab vea: 
         return false; 

       } 
       return null; 
      } 
      if (value is decimal? || value is decimal) 
       return Convert.ChangeType(value, typeof(double)); 

      if (value is string) 
       return value.ToString().TrimEnd(); 
      return value; 
     } 

     public override IEnumerable<string> GetDynamicMemberNames() 
     { 
      return Columns; 
     } 

     private void VerifyColumn(string name) 
     { 
      // REVIEW: Perf 
      if (!Columns.Contains(name, StringComparer.OrdinalIgnoreCase)) 
      { 
       throw new InvalidOperationException(
        String.Format(CultureInfo.CurrentCulture, 
            "Invalid Column Name " + name)); 
      } 
     } 

     AttributeCollection ICustomTypeDescriptor.GetAttributes() 
     { 
      return AttributeCollection.Empty; 
     } 

     string ICustomTypeDescriptor.GetClassName() 
     { 
      return null; 
     } 

     string ICustomTypeDescriptor.GetComponentName() 
     { 
      return null; 
     } 

     TypeConverter ICustomTypeDescriptor.GetConverter() 
     { 
      return null; 
     } 

     EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() 
     { 
      return null; 
     } 

     PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() 
     { 
      return null; 
     } 

     object ICustomTypeDescriptor.GetEditor(Type editorBaseType) 
     { 
      return null; 
     } 

     EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) 
     { 
      return EventDescriptorCollection.Empty; 
     } 

     EventDescriptorCollection ICustomTypeDescriptor.GetEvents() 
     { 
      return EventDescriptorCollection.Empty; 
     } 

     PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) 
     { 
      return ((ICustomTypeDescriptor)this).GetProperties(); 
     } 

     PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() 
     { 
      // Get the name and type for each column name 
      var properties = from columnName in Columns 
          let columnIndex = Record.GetOrdinal(columnName) 
          let type = Record.GetFieldType(columnIndex) 
          select new DynamicPropertyDescriptor(columnName, type); 

      return new PropertyDescriptorCollection(properties.ToArray(), readOnly: true); 
     } 

     object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) 
     { 
      return this; 
     } 

     private class DynamicPropertyDescriptor : PropertyDescriptor 
     { 
      private static readonly Attribute[] _empty = new Attribute[0]; 
      private readonly Type _type; 

      public DynamicPropertyDescriptor(string name, Type type) 
       : base(name, _empty) 
      { 
       _type = type; 
      } 

      public override Type ComponentType 
      { 
       get { return typeof(EevaDynamicRecord); } 
      } 

      public override bool IsReadOnly 
      { 
       get { return true; } 
      } 

      public override Type PropertyType 
      { 
       get { return _type; } 
      } 

      public override bool CanResetValue(object component) 
      { 
       return false; 
      } 

      public override object GetValue(object component) 
      { 
       EevaDynamicRecord record = component as EevaDynamicRecord; 
       // REVIEW: Should we throw if the wrong object was passed in? 
       if (record != null) 
       { 
        return record[Name]; 
       } 
       return null; 
      } 

      public override void ResetValue(object component) 
      { 
       throw new InvalidOperationException(
        String.Format(CultureInfo.CurrentCulture, 
            "DataResources.RecordIsReadOnly", Name)); 
      } 

      public override void SetValue(object component, object value) 
      { 
       throw new InvalidOperationException(
        String.Format(CultureInfo.CurrentCulture, 
            "DataResources.RecordIsReadOnly", Name)); 
      } 

      public override bool ShouldSerializeValue(object component) 
      { 
       return false; 
      } 
     } 
    } 
} 

http://forums.asp.net/p/2013821/5795169.aspx

更新發布,這也

Problably創造每dynamicrecord的副本解決了這一問題:

Dictionary<string, object> Clonedict; 
public EevaDynamicRecord Clone() 
{ 
    var res = new EevaDynamicRecord(Columns, Record); 
    res.Clonedict = new Dictionary<string, object>(); 
    for (int i = 0; i < Record.FieldCount; i++) 
     res.Clonedict[Record.GetName(i)] = Record[i]); 
    return res; 
    // this also does not work: 
    // return (EevaDynamicRecord)this.MemberwiseClone(); 
} 

是有更好的解決辦法

+0

我注意到,你沒有驗證該'我'是'GetNonNullValue'或'this [int index]'中的有效列索引。也許你最終會使用超出範圍的列號? – 2014-10-18 14:23:40

+0

調試器顯示我的值爲0。這是完美的價值,結果包含單列。 – Andrus 2014-10-18 14:33:29

+0

我已經看到了問題,而不是這種模式,但與其他人,其中'DbCommand'和'DbDataReader'被包裹在一個'using'子句和'DbDataReader'返回那是'DbCommand'後採取行動和'DbDataReader'超出了範圍。它截斷了數據。也許類似這樣的事情發生在'yield':'IEnumerable '被枚舉時,'DbDataReader'從源數據中分離出來。因此,您的IDataRecord在訪問時無效。 – 2014-10-18 22:28:39

回答

0

我不知道這一定會起作用,但建議我需要發佈一個答案,因爲它包含一個代碼示例。

如果我是正確的,並且問題在您嘗試在命令超出範圍之後從讀取器訪問數據時發生,那麼這可以解決這個問題。

這個替換您Test方法:

public ActionResult Test() 
    { 
     var data = TestData().ToArray(); 
     return View("ReportData", new ReportDataViewModel(data, "")); 
    } 

如果不幫助,嘗試用這種替代TestData方法:

IEnumerable<dynamic> TestData() 
    { 
     List<dynamic> results = new List<dynamic>(); 
     using (var connection = new NpgsqlConnection(ConnectionString())) 
     { 
      connection.Open(); 

      DbCommand command = (DbCommand)connection.CreateCommand(); 
      command.CommandText = "select 'A' union select 'B'"; 

      using (command) 
      { 
       using (DbDataReader reader = command.ExecuteReader()) 
       { 
        IEnumerable<string> columnNames = null; 

        while (reader.Read()) 
        { 
         if (columnNames == null) 
          columnNames = GetColumnNames(reader); 
         results.Add(new EevaDynamicRecord(columnNames, reader)); 
        } 
        //foreach (DbDataRecord record in reader) 
        //{ 
        // if (columnNames == null) 
        //  columnNames = GetColumnNames(record); 
        // yield return new EevaDynamicRecord(columnNames, record); 
        //} 
       } 
      } 
     } 
     return results; 
    } 
+0

實際結果中的行數很大。在這種情況下,WebGrid只顯示第一頁。您的代碼讀取所有行。這很慢,結果不適合記憶。如何解決這個問題? – Andrus 2014-10-19 14:46:25

+0

我將這兩個更改都應用於測試代碼,但問題仍然存在。 – Andrus 2014-10-19 16:15:54

+0

那麼,這是值得一試。我沒有意識到你的數據集會如此之大。 – 2014-10-20 13:09:51

相關問題