Tair had a brainstorm this week: why not use a custom UserControl together with a DataList and an ObjectDataSource? When using a UserControl, we can set one property of the type of our business entity in the control, and in the setter of that property fill the child controls of the UserControl with the needed data.
Just to show that this can be done, I wrote up some stupid code. I'll put it up in a minute at cc.jct.ac.il/~fast/ObjectDataSrcExample.zip. Anyway, here's what I did:
Just as in the past, I have a Manager class and an Info (business entity) class in the BL. The Manager class has a Select() method that returns a strongly-typed List of my Info class.
namespace PizzaPlanet
{
public class PizzaInfo
{
public string Size { get; set; }
public bool Peppers { get; set; }
public bool Olives { get; set; }
public bool Mushrooms { get; set; }
public PizzaInfo AllInfo { get { return this; } }
}
public style PizzaManager
{
public List<PizzaInfo> Select(List<PizzaInfo> SavedList)
{
return SavedList;
}
}
}
Note what's different this time: my Info class has an extra property AllInfo that returns this. Meaning the whole business entity. When you use an ObjectDataSource for databinding, it exposes the properties of every entity in the returned list, but not the entire encapsulated object. So it's convenient to have a property that returns this.
Now, here's the source code for my user control:
public partial class PizzaControl : System.Web.UI.UserControl
{
private PizzaPlanet.PizzaInfo _internalPizza;
public PizzaPlanet.PizzaInfo InternalPizza {
get { return _internalPizza; }
set {
_internalPizza = value;
if (_internalPizza.Size == "Small")
{
imgPizza.Width = 200;
imgPizza.Height = 200;
}
else
{
imgPizza.Width = 400;
imgPizza.Height = 400;
}
if (_internalPizza.Mushrooms)
{
if (_internalPizza.Olives)
{
if (_internalPizza.Peppers)
{
imgPizza.ImageUrl = "~/images/mushroomOlivePepperPizza.png";
}
else
{
imgPizza.ImageUrl = "~/images/mushroomOlivePizza.png";
}
}
else
{
if (_internalPizza.Peppers)
{
imgPizza.ImageUrl = "~/images/mushroomPepperPizza.png";
}
else
{
imgPizza.ImageUrl = "~/images/mushroomPizza.png";
}
}
}
else
{
if (_internalPizza.Olives)
{
if (_internalPizza.Peppers)
{
imgPizza.ImageUrl = "~/images/olivePepperPizza.png";
}
else
{
imgPizza.ImageUrl = "~/images/olivePizza.png";
}
}
else
{
if (_internalPizza.Peppers)
{
imgPizza.ImageUrl = "~/images/pepperPizza.png";
}
else
{
imgPizza.ImageUrl = "~/images/plainPizza.png";
}
}
}
}
}
}
The control itself contains one Image, named imgPizza. All my .cs is doing is setting the toppings and size of the picture when InternalPizza is set.
Now for the actual form:
we have an ObjectDataSource with its TypeName set to my Manager class. The SelectMethod is set to PizzaManager's Select() method.
To make this sample easy to work with, I have the user enter new pizzas, which the order button saves in the Session, and then when the form reloads the ObjectDataSource passes the saved list from the Session to the PizzaManager (note that I disabled ViewState for all my controls).
The visible part of the form is a DataList. The DataList accepts ItemTemplates only (no BoundColumns or other predefined options here). So our ItemTemplate consists of exactly one control: the UserControl we defined.
(For the uninitiated, here's a quick list of what to do:
Add a DataList to your form.
Click on the side-arrow next to the new DataList.
Set the DataSource to your ObjectDataSource.
Click EditTemplates.
Select ItemTemplate.)
Normally, we would set the bound properties of the control contained in the Template via a wizard. When working with a UserControl, though, custom properties don't register in the wizard, so we're going to do this by hand. The InternalPizza property of our PizzaControl needs to be bound to Eval("AllInfo") -- remember, InternalPizza is of type PizzaInfo, and AllInfo returns the entire object. Here's the code from the .aspx:
<asp:DataList ID="DataList1" runat="server" DataSourceID="odsPizza"
EnableViewState="False">
<ItemTemplate>
<uc1:PizzaControl ID="PizzaControl1" runat="server"
InternalPizza='<%# Eval("AllInfo") %>' />
</ItemTemplate>
</asp:DataList>
That just about wraps it up. I think I'll go have lunch...
Showing posts with label ObjectDataSource. Show all posts
Showing posts with label ObjectDataSource. Show all posts
01 September 2009
02 July 2009
Pile on the layers
Here's a quote from MSDN:
"Most ASP.NET data source controls, such as the SqlDataSource, are used in a two-tier application architecture where the presentation layer (the ASP.NET Web page) communicates directly with the data tier (the database, an XML file, and so on)."
This is a cute way of saying that all those nice data-bound ASP.net controls - the ones that are supposed to take a data source and then do all the work of Selecting, Inserting, Updating, and Deleting (ie, CRUD) for you - will not work in a 3-layered structure.
That was the bad news, except that we knew it all along.
Here comes the good news:
"The ObjectDataSource works with a middle-tier business object to select, insert, update, delete, page, sort, cache, and filter data declaratively without extensive code."
http://msdn.microsoft.com/en-us/library/9a4kyhcx.aspx
So what do we need to do in order to get the data-sourced controls to work with an ObjectDataSource and, by extension, a BL and DAL?
Creating an ObjectDataSource Control Source Object has some answers. We need to define a stateless class (no non-static members) to provide CRUD logic for the data to populate the data-bound control on our form. Optionally, this class can also provide functions to filter the data and sort it. Then, using the (very handy) wizard provided by the ObjectDataSource, we select the class with the CRUD functions and identify the parameters it needs to preform Select.
Very well, but what about the parameters for Insert, Update, and Delete?
We can define another class to represent a record/row in our data schema. (The TypeName property of the ObjectDataSource must be set to this class.) All properties of the class will be polled (by reflection, one presumes) in order to fill the data-sourced control, and will be filled in return for the Update function. By setting the ConflictDetection property of the ObjectDataSource, we can even decide how updates should be done:
OverwriteChanges will simply fill an object with the new values and pass it to the Update function.
CompareAllValues will fill two objects: the first with the old values, the second with the new, and pass both to the Update function.
Two items of note:
1. The wizard will only work if your latest working build contains all the classes and functions you want to use! In other words, Build your project before running the wizard.
2. The CRUD functions I'm describing here are in my BL. They themselves do NOT do the actual persistence to the DB; rather they massage the data as appropriate and pass it in the appropriate format to the DAL, which does the real work.
"Most ASP.NET data source controls, such as the SqlDataSource, are used in a two-tier application architecture where the presentation layer (the ASP.NET Web page) communicates directly with the data tier (the database, an XML file, and so on)."
This is a cute way of saying that all those nice data-bound ASP.net controls - the ones that are supposed to take a data source and then do all the work of Selecting, Inserting, Updating, and Deleting (ie, CRUD) for you - will not work in a 3-layered structure.
That was the bad news, except that we knew it all along.
Here comes the good news:
"The ObjectDataSource works with a middle-tier business object to select, insert, update, delete, page, sort, cache, and filter data declaratively without extensive code."
http://msdn.microsoft.com/en-us/library/9a4kyhcx.aspx
So what do we need to do in order to get the data-sourced controls to work with an ObjectDataSource and, by extension, a BL and DAL?
Creating an ObjectDataSource Control Source Object has some answers. We need to define a stateless class (no non-static members) to provide CRUD logic for the data to populate the data-bound control on our form. Optionally, this class can also provide functions to filter the data and sort it. Then, using the (very handy) wizard provided by the ObjectDataSource, we select the class with the CRUD functions and identify the parameters it needs to preform Select.
Very well, but what about the parameters for Insert, Update, and Delete?
We can define another class to represent a record/row in our data schema. (The TypeName property of the ObjectDataSource must be set to this class.) All properties of the class will be polled (by reflection, one presumes) in order to fill the data-sourced control, and will be filled in return for the Update function. By setting the ConflictDetection property of the ObjectDataSource, we can even decide how updates should be done:
OverwriteChanges will simply fill an object with the new values and pass it to the Update function.
CompareAllValues will fill two objects: the first with the old values, the second with the new, and pass both to the Update function.
Two items of note:
1. The wizard will only work if your latest working build contains all the classes and functions you want to use! In other words, Build your project before running the wizard.
2. The CRUD functions I'm describing here are in my BL. They themselves do NOT do the actual persistence to the DB; rather they massage the data as appropriate and pass it in the appropriate format to the DAL, which does the real work.
Subscribe to:
Posts (Atom)