Programmatic parameter population with declarative data sources in ASP.NET 2.0

Onion Blog

Syndication

How's that for an alliteration? I've been meaning to post this tip for a while, since it's something I find myself using quite a bit, but I haven't seen the technique described elsewhere.
 
Here's the scenario: You have a declarative data source for a control (let's use an example of a DetailsView with a SqlDataSource) and you want to implicitly populate one or more of the parameters when the user inserts a new item. A common example of this is a timestamp field that should be set to whatever time the new row was inserted, and not something populated by the user. This could equally apply to parameter population for select, update, or delete queries too, I just find inserts to be the most common.
 
There are several parameter types available for declarative data sources, including the ability to draw a value from a cookie, a form field, the user's profile data, a query string, etc. There are occasions, however, where none of these fits the bill and you just need to populate the parameter yourself (like the timestamp case). Another common one is username - if you need to populate a column with a user name in an authenticated site, you need to grab the User.Identity.Name from the context and stuff it in the parameter value of the query.
 
Anyway, the solution I've been using is to add a handler for the appropriate event on the data source control (the events of interest are Inserting, Selecting, Updating, and Deleting, which are all called before the actual SQL call is made). So to complete the example described above, imagine a bug tracking system where users insert bugs through a DetailsView control, and we want to populate the user field of the defect table with the current user name and the time the bug was submitted with the current time. The following does the trick nicely:
 
<asp:DetailsView ID="_defectDetailView" runat="server" AutoGenerateRows="False"
                        DataKeyNames="defect_id"
                        DataSourceID="defectsDataSource" AllowPaging="True">
  <Fields>
    <asp:BoundField DataField="defect_id" HeaderText="defect_id"
                          InsertVisible="False" ReadOnly="True" />
    <asp:BoundField DataField="title" HeaderText="title" />
    <asp:BoundField DataField="description" HeaderText="description" />
    <asp:BoundField DataField="filed_by" HeaderText="filed_by" />
    <asp:BoundField DataField="assigned_to" HeaderText="assigned_to" InsertVisible="false" />
    <asp:BoundField DataField="severity" HeaderText="severity" />
    <asp:BoundField DataField="status" HeaderText="status" />
    <asp:BoundField DataField="date_filed" HeaderText="date_filed" InsertVisible="false" />
    <asp:CommandField ShowInsertButton="True" />
  </Fields>
</asp:DetailsView>
 
<asp:SqlDataSource ID="defectsDataSource" runat="server"
        ConnectionString="<%$ ConnectionStrings:defects_dbConnectionString %>"
        InsertCommand="INSERT INTO [defect_reports] ([title], [description], [filed_by], [assigned_to], [severity], [status], [date_filed]) VALUES (@title, @description, @filed_by, @assigned_to, @severity, @status, @date_filed)"
        SelectCommand="SELECT defect_id, title, description, filed_by, assigned_to, severity, status, date_filed FROM [defect_reports]"
        OnInserting="defectsDataSource_Inserting">
  <InsertParameters>
    <asp:Parameter Name="title" Type="String" />
    <asp:Parameter Name="description" Type="String" />
    <asp:Parameter Name="filed_by" Type="String" />
    <asp:Parameter Name="assigned_to" Type="String" />
    <asp:Parameter Name="severity" Type="String" />
    <asp:Parameter Name="status" Type="String" />
    <asp:Parameter Name="date_filed" Type="DateTime" />
  </InsertParameters>
</asp:SqlDataSource>
 
And the defectsDataSource_Inserting method that would go in your code behind class:
 
protected void defectsDataSource_Inserting(object sender,
                                                            SqlDataSourceCommandEventArgs e)
{
  e.Command.Parameters["@filed_by"].Value = User.Identity.Name;
  e.Command.Parameters["@date_filed"].Value = DateTime.Now.ToShortDateString();
}
 
Note the use of the handy "InsertVisible" property on the BoundField controls in the DetailsView. This makes the field visible only when displaying (or updating) and is perfect for the scenario I'm describing.
 
