zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    我的浏览器收藏夹分类
    我的浏览器收藏夹分类
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 315 计算右侧小于当前元素的个数
    Java实现 LeetCode 315 计算右侧小于当前元素的个数
  • 原文地址:https://www.cnblogs.com/ServiceboyNew/p/2397925.html
Copyright © 2011-2022 走看看