• ObjectDataSource未能找到带参数的非泛型方法的的解决以及自定义更新参数的探究


    传统ADO.NET的情况下使用ObjectDataSource使得我们可以使用任何数据源作为底层进行CRUD的操作,简单易行。不过ObjectDataSource用的不好往往调试起来很困难(当然这是微软控件封装了太好的缘故:-))。在常见的此类数据源控件出错中,最最常见切频繁出现的就是“XXXDataSource未能找到带参数的……”一类红色的提示。如何解决此类问题呢?

    俗话说得好——工欲善其事,必先利其器——我们就从提示入手,仔细研究揣摩ObjectDataSource究竟是如何映射ObjectDataSource的Update方法参数的。

    方便期间,我们先创建一个工程(我目前用VS2011 Beta),其中包含一个静态类,用于模拟从数据库中读取的数据资料,以及一个更新方法,大体上代码如下:

    [C#]

    /// <summary>
    /// 实体类
    /// </summary>
    public class Product
    {
    public int Id{get;set;}
    public string Name{get;set;}
    }

    public static class DataSourceFile
    {
    static List<Product> _products = null;

    //第一次加载,就初始化仅一次
    static DataSourceFile()
    {
    _products = new List<Product>();
    for (int i = 1; i < 11; i++)
    {
    _products.Add(new Product { Id = i, Name = "Product" + i});
    }
    }

    /// <summary>
    /// 选出全部记录的静态方法
    /// </summary>
    public static IList<Product> GetAllProducts()
    {
    return _products;
    }

    public static void UpdateProducts(int Id,string Name)
    {
    Product up= _products.Find(pro => pro.Id == Id);
    up.Name = Name;
    }
    }

    [VB.NET]

    ''' <summary>
    ''' 实体类
    ''' </summary>
    Public Class Product
    Public Property Id() As Integer
    Get
    Return m_Id
    End Get
    Set
    m_Id = Value
    End Set
    End Property
    Private m_Id As Integer
    Public Property Name() As String
    Get
    Return m_Name
    End Get
    Set
    m_Name = Value
    End Set
    End Property
    Private m_Name As String
    End Class

    Public NotInheritable Class DataSourceFile
    Private Sub New()
    End Sub
    Shared _products As List(Of Product) = Nothing

    '第一次加载,就初始化仅一次
    Shared Sub New()
    _products = New List(Of Product)()
    For i As Integer = 1 To 10
    _products.Add(New Product() With { _
    Key .Id = i, _
    Key .Name = "Product" & i _
    })
    Next
    End Sub

    ''' <summary>
    ''' 选出全部记录的静态方法
    ''' </summary>
    Public Shared Function GetAllProducts() As IList(Of Product)
    Return _products
    End Function

    Public Shared Sub UpdateProducts(Id As Integer,Name As String)
    Dim up As Product = _products.Find(Function(pro) pro.Id = Id)
    up.Name = Name
    End Sub
    End Class

    对应的aspx代码生成如下:

    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
    DataSourceID
    ="ProductObjectDataSource" EnableModelValidation="True">
    <Columns>
    <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />
    <asp:BoundField DataField="Id" HeaderText="Id" SortExpression="Id" ReadOnly=true />
    <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
    </Columns>
    </asp:GridView>

    <asp:ObjectDataSource ID="ProductObjectDataSource" runat="server" SelectMethod="GetAllProducts"
    TypeName
    ="WebCSharp.DataSourceFile"
    UpdateMethod
    ="UpdateProducts">
    </asp:ObjectDataSource>

    这是最简单的情况,假设现在Id是主键(自然只读不允许修改)的情况下,我们在UpdateProducts这里设置断点,然后F5调试,我们便可以发现p已经填充了更新后的数据,但是Id不会发生变化(回传原来的Id),因此可以进行更新。
    不过这里提醒大家注意一点:当把objectDataSource绑定到GridView上的时候,默认生成的<asp:ObjectDataSource……>这里边应该还包括了<UpdateParameters>……</UpdateParameters>一节,其中生成的内容与UpdateProducts参数类型、名称完全一致。这很容给大多数读者造成误会——似乎更新以后的数据就是通过这些Parameters进行传递的,如果你这样认为,那就大错特错了!其实,UpdateParameters只是用于定义额外需要的更新字段,并非是数据模型自身。换句话说,如果你定义了一个UpdateParameter,那么对应的UpdateMethod必须拥有这一个额外的字段(包括类型也必须匹配)!而数据表模型自身的字段即便不定义(当然定义了也无所谓),也会自动传递到UpdateMethod,和其中的参数一一匹配(参数顺序并不重要)

    下面给一个例子进行严格证明(其它地方不变,我只多定义一个UpdateParameter):

     <asp:ObjectDataSource ID="ProductObjectDataSource" runat="server" SelectMethod="GetAllProducts" 
    TypeName
    ="WebCSharp.DataSourceFile"
    UpdateMethod
    ="UpdateProducts">
    <UpdateParameters>
    <asp:Parameter Name="s" Type="string" />
    </UpdateParameters>
    </asp:ObjectDataSource>

    因为多定义了一个参数为string类型的s,因此在对应的UpdateMethod中必然也要有所映射,所以UpdateMethod必须写成:

    [C#]

    public static void UpdateProducts(int Id,string Name,string s)
    {
    ………………
    }

    [VB.NET]

    Public Shared Sub UpdateProducts(Id As Integer, Name As String, s As String)
    …………
    End Sub

    此时,如果你没有定义s,那么就会引发我说指出的上面错误。

    因此我们可以先这样总结:Update方法的参数必须包含由Select语句或者是模型类中的各个公共属性字段作为参数,同时还要包含UpdateParameters中额外定义的参数。更进一步地——如果ObjectDataSource设置了ConflictDection和OldValuesParameterFormatString,那么Update方法必须同时包含这两类参数名称和类型。

    Delete方法:未设置“冲突检测”之时,必须指定DataKeyNames(主键作为删除的依据)以及对应的参数名和类型,设置了“冲突检测”之后,则只需指定全部的OldParameterValueFormation的格式化参数名称以及类型即可。

    如果上面一个示例使用了“冲突检测”机制,那么Update方法必然要加入OldValuesparameterFormatString(假设设置为old_{0},那么参数就还要多出“int old_Id”、“string old_Name”)。

    说了那么半天,你大概感悟颇多,不过同时觉得把字段作为参数一个个写未免有些太过于麻烦。下面我们来介绍一种更为直接的方法——模型实体映射:

    所谓“模型实体映射”就是说参数不再是一个个独立的参数那么离散,而是统一扔给一个模型做处理,这样,我们直接就针对类对象处理,而不是一个个单独离散的数据啦(PS:毕竟OOP嘛!)

    模型映射分两种情况:

    第I型:自映射——

    所谓自映射,就是人工无需写任何的代码,直接把一个模型类取代先前的参数即可。那么一开始的例子就直接可以这样表示:

    [C#]

    public static void UpdateProducts(Product p)
    {
    Product up= _products.Find(pro => pro.Id == p.Id);
    up.Name = p.Name;
    }

    [VB.NET]

    Public Shared Sub UpdateProducts(p As Product)
    Dim up As Product = _products.Find(Function(pro) pro.Id = p.Id)
    up.Name = p.Name
    End Sub

    和第一次的例子一样,因为p包含了Id和Name,也被自动映射了。

    不过这里注意:如果使用了冲突检测机制,则不能使用此方法(即便为Product定义了OldParameterValueFormation);你只能老老实实定义参数哦!

    第II型:自定义实体模型映射:

    在第I型的例子中和先前所有的例子中,我们的Id都假设是不改变的。但是现在我们撤去这个理想化的条件,让Id也可以改变,那么如何更新呢?显然,使用只用一个Product只能获取更新后的数据,而我们需要更新前的数据作为参考才可以借助更新前的旧Id来更新旧Id对应的那个模型实体的数据。

    你可以选择使用参数化的方式(一个个定义参数),同时设置objectDataSource的ConflictDection和OldParameterValueFormat进行更新。如果我们需要你用OOP的方式呢?也就是定义类似这样的一个Update方法——其中包含一个旧的Product实体和新一个Product实体数据,这样我们直接从List中找到符合条件的旧实体数据,然后逐一把新的值赋给他。

    怎么做呢?如果我们直接这样定义:

    [C#]

    public static void UpdateProducts(OldProduct oldp,Product p)

    [VB.NET]

    Public Shared Sub UpdateProducts(oldp As OldProduct, p As Product)

    显然无论是UpdateParameter或者是自动模型的I方法都无法识别,也会报出本篇抬头的那个错误。
    要自定义更新参数,我们还必须了解究竟ObjectDataSource是如何自定义映射的。其实在做映射之前,ObjectDataSource将首先触发Updating事件,把映射的参数名称和对应的数值全部存入一个InputParameters的Dictionary中。因此如果要实现自定义的参数,我们只需在这个方法中逐个取出需要的参数值,并且使用Add方法包含全部UpdateMethod中的参数名称即可

    接着上面的定义,我们给出完整的代码:

    [C#]

    protected void ProductObjectDataSource_Updating(object sender, ObjectDataSourceMethodEventArgs e)
    {
    Product p = new Product() { Id = Convert.ToInt32(e.InputParameters["Id"]),Name=e.InputParameters["Name"].ToString() };
    OldProduct oldp = new OldProduct() { old_Id = Convert.ToInt32(e.InputParameters["old_Id"]), old_Name = e.InputParameters["old_Name"].ToString() };
    e.InputParameters.Clear();
    e.InputParameters.Add("p", p); //Product类型,名称p(对应UpdateMethod方法"p"名称)
    e.InputParameters.Add("oldp", oldp); //OldProduct类型,名称oldp(对应UpdateMethod方法"oldp"名称)
            }

    [VB.NET]

    Protected Sub ProductObjectDataSource_Updating(sender As Object, e As ObjectDataSourceMethodEventArgs)
    Dim p As New Product() With { _
    Key .Id = Convert.ToInt32(e.InputParameters("Id")), _
    Key .Name = e.InputParameters("Name").ToString() _
    }
    Dim oldp As New OldProduct() With { _
    Key .old_Id = Convert.ToInt32(e.InputParameters("old_Id")), _
    Key .old_Name = e.InputParameters("old_Name").ToString() _
    }
    e.InputParameters.Clear()
    e.InputParameters.Add("p", p) 'Product类型,名称p(对应UpdateMethod方法"p"名称)
        e.InputParameters.Add("oldp", oldp) 'OldProduct类型,名称oldp(对应UpdateMethod方法"oldp"名称)
    End Sub
  • 相关阅读:
    HDU-3065-病毒侵袭持续中(AC自动机)
    HDU-2896-病毒侵袭(AC自动机)
    HDU
    Codeforces Round #591 (Div. 2, based on Technocup 2020 Elimination Round 1) D. Sequence Sorting
    Codeforces Round #591 (Div. 2, based on Technocup 2020 Elimination Round 1) B. Strings Equalization
    Codeforces Round #591 (Div. 2, based on Technocup 2020 Elimination Round 1) C. Save the Nature
    Codeforces Round #591 (Div. 2, based on Technocup 2020 Elimination Round 1) A. CME
    Codeforces Round #586 (Div. 1 + Div. 2) E. Tourism
    Codeforces Round #586 (Div. 1 + Div. 2) D. Alex and Julian
    jsp内置对象
  • 原文地址:https://www.cnblogs.com/ServiceboyNew/p/2397925.html
Copyright © 2020-2023  润新知