So before you resort to manual data binding, remember that you can always influence the execution of any SQL calls made by a declarative data source by adding a handler for the appropriate event.
 
 
 

Posted Jan 16 2006, 10:13 AM by fritz-onion
Filed under:

Comments

Gregor Suttie wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 01-16-2006 9:25 AM
Jolly good tip Fritz.

Are there any particularly useful weblinks on such content?

Thanks
Gregor
Andrew Robinson wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 01-16-2006 9:48 AM
Fritz, I know that my blog is not widely read by anyone but me, but I did post about this a few days ago. Mainly writing to see what you think of my original post:

http://blog.binaryocean.com/PermaLink,guid,a1ff6cab-dc2d-441c-8557-7dce920d4075.aspx

http://blog.binaryocean.com/PermaLink,guid,eca3c5a1-08c1-4226-bfb5-d36fddaef93b.aspx

I thurowly enjoy reading what you write.

-A
Christopher Steen wrote Link Listing - January 16, 2005
on 01-16-2006 5:48 PM
Cool Command-Line Stuff
[Via: kaevans ]
Programmatic parameter population with declarative data ...
Fritz Onion wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 01-17-2006 4:14 AM
Thanks for the link Andrew, It's always reassuring to see someone else use the same technique. Your blog looks like it has some great content, I'll keep an eye there in the future.
-Fritz
David Taylor wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 01-20-2006 2:40 AM
Hi Fritz,

Yes I have done this as well.

I must say that one of this things Microsoft seem to have missed is the concept of an <asp:PropertyParameter />.

Why do I think this is missing and important? Because you often get in a "pull" mindset with the new databinding model where you want to setup Control Parameters to pull their values from Session or Profile or the QueryString, etc. But it often breaks down and you need to use the "push" ie Event handling model, which makes the code harded to comprehend.

Wouldn't it be elegant to just define a couple of Page level properties like:
public DateTime Today
{
get { return DateTime.Now; }
}

public string UserName
{
get { return User.Identity.Name;
}

and then just include a param like:
<asp:PropertyParameter Name="filed_by" Property="UserName" />
<asp:PropertyParemeter Name=date_filed" Property="Today" />

This would allow you to keep the same mindset.

Lets all hit Scott G with this suggestion ;-)

Another reason you *need* this feature, is because ASP.NET 2 is really bad when splitting stuff between different user controls or between a master page and a page or user control. The data binding stuff in V2 seems really bad unless both the data source and data control are within the same page/control/masterpage. My suggestions would also be to use page level properties to solve this problem.



Richard wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 01-25-2006 3:10 AM
It's actually quite easy to roll your own:

using System;
using System.ComponentModel;
using System.Globalization;
using System.Web;
using System.Web.UI.WebControls;

[DefaultProperty("PropertyName")]
public class PagePropertyParameter : Parameter
{
public PagePropertyParameter(string name, TypeCode typeCode, string propertyName)
: base(name, typeCode)
{
PropertyName = propertyName;
}

public PagePropertyParameter(string name, string propertyName)
: base(name)
{
PropertyName = propertyName;
}

protected PagePropertyParameter(PagePropertyParameter original)
: base(original)
{
PropertyName = original.PropertyName;
}

public PagePropertyParameter()
{
}

[DefaultValueAttribute("")]
[Category("Parameter")]
public string PropertyName
{
get
{
object value = ViewState["PropertyName"];
if (null == value) return string.Empty;
return (string)value;
}
set
{
if (string.IsNullOrEmpty(value))
{
ViewState.Remove("PropertyName");
}
else
{
ViewState["PropertyName"] = value;
}
}
}

protected override Parameter Clone()
{
return new PagePropertyParameter(this);
}

protected override object Evaluate(HttpContext context, Control control)
{
string prop = this.PropertyName;
if (string.IsNullOrEmpty(prop)) return null;

IHttpHandler handler = context.Handler;
if (null == handler) return null;

return DataBinder.Eval(handler, prop);
}
}
Clay McKinney wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 02-13-2006 12:31 PM
Hi there. I love this tip. It's exactly what I'm looking for. I'm trying to adapt this to VB/Access, and I'm getting an odd error. Any ideas?

Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.

Compiler Error Message: BC30545: Property access must assign to the property or use its value.

Source Error:



Line 2: <script runat="server">
Line 3: Sub AccessDataSource_Inserting(ByVal sender As Object, ByVal e As SqlDataSourceCommandEventArgs)
Line 4: e.Command.Parameters["@UpdateBy"].Value = User.Identity.Name
Line 5: e.Command.Parameters["@LastUpdate"].Value = DateTime.Now.ToShortDateString()
Line 6: End Sub

Fritz Onion wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 02-13-2006 12:38 PM
VB uses '(' instead of '[' for indexing:
e.Command.Parameters("@UpdateBy").Value = User.Identity.Name
Clay McKinney wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 02-14-2006 7:07 AM
Thank you so much.
Jason Burton wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 02-14-2006 8:41 PM
Thanks so much... Fritz has the answer again! :)
saeed abaskhah wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 04-04-2006 9:33 PM
i need to change select parameter in "where" cluses in any part of source without "e"
CStick wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 04-22-2006 9:01 AM
Your blog is being added to my favorites now, thank you for this trick. In my case, I am using an objectdatasource with the onSelecting event, but it works very similar. Thx again.
Alistair wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 04-26-2006 8:53 AM
I'm trying to do exactly the same thing using the ObjectDataSource and it's OnInserting and OnUpdating properties.

protected void insertCompany_LastChangeUserNameParam(object sender, ObjectDataSourceMethodEventArgs e)
{
// The business object expects a custom type. Build it
// and add it to the parameters collection.
CSI.Company c = (CSI.Company)paramsFromPage["c"];
c.LastChangeUserName = User.Identity.Name;

e.InputParameters.Clear();
e.InputParameters.Add("c", c);

}

I get "The OrderedDictionary is readonly and cannot be modified." on the e.InputParameters.Clear() line.

What am I doing wrong?
Matthew wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 05-11-2006 5:45 AM
Just can't seem to get this to work. I get no error's - all other fields in the database update. Trying to do this against the objectdatasource.

Sub CommentsObject_Inserting(ByVal sender As Object, ByVal e As SqlDataSourceCommandEventArgs)
e.Command.Parameters("@user_name").Value = User.Identity.Name
e.Command.Parameters("@comment_date").Value = DateTime.Now.ToShortDateString()
End Sub


<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataKeyNames="comment_id"
DataSourceID="CommentsObject" DefaultMode="Insert" Height="1px" Width="712px">
<Fields>
<asp:BoundField DataField="wine_user_name" HeaderText="wine_user_name" SortExpression="wine_user_name" InsertVisible="False" />
<asp:BoundField DataField="comment" HeaderText="comment" SortExpression="comment" />
<asp:BoundField DataField="comment_date" HeaderText="comment_date" SortExpression="comment_date" InsertVisible="False" />
<asp:BoundField DataField="wine_rating" HeaderText="wine_rating" SortExpression="wine_rating" />
<asp:BoundField DataField="comment_id" HeaderText="comment_id" InsertVisible="False" SortExpression="comment_id" />
<asp:BoundField DataField="wine_id" HeaderText="wine_id" SortExpression="wine_id" InsertVisible="False" />
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
<asp:ObjectDataSource ID="CommentsObject" runat="server" InsertMethod="Insert" OldValuesParameterFormatString="original_{0}"
SelectMethod="GetData" TypeName="winesTableAdapters.selectWineCommentTableAdapter">
<SelectParameters>
<asp:QueryStringParameter Name="wineid" QueryStringField="wineid" Type="Int32" />
</SelectParameters>
<InsertParameters>
<asp:Parameter Name="wine_user_name" Type="String" />
<asp:Parameter Name="comment" Type="String" />
<asp:Parameter Name="comment_date" Type="DateTime" />
<asp:Parameter Name="wine_rating" Type="Int32" />
<asp:QueryStringParameter Name="wine_id" QueryStringField="wineid" Type="Int32" />
</InsertParameters>
</asp:ObjectDataSource>
Michael wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 06-11-2006 7:31 AM
Perfect! Thanks for the information. I was looking for this for a _long_ time.
mary wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 06-14-2006 2:02 PM
I receive an error message trying to use this with an AccessDataSource:

Parser Error Message: Literal content ('OnInserting="defectsDataSource_Inserting"') is not allowed within a 'System.Web.UI.WebControls.AccessDataSource'.

Source Error:


Line 78: OldValuesParameterFormatString="original_{0}" SelectCommand="SELECT [Cat_code], [SME_code], [Pub_code], [Pub_name], [Pub_date], [Pub_format], [Pub_location], [Pub_nopages] FROM [TblPubdetail]"
Line 79: UpdateCommand="UPDATE [TblPubdetail] SET [Pub_format] = ?, [Pub_location] = ?, [Pub_nopages] = ? WHERE [Cat_code] = ? AND [SME_code] = ? AND [Pub_code] = ? AND [Pub_name] = ? AND [Pub_date] = ? AND [Pub_format] = ? AND [Pub_location] = ? AND [Pub_nopages] = ?">
Line 80: OnInserting="defectsDataSource_Inserting"
Line 81: <InsertParameters>
Line 82: <asp:Parameter Name="Cat_code" Type="Int16" />

Caleb wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 06-22-2006 3:28 PM
SuperSweet! (dont mistake the exclamation point for a "not")
;o)
Fred wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 07-10-2006 11:04 AM
I have to agree with David Taylor. Not having access to te page level is a real pain. You can access any of the controls using a ControlParameter but I have not to found a way to access the page itself.
Mariano wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 07-20-2006 12:19 AM
Exactly what I was looking for! Thanks!
bdiaz wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 09-26-2006 6:50 PM
For those that were looking for the functionality described by David and Richard at the begining of the comments to this blog, you can pull values from page level properties using the following:

<asp:ControlParameter Name="UserName" ControlID="__Page" PropertyName="UserName" />

__Page is a *special* identifier for the current page object.

Hope that helps!

Bobby Diaz
Member of the .netTiers team.
Jenny wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 10-23-2006 5:06 PM
I'm trying to convert this to VB too.
Code behind:
Sub SqlDataSource1_Inserting(ByVal sender As Object, ByVal e As SqlDataSourceCommandEventArgs)
e.Command.Parameters.Item("@UserLastUpdate").Value = User.Identity.Name
End Sub
Keep coming up with:
Cannot insert the value NULL into column 'UserLastUpdate', table 'C_Scheduler.dbo.tbl_Building'; column does not allow nulls. INSERT fails.


What's not working? Thanks for any suggestions.
Dan Clem wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 12-30-2006 3:02 PM
OUTSTANDING! Thanks for the blog and thanks to Bobby Diaz for the final answer!
Özgür wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 03-27-2007 6:46 AM
many many Thanks for the Solution that you have shared with us.

greetings from Germany :)))
Mrb wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 06-03-2007 6:59 AM
This has helped me out.... Well Done tx for you post!!
oa wrote re: Programmatic parameter population with declarative data sources in ASP.NET 2.0
on 11-12-2007 1:07 PM
THanks! hit the spot!
seiti.eti.br » Blog Archive » DataSource Parameters: falta um PropertyParameter! wrote seiti.eti.br &raquo; Blog Archive &raquo; DataSource Parameters: falta um PropertyParameter!
on 09-22-2008 3:44 PM

Pingback from  seiti.eti.br  » Blog Archive   » DataSource Parameters: falta um PropertyParameter!

Add a Comment

(required)  
(optional)
(required)  
Remember Me?