October 2008 - Posts
The End of LINQ To SQL?
Today I read a few
blog posts that were
published regarding
the announcement
on the ADO.NET Team Blog
(David Hayden, Oren Eini
and more).
That announcement included the following lines which made everybody furious:
”We’re making significant investments in the Entity Framework such that
as of .NET 4.0 the Entity Framework will be our recommended data access
solution for LINQ to relational scenarios. We are listening to customers regarding
LINQ to SQL and will continue to evolve the product based on feedback we receive
from the community as well.”.
I share the same thoughts as David Hayden expressed in his post. I think that if
Microsoft is going to abandon LINQ to SQL, they should publish the framework on
CodePlex and let the community continue the development effort.
This way the people that started using LINQ to SQL in the last year
will have a support for that framework and won’t have to change all their data
access because the framework is legacy. Also, LINQ to SQL will continue to be
a light weight alternative to the Entity Framework like other open source frameworks
(NHibernate, SubSonic, Castle ActiveRecord and more).
If you think like the other community members and also like I do, write about it and
show that you care.
Back to Basics – How to Retrieve a Value Returned by a SP
In the following posts
I’m going to describe
basic tools which every
developer needs to
know. In today’s post
I’m going to explain
how to retrieve a value
returned by a stored procedure.
The Story Behind the Post
During this week one of my colleagues asked me if it’s possible get a
returned value of a stored procedure with the infrastructure I wrote to my
current project. The question regarded a returning value and not output
parameters which are another way to return a value from a stored procedure.
My answer was absolutely yes and I explained the process to him and to the
other members of the team who didn’t know how you can achieve that.
Stored Procedures and Returned Value
Every stored procedure returns a value even if you don’t use the
return keyword. That default returned value is an integer that holds
the value 0. The return value is limited to returning integer values only.
We can use the return functionality in order to return values to the calling
environment and notify some things like errors for example.
The Stored Procedure Example
In my example I use a School database. The stored procedure:
CREATE PROCEDURE SP_IsCourseExists
@CourseID int
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT CourseID FROM Course WHERE CourseID = @CourseID)
RETURN 1
ELSE
RETURN 0
END
The stored procedure checks if exists in the database a course with
the given CourseID and returns 1 if true otherwise 0.
How to Retrieve a Value Returned by a Stored Procedure?
We can use simple ADO.NET functionality in order to get the returned value.
The only thing to do is to add a new parameter to the parameter list that we
send to the with our command. The parameter needs to have a Direction of
ParameterDirection.ReturnValue to indicate that its going to hold the return
value. Also, it’s DbType must be of type integer.
Lets look at an example of a simple way to call the stored procedure using
ADO.NET:
private string IsCourseExists(int courseID)
{
using (SqlConnection conn =
new SqlConnection(
ConfigurationManager.ConnectionStrings["School"].ConnectionString))
{
// create the command
SqlCommand cmd = new SqlCommand("SP_IsCourseExists", conn);
cmd.CommandType = CommandType.StoredProcedure;
// create the returned value parameter
SqlParameter returnedValue = cmd.CreateParameter();
returnedValue.Direction = ParameterDirection.ReturnValue;
returnedValue.SqlDbType = SqlDbType.Int;
returnedValue.ParameterName = "@ReturnValue";
// create the course id input parameter
SqlParameter courseIDParam = cmd.CreateParameter();
courseIDParam.ParameterName = "@CourseID";
courseIDParam.Value = courseID;
cmd.Parameters.Add(returnedValue);
cmd.Parameters.Add(courseIDParam);
// execute the command
conn.Open();
cmd.ExecuteNonQuery();
string output = (int)returnedValue.Value == 0 ?
"Not exists" : "Exists";
conn.Close();
return output;
}
}
As you can see I create two parameters the first will hold the returned value.
After executing the command the returnedValue parameter will hold the
returned value from the stored procedure and I’m able to retrieve it and
use it in my code.
Summary
Lets sum up, in the post I showed how easy it is to retrieve a value
returned by a stored procedure. The example I showed is using a traditional
ADO.NET functionality. In my current project I use the enterprise library DAAB
in order to get the same functionality.
Action Sequences in ADO.NET Data Services
In this post I’m
going to demonstrate
how you can use the
action sequence feature
of the ADO.NET Data
Services AJAX Client Library.
What is Action Sequence?
As written in my previous post, when we perform a CRUD operation using the
AJAX Client Library it is immediately transmitted and performed.
In the server side when using the DataServiceContext the operations are cached
and only transmitted when we use the SaveChanges method.
Action sequence enable us to perform the same unit of work functionality but on
the client side. Using the ActionSequence object, which is part of the ADO.NET
Data Services AJAX Client Library, we can cache the CRUD operations and submit
them together. An ActionSequence object is a wrapper object on top of a
Sys.Data.DataService object which collect the CRUD operations and uses the
DataService object it holds to submit them.
Creating Action Sequence
We have two ways to create an ActionSequence object:
One thing to know about the ActionSequence object is that once it was created
you can’t change the underling DataService object but you can access it through
the get_service property.
After we have the action sequence in hand it’s time to understand how to
register CRUD operations inside of it.
Registering CRUD Operations to a ActionSequence
The ActionSequence exposes three methods to register the three CRUD
operation options:
- Update – use the addUpdateAction method:
sequence.addUpdateAction(course, "Courses(1)", context, true);
The method takes the same four parameters like the insert.
- Delete – use the addRemoveAction method:
sequence.addRemoveAction(course, "Courses(1)", context);
The method gets three parameters that are the same as the
three parameters the previous methods got. The first two
parameters are exclusively optional and you can use the first alone,
the second alone or both of them.
Saving Changes Using an Action Sequence
After registering the operations to an ActionSequence object it’s time to
run them. The “magic” is performed using the executeActions method:
sequence.executeActions(OnSuccess, context);
The method gets two parameters – the callback function and a user context.
The actions will be performed one by one. The callback function will be called
after the sequence operations will complete, regardless of their success or
failure. That means that if some of the operations failed the sequence will
continue with the other operations. The callback function gets three
parameters - an array of ActionResult objects, a boolean that indicate
whether one of the operations in the sequence failed and a context.
Using the ActionResult Object
The ActionResult object is the representation of a result of an individual
CRUD operation in the sequence. The array that is returned in the
executeActions method will hold the same number of ActionResult objects
as the number of the operations in the sequence. The ActionResult object
holds three properties – the result itself, the operation and the context of
the operation. The result property is the most interesting property. It can
hold the returned objects of insert or update operations if the boolean to
indicate if we want a return object was true. It can hold null otherwise
for insert or update. For delete operations it will always be null. If an error
occurred during the executing of an operation the result object will hold a
Sys.Data.DataServiceError that will indicate an error.
Cleaning the ActionSequence Object
After transmitting the operations as a unit of work using the action sequence
if we want to reuse the object we can call the clearActions method of the
object. The method will clear the sequence of all it’s operations.
Summary
Lets sum up, in the post I showed the how you can use the action sequence
feature of the ADO.NET Data Services AJAX Client Library. That feature enable
us to transmit a batch of operations to a data service from client side. This
feature and the ability to perform CRUD operations from client side makes
the data services world more flexible and versatile to use.
ADO.NET Data Services AJAX Client Library in VS2008 SP1 Release
In a previous post
I showed how to
build an Ajax client
for a data service.
In that post I added to
the solution a new
ASP.NET 3.5 Extensions Web Application project which came with the the
ASP.NET 3.5 Extensions preview. In that preview the ADO.NET data
services Ajax client library was embedded in the System.Web.Extensions
assembly. In the release of VS2008 SP1 the ADO.NET data services Ajax client
library is no longer a part of the System.Web.Extensions assembly.
The First Solution - ADO.NET Data Services AJAX Client Library
If you want to use the Ajax functionality without the need to use the
ASP.NET 3.5 Extensions Web Application template you can download the
library from here. The download contains two js files – DataService.js and
DataService.debug.js. After you downloaded and extracted the files you
can include them in every web application that you build.
Put the files in Scripts directory (or where ever you prefer to put it)
in your project and then register the script reference in the ScriptManager
like this:
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Path="~/Scripts/DataService.js" />
</Scripts>
</asp:ScriptManager>
After you registered the js file script reference you’ll be able to use it in
your client scripts.
Another Solution to the Problem
Another way to solve the problem is to use the System.Web.Extensions
assembly that came with the ASP.NET 3.5 Extensions preview. First you’ll
need to replace the System.Web.Extensions (version 3.5) that your project
reference with the System.Web.Extensions (version 3.6) that is located in
the following path:
C:\Program Files\Reference Assemblies\Microsoft\Framework\ASP.NET 3.5 Extensions.
After the replacement, you should go to the web.config file and replace the version
of System.Web.Extensions to 3.6.0.0 instead of 3.5.0.0. Doing so will enable every
web application to use the ADO.NET data services Ajax client library as I wrote in
the previous post.
Summary
I gave two ways to solve the problem of adding
ADO.NET data services Ajax client library to your project.
I tested the ways that I suggested and both of them are working well. If you ask me,
I recommend the first solution but you can choose from both of them.
You can use the first and then you’ll have to update the js files every time a new
version will appear in codeplex. You can use the second and you will be bound to the
System.Web.Extensions (version 3.6) assembly that is currently a part of the
ASP.NET 3.5 Extensions preview and not a part of the new SP1 release. The choice
is yours.
How to Perform CRUD Operations in ADO.NET Data Services Using Ajax Client
In the last post in
the ADO.NET data
services series I wrote
about how you can
consume data services
using Ajax client. In this post I’m going to explain how to perform CRUD
operations on a data service using an Ajax client.
DataServiceContext and Sys.Data.DataService
Before starting to understand how to perform CRUD operations with Ajax
client first I want to discuss the main difference between using
DataServiceContext, like I did in previous posts, and using
Sys.Data.DataService. When we use the DataServiceContext on the server
side the context caches all the CRUD operations that we make and the only
way to submit these changes is by using the SaveChanges method. In the
Sys.Data.DataService we don’t have this unit of work functionality by default.
Every operation that we perform on the Sys.Data.DataService object is
immediately submitted to the server. There is a way to cache the operations
using an action sequence but I’ll describe it in the next post.
Another difference is that in the Ajax situation we are using the JSON
functionality in order to build the objects on the client side
and submit them to the server. We can’t use the server classes because the
client doesn’t know them.
The Example Data Service Context
In the example I’m using the same data service context I built in the post
How to Perform CRUD Operations in ADO.NET Data Services Using a Custom Provider.
I changed the code that I wrote a little by adding the functionality to the
ResetResource method and the code looks like:
[DataServiceKey("CourseID")]
public class Course
{
#region Properties
/// <summary>
/// The course ID
/// </summary>
public int CourseID { get; set; }
/// <summary>
/// The course title
/// </summary>
public string Title { get; set; }
/// <summary>
/// The duration in days of the course
/// </summary>
public int Days { get; set; }
/// <summary>
/// The course academic credit
/// </summary>
public int Credit { get; set; }
#endregion
#region Methods
/// <summary>
/// Return a fixed list of courses
/// </summary>
/// <returns>A list of courses</returns>
public static List<Course> GetCourses()
{
return new List<Course> {
new Course() { CourseID = 1, Credit = 3, Days = 3 , Title = "Algorithms"},
new Course() { CourseID = 2, Credit = 5, Days = 5 , Title = "Data Structures"},
new Course() { CourseID = 3, Credit = 5, Days = 5 , Title = "Databases"}
};
}
#endregion
}
public class CoursesDataContext : IUpdatable
{
#region Members
private static List<Course> _courses;
#endregion
#region Properties
/// <summary>
/// Return the courses in a IQueryable format
/// </summary>
public IQueryable<Course> Courses
{
get
{
return _courses.AsQueryable();
}
}
#endregion
#region Ctor
/// <summary>
/// Construct a new CoursesDataContext object
/// </summary>
static CoursesDataContext()
{
_courses = Course.GetCourses();
}
#endregion
#region IUpdatable Members
public object CreateResource(string containerName, string fullTypeName)
{
// create the object using reflection
var objType = Type.GetType(fullTypeName);
var resourceToAdd = Activator.CreateInstance(objType);
// add the course to the courses in-memory list
_courses.Add((Course)resourceToAdd);
return resourceToAdd;
}
public void DeleteResource(object targetResource)
{
// remove the course form the courses list
_courses.Remove((Course)targetResource);
}
public object GetResource(IQueryable query, string fullTypeName)
{
object result = null;
var enumerator = query.GetEnumerator();
while (enumerator.MoveNext())
{
if (enumerator.Current != null)
{
result = enumerator.Current;
break;
}
}
if (fullTypeName != null &&
!fullTypeName.Equals(result.GetType().FullName))
{
throw new DataServiceException();
}
return result;
}
public object GetValue(object targetResource, string propertyName)
{
// get the property info using reflection
var targetType = targetResource.GetType();
var targetProperty = targetType.GetProperty(propertyName);
// retrun the value of the property
return targetProperty.GetValue(targetResource, null);
}
public void SetValue(object targetResource, string propertyName, object propertyValue)
{
// get the property info using reflection
Type targetType = targetResource.GetType();
PropertyInfo property = targetType.GetProperty(propertyName);
// set the property value
property.SetValue(targetResource, propertyValue, null);
}
public object ResolveResource(object resource)
{
// nothing to do just return the resource
return resource;
}
public void SaveChanges()
{
// object in memory - do nothing
}
public object ResetResource(object resource)
{
var resourceType = resource.GetType();
// get the list of attributes of the type
object[] attributes = resourceType.GetCustomAttributes(true);
// get the data service key attribute if exists
var keyAttribute = attributes.First<object>(
new Func<object, bool>(
attribute => attribute.GetType().Equals(typeof(DataServiceKeyAttribute))));
if (keyAttribute != null)
{
var resourceKeyNames = ((DataServiceKeyAttribute)keyAttribute);
foreach (var propertyInfo in resourceType.GetProperties())
{
if (!resourceKeyNames.KeyNames.Contains(propertyInfo.Name))
{
// if the property name isn't part of the keys colletion
// then set it to default value
var propType = propertyInfo.PropertyType;
object defaultValue =
propType.IsValueType
? Activator.CreateInstance(propType)
: null;
propertyInfo.SetValue(resource, defaultValue, null);
}
}
return resource;
}
// no key
return new Course();
}
public void SetReference(object targetResource, string propertyName, object propertyValue)
{
throw new NotImplementedException();
}
public void ClearChanges()
{
throw new NotImplementedException();
}
public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
{
throw new NotImplementedException();
}
public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
{
throw new NotImplementedException();
}
#endregion
}
I made that change because in the update operation of Ajax client the method
resets the data in the resource in order to fill it with the updated data.
The data service code stays the same.
Performing CRUD Operations on Data Service Using Ajax Client
I added to the example from the last post more functionality in order to show
how to perform the CRUD operations. I added three buttons to the web form
I built and it’s code is now:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Name="MicrosoftAjaxDataService.js" />
</Scripts>
</asp:ScriptManager>
<div>
<select id="ddlCourses">
</select>
<span id="spanError" style="display: none;"></span>
<br />
<input id="btnFill" type="button" value="Fill Drop Down List" onclick="Fill();" />
<input id="btnInsert" type="button" value="Insert Course" onclick="Create();" />
<input id="btnUpdate" type="button" value="Update Course" onclick="Update();" />
<input id="btnDelete" type="button" value="Delete Course" onclick="Delete();" />
</div>
<script type="text/javascript">
var proxy = new Sys.Data.DataService("/Services/CoursesService.svc");
function Fill() {
proxy.query("Courses", OnSuccess, OnFailure);
}
function OnSuccess(result, context, operation) {
if (result != null) {
var select = $get("ddlCourses");
var option;
for (i = 0; i < result.length; i++) {
option = new Option(result[i].Title, result[i].CourseID);
select.options.add(option);
}
}
}
function OnFailure(error, context, operation) {
var span = $get("spanError");
span.innerHTML = BuildErrorString(operation);
span.style.display = "inline";
}
function BuildErrorString(operation) {
var sb = new Sys.StringBuilder("Error occurred while performing operation ");
sb.append(operation);
sb.append(".");
return sb.toString();
}
function Clear() {
var select = $get("ddlCourses");
for (i = select.length; i >= 0; i--) {
select.remove(i);
}
}
function OnOperationSuccess(result, context, operation) {
if (result != null) {
Clear();
Fill();
}
}
function Create() {
var course = { Title: "Introduction to Computer science", Days: 5, Credit: 5 };
proxy.insert(course, "Courses", OnOperationSuccess, OnFailure);
}
function Update() {
proxy.query("Courses(1)", OnQueryForUpdateSuccess, OnFailure);
}
function Delete() {
proxy.remove(null, "Courses(1)", OnOperationSuccess, OnFailure);
}
function OnQueryForUpdateSuccess(result, context, operation) {
if (result != null) {
result.Title = "Introduction";
proxy.update(result, "Courses(1)", OnOperationSuccess, OnFailure);
}
}
</script>
</form>
Create
When we want to create an object and add it, we use the
Sys.Data.DataService object’s insert method.
The insert method gets six parameters:
proxy.insert(course, "Courses", OnOperationSuccess, OnFailure, context, true);
The parameters are:
- The inserted object
- The resource set Uri
- The success callback function
- The failure callback function
- The user context
- A flag to indicate if we want a return data which will return in the
success callback. The default value is true so you can omit that variable
if you want the inserted data to be returned.
Behind the scenes the HTTP POST method will be called to perform the create
operation. In my example the Create method creates a new course and add it to
the Courses list on the server.
Update
When we want to update a resource we need to have two things – the
resource that we want to update and the resource Uri. Without those two
you won’t be able to update the resource.
We use the Sys.Data.DataService object’s update method.
The update method gets the same six parameters like the insert method:
proxy.update(result, "Courses(1)", OnOperationSuccess, OnFailure, context, true);
Using the update method will cause an HTTP PUT request to the server.
In my example the Update method first searches a known resource and then
performs the update.
Delete
When we want to delete a resource we need to have one of two things -
the resource to delete or it’s resource Uri.
We use the Sys.Data.DataService object’s remove method.
The remove method take five parameters instead of six:
proxy.remove(course, "Courses(1)", OnOperationSuccess, OnFailure, context);
The parameters are:
- The removed object
- The resource set Uri of the removed object
- The success callback function
- The failure callback function
- The user context
The two parameters are exclusively optional and you can use the first alone,
the second alone or both of them.
Using the remove method will cause an HTTP DELETE request to the server.
In my example the Delete method removes the first resource from the list
on the server. If you’ll press the delete button again it will cause an exception
because that resource was omitted.
Summary
Lets sum up, in the post I explained how you can perform CRUD operations
from the client side using Ajax. These operations are part of the
Sys.Data.DataService’s API and are very useful if you want to manipulate
your data from client side. As I wrote, the operations are performed immediately
after you call them and therefore you don’t have a unit of work functionality.
In the next post in the series I’m going to show how to cache and submit the
operations you perform using an action sequence
Building an Ajax Client for ADO.NET Data Service
The post is another post
in the series of posts
that I write in the
ADO.NET data services
subject. You can read
the previous posts here.
In the post I’m going to explain how to build an Ajax client for a data service.
Revisiting Previous Posts Example
In the example I’m going to use the same course data context that I used in my
previous posts. If you are not familiar with the example, you can go to the following
links to update:
Building the Project and Adding the Data Service
The first thing to do is to add to the solution a new ASP.NET 3.5 Extensions Web
Application:
After we have the project I add a Services directory to it and put the data service
file CoursesService.svc inside of it. The CoursesDataContext class and the Course
class will be in a separate class library project. After adding the data service your
project should look like:
Consuming a Data Service from an Ajax Client
By now we have a starting project. Lets understand how the Ajax “magic” will work.
In the ASP.NET Ajax client library there is a new namespace – Sys.Data. This
namespace hosts the main client side object to interact with data services –
DataService. This object holds all the functionality we need in order to have JavaScript
abstraction over a data service. In order to have that object inside our web application
we need to register its js file in our pages. You do that by adding a script reference to
the ScriptManager:
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Name="MicrosoftAjaxDataService.js" />
</Scripts>
</asp:ScriptManager>
After registering the script reference we can start playing with the data services in our
client side code. In order to use the DataService object we first need to instantiate it.
The DataService constructor gets a serviceUri string as a parameter. After that we can
use the DataService API to perform queries and CRUD operations. The communication
between the data service and its consuming code will be in JSON format (not in ATOM)
and on the client side we will get JSON objects that resemble our server side objects.
Consuming a Data Service from an Ajax Client Example
In the example I’m going to show I have a web form that holds three objects: an Html
button, an Html select object and a span to hold errors. Pressing the button will perform
an asynchronous call through Ajax to the data service and fill the select object with all
the courses I hold on the server side. Lets look at the web form:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Name="MicrosoftAjaxDataService.js" />
</Scripts>
</asp:ScriptManager>
<div>
<select id="ddlCourses">
</select>
<span id="spanError" style="display:none;"></span>
<br />
<input id="btnFillDll" type="button"
value="Fill Drop Down List" onclick="FillDropDown();" />
</div>
</form>
As I wrote this is a simple form. The only relevant thing to understand is the call for
FillDropDown in the onclick event of the button. This call will start the process of
getting the courses from the data service.
After we built our web form lets build our scripts. First, I’ll start with the FillDropDown
function:
function FillDropDown() {
var proxy = new Sys.Data.DataService("/Services/CoursesService.svc");
proxy.query("Courses", OnSuccess, OnFailure);
}
In the function I construct the DataService object with the Uri of the service.
Then, I call the query function of the DataService object to perform the query I
want. The query function gets four parameters (I use only three in the example) -
the query to perform, a callback function on success, a callback function on failure
and a context. My query search all the courses available on the data service.
The code for the callback functions:
function OnSuccess(result, context, operation) {
if (result != null) {
var select = $get("ddlCourses");
var option;
for (i = 0; i < result.length; i++) {
option = new Option(result[i].Title, result[i].CourseID);
select.options.add(option);
}
}
}
function OnFailure(error, context, operation) {
var span = $get("spanError");
span.innerHTML = BuildErrorString(operation);
span.style.display = "inline";
}
function BuildErrorString(operation) {
var sb = new Sys.StringBuilder("Error occurred while performing operation ");
sb.append(operation);
sb.append(".");
return sb.toString();
}
In the OnSuccess, I take the coming result and parse it to the select options.
In the OnFailure, I write in the error span the operation that failed.
Putting all the example together and clicking the button will result in the following
screen:
A last thing to consider – JavaScript can’t make cross-domain calls. This restriction
doesn’t allow you to consume a data service from client side if that data service is
outside your domain.
Summary
Let sum up, in the post I explained how to consume a data service through Ajax client.
This is really cool because instead of building a data-centric web service in order
to get better user experience through Ajax, I now can build a data service to do
this instead. Also, the DataService object include the options to do CRUD operations
through Ajax that I didn’t covered in the post and will be covered in a follow up post.
Where is The Application Block Software Factory?
As someone who uses
Enterprise Library for many
cases, I recently searched
for the Application Block
Software Factory in the
release of ent-lib 4.0 and didn’t find it.
As a result, I went to the internet to search it and found Grigori Melnik’s
post that indicates that “two pieces were removed from the release:
Application Block Software Factory and Strong-Naming Guidance Package”
In the release of ent-lib 4.0 the Application Block Software Factory and
Strong-Naming Guidance Package were removed and were inserted to the
EntLibContrib. Going to the following link and downloading the last release of
EntLibContrib raised another problem.
I tried to run the setup of Application Block Software Factory and got errors.
Apparently, the Application Block Software Factory misses a strong name key -
25StrongName.snk and therefore it isn’t working. The work around for the problem
is to remove the snk or to generate it by myself. I chose the easy way – the first solution.
Application Block Software Factory comes with all it sources so I opened its solution.
In order to remove the snk I went to the Properties menu of the projects that have
signing with snk and in the Signing tab I unchecked the “Sign the assembly” checkbox.
After compiling the solution and then rebuilding the BlockFactorySetup project a setup
project that I could run was generated.
Before running the setup don’t forget to install GAX (Guidance Automation Extensions)
or else the installation won’t work.
Building a Simple Iframe Custom Control
In today’s post I’m going
to explain how to build
a simple custom control – in my
case it’s going to be an Iframe
custom control.
What is an Iframe?
Iframes are a web page elements that create an inline frame which contains
another document or web page. Iframes load another html document within their
<iframe> tags. I don’t recommend the use of Iframes because they are bad for
search engine optimizations but sometimes it is proper to use them.
Building a Simple Iframe Custom Control
The first thing to do before starting to build the Iframe control is to understand
what are the optional attributes that it can have. My favorite site to check such
things is W3 Schools. If you are not familiar with that site I’m very recommending to
go and check it out. Searching the site for Iframe tag returns the following link:
HTML iframe tag. In that link I collect all the Iframe optional attributes.
After knowing what are the Iframe attributes we can start building the custom control.
First Step
From File menu choose New and then choose Project.
From the Web project types choose ASP.NET Server Control to add a new ASP.NET
server control class library.
Second Step
Clean the class that was generated and insert the next code instead.
[ToolboxData("<{0}:IFrame runat=server></{0}:IFrame>")]
public class IFrame : WebControl
{
}
Third Step
Add the following enum above the web control definitions:
#region Helpers
public enum eScrolling
{
Auto = 0,
Yes = 1,
No = 2,
}
#endregion
That enum will help to determine the scrolling definitions for the IFrame
control.
Forth Step
Add the following properties to the IFrame control:
#region Properties
/// <summary>
/// Specifies how to align the iframe according
/// to the surrounding text.
/// </summary>
[Category("Appearance")]
public HorizontalAlign Align { get; set; }
/// <summary>
/// Specifies whether or not to display a
/// frame border
/// </summary>
[Category("Appearance")]
public bool FrameBorder { get; set; }
/// <summary>
/// A URL to a long description of the
/// frame contents
/// </summary>
[Category("Appearance")]
public string LongDesc { get; set; }
/// <summary>
/// Defines the top and bottom margins of the
/// iframe
/// </summary>
[Category("Appearance")]
public int MarginHeight { get; set; }
/// <summary>
/// Defines the left and right margins of the
/// iframe
/// </summary>
[Category("Appearance")]
public int MarginWidth { get; set; }
/// <summary>
/// Define scroll bars
/// </summary>
[Category("Appearance")]
public eScrolling Scrolling { get; set; }
/// <summary>
/// The URL of the document to show in the
/// iframe
/// </summary>
[Editor("System.Web.UI.Design.UrlEditor, System.Design",
typeof(UITypeEditor)),
Bindable(true), DefaultValue(""), UrlProperty]
public string Src { get; set; }
/// <summary>
/// Specifies a unique name of the iframe
/// (to use in scripts)
/// </summary>
public string FrameName { get; set; }
#endregion
These are all the IFrame optional properties that we got from W3 Schools.
Pay attention that there are some properties like width and height that
comes a long with the WebControl definitions.
Fifth Step
Add the following const variables to the IFrame control:
#region Consts
private const string FRAME_BORDER = "frameborder";
private const string LONG_DESC = "longdesc";
private const string MARGIN_HEIGHT = "marginheight";
private const string MARGIN_WIDTH = "marginwidth";
private const string SCROLLING = "scrolling";
#endregion
These const will help us to write the attributes of the control.
Sixth Step
Add the Render method to write the IFrame to the page:
#region Methods
protected override void Render(HtmlTextWriter writer)
{
// Don't render if no src available
if (string.IsNullOrEmpty(Src))
{
return;
}
writer.WriteBeginTag(HtmlTextWriterTag.Iframe.ToString());
RenderProperties(writer);
writer.Write(HtmlTextWriter.TagRightChar.ToString());
writer.WriteEndTag(HtmlTextWriterTag.Iframe.ToString());
}
private void RenderProperties(HtmlTextWriter writer)
{
writer.WriteAttribute(HtmlTextWriterAttribute.Id.ToString(),
ID);
writer.WriteAttribute(HtmlTextWriterAttribute.Src.ToString(),
Src);
writer.WriteAttribute(HtmlTextWriterAttribute.Align.ToString(),
Align.ToString());
writer.WriteAttribute(
FRAME_BORDER, FrameBorder ? 1.ToString() : 0.ToString());
writer.WriteAttribute(MARGIN_HEIGHT, MarginHeight.ToString());
writer.WriteAttribute(MARGIN_WIDTH, MarginWidth.ToString());
writer.WriteAttribute(SCROLLING, Scrolling.ToString());
writer.WriteAttribute(
HtmlTextWriterAttribute.Height.ToString(), Height.ToString());
writer.WriteAttribute(
HtmlTextWriterAttribute.Width.ToString(), Width.ToString());
if (!string.IsNullOrEmpty(CssClass))
{
writer.WriteAttribute(
HtmlTextWriterAttribute.Class.ToString(), CssClass);
}
if (!string.IsNullOrEmpty(FrameName))
{
writer.WriteAttribute(
HtmlTextWriterAttribute.Name.ToString(),FrameName);
}
if (!string.IsNullOrEmpty(LongDesc))
{
writer.WriteAttribute(LONG_DESC, LongDesc);
}
}
#endregion
The Render method is one of two important methods that you have to
override when building a custom control.
The second method is CreateChildControls which wasn’t implemented.
After that the control is almost ready to use.
Last Step
I added a constructor that initialize the properties for default values.
You don’t have to write it but here is the code:
#region Ctor
/// <summary>
/// Construct a new IFrame contorl and
/// insert default values to the IFrame
/// </summary>
public IFrame()
{
Align = HorizontalAlign.NotSet;
FrameBorder = false;
LongDesc = string.Empty;
MarginHeight = 0;
MarginWidth = 0;
FrameName = string.Empty;
Scrolling = eScrolling.Auto;
Height = Unit.Percentage(100);
Width = Unit.Percentage(100);
}
#endregion
Wrapping up
After you added all the code this is how the IFrame control should look like:
#region Helpers
public enum eScrolling
{
Auto = 0,
Yes = 1,
No = 2,
}
#endregion
#region Control
[ToolboxData("<{0}:IFrame runat=server></{0}:IFrame>")]
public class IFrame : WebControl
{
#region Consts
private const string FRAME_BORDER = "frameborder";
private const string LONG_DESC = "longdesc";
private const string MARGIN_HEIGHT = "marginheight";
private const string MARGIN_WIDTH = "marginwidth";
private const string SCROLLING = "scrolling";
#endregion
#region Properties
/// <summary>
/// Specifies how to align the iframe according
/// to the surrounding text.
/// </summary>
[Category("Appearance")]
public HorizontalAlign Align { get; set; }
/// <summary>
/// Specifies whether or not to display a
/// frame border
/// </summary>
[Category("Appearance")]
public bool FrameBorder { get; set; }
/// <summary>
/// A URL to a long description of the
/// frame contents
/// </summary>
[Category("Appearance")]
public string LongDesc { get; set; }
/// <summary>
/// Defines the top and bottom margins of the
/// iframe
/// </summary>
[Category("Appearance")]
public int MarginHeight { get; set; }
/// <summary>
/// Defines the left and right margins of the
/// iframe
/// </summary>
[Category("Appearance")]
public int MarginWidth { get; set; }
/// <summary>
/// Define scroll bars
/// </summary>
[Category("Appearance")]
public eScrolling Scrolling { get; set; }
/// <summary>
/// The URL of the document to show in the
/// iframe
/// </summary>
[Editor("System.Web.UI.Design.UrlEditor, System.Design",
typeof(UITypeEditor)),
Bindable(true), DefaultValue(""), UrlProperty]
public string Src { get; set; }
/// <summary>
/// Specifies a unique name of the iframe
/// (to use in scripts)
/// </summary>
public string FrameName { get; set; }
#endregion
#region Ctor
/// <summary>
/// Construct a new IFrame contorl and
/// insert default values to the IFrame
/// </summary>
public IFrame()
{
Align = HorizontalAlign.NotSet;
FrameBorder = false;
LongDesc = string.Empty;
MarginHeight = 0;
MarginWidth = 0;
FrameName = string.Empty;
Scrolling = eScrolling.Auto;
Height = Unit.Percentage(100);
Width = Unit.Percentage(100);
}
#endregion
#region Methods
protected override void Render(HtmlTextWriter writer)
{
// Don't render if no src available
if (string.IsNullOrEmpty(Src))
{
return;
}
writer.WriteBeginTag(HtmlTextWriterTag.Iframe.ToString());
RenderProperties(writer);
writer.Write(HtmlTextWriter.TagRightChar.ToString());
writer.WriteEndTag(HtmlTextWriterTag.Iframe.ToString());
}
private void RenderProperties(HtmlTextWriter writer)
{
writer.WriteAttribute(HtmlTextWriterAttribute.Id.ToString(),
ID);
writer.WriteAttribute(HtmlTextWriterAttribute.Src.ToString(),
Src);
writer.WriteAttribute(HtmlTextWriterAttribute.Align.ToString(),
Align.ToString());
writer.WriteAttribute(
FRAME_BORDER, FrameBorder ? 1.ToString() : 0.ToString());
writer.WriteAttribute(MARGIN_HEIGHT, MarginHeight.ToString());
writer.WriteAttribute(MARGIN_WIDTH, MarginWidth.ToString());
writer.WriteAttribute(SCROLLING, Scrolling.ToString());
writer.WriteAttribute(
HtmlTextWriterAttribute.Height.ToString(), Height.ToString());
writer.WriteAttribute(
HtmlTextWriterAttribute.Width.ToString(), Width.ToString());
if (!string.IsNullOrEmpty(CssClass))
{
writer.WriteAttribute(
HtmlTextWriterAttribute.Class.ToString(), CssClass);
}
if (!string.IsNullOrEmpty(FrameName))
{
writer.WriteAttribute(
HtmlTextWriterAttribute.Name.ToString(), FrameName);
}
if (!string.IsNullOrEmpty(LongDesc))
{
writer.WriteAttribute(LONG_DESC, LongDesc);
}
}
#endregion
}
#endregion
Testing the New Control
After building a control you have to test it’s functionality.
I added to the solution a new web application project and added two
web forms. The first web form only write to the response “Hello”.
The second web form holds the new IFrame control with it’s Src property
directing to the first web form. This test only shows that the control is working.
You can take the control, use it and add to it functionality as you like.
Summary
Lets sum up, in the post I explained a little about Iframes and also showed
how to build an Iframe custom control from scratch. I used that control once in a
testing project that needed to show an output pdf file that it’s location was created
dynamically. It’s still being used there until today and because it’s so simple there are
no problems with it.
Back to Basics – The Default Keyword in Generic Code
In the following posts
I’m going to describe
basic tools which every
developer needs to
know. In today’s post
I’m going to explain
what is the default keyword in generic code and how to use it.
What is the Default Keyword?
The default keyword has dual meaning since .Net framework 2.0 was published.
The first meaning is inside the switch construct to inform the switch that if non
of the cases is valid then the switch will go to the default block.
The second meaning was new in .Net framework 2.0 and it was to set a type
parameter to its default value. The need for that came from generics which enables
us to make a type parameter which is not known until runtime. This behavior raised
a question – how to assign a default value to a parameterized type T when you do
not know in advance whether that type is value type, struct or a reference type.
To our rescue came the default keyword.
The default keyword when operated on a parameterized type T will return null for
reference types and 0 for value types. For structs, each of the struct’s members will
be initialized according to its type – value types to 0 and reference types to null.
How to Use the Default Keyword?
Lets look at a simple example of how to use the default keyword.
In the example I use the following class:
class MyClass<T>
{
#region Properties
/// <summary>
/// The value that MyClass holds
/// </summary>
public T Value { get; private set; }
#endregion
#region Ctor
/// <summary>
/// Construct a MyClass object and set all
/// properties to their default state
/// </summary>
public MyClass()
{
Value = default(T);
}
#endregion
}
The class is insert the default value for its Value property in the constructor.
Then run the following code:
class Program
{
static void Main(string[] args)
{
MyClass<int> myClassInt = new MyClass<int>();
MyClass<DateTime> myClassDT = new MyClass<DateTime>();
MyClass<object> myClassObj = new MyClass<object>();
Console.WriteLine("myClassInt Value property: {0}",
myClassInt.Value);
Console.WriteLine("myClassDT Value property: {0}",
myClassDT.Value);
Console.WriteLine("myClassObj Value property: {0}",
myClassObj.Value);
Console.ReadLine();
}
}
and the output will be:
Pay attention, if I try to make operations on myClassObj.Value I’ll get an exception
because it is being set to null.
Summary
Lets sum up the post, the default keyword is very useful in order to initialize a
type parameter to it’s default value. The keyword is very helpful when you
need to reset a class with generic parameters such as a custom generic Enumerator
for example. I use the keyword a lot in converter classes which have generic parameters.
Invoking Generic Methods With Reflection
A few days ago
I answered a question
in the ASP.NET forums
concerning the issue
of invoking generic methods
with reflection.
In this post I’m going to explain that issue. If generics or reflection are new to you
you can read about them in the next MSDN links:
Getting started
You will need to create a new console application in order to make the example work.
In the following examples I’m going to use the following class:
public class MyClass
{
#region Methods
#region Regular Method
public string GetHelloString(string name)
{
return string.Format("Hello {0}", name);
}
#endregion
#region Generic Methods
public string GetString<M>(M item)
{
return string.Format("The item you sent is: {0}",
item.ToString());
}
public List<M> GetList<M>(params M[] items)
{
return items.ToList<M>();
}
#endregion
#endregion
}
The class holds one non generic method, GetHelloString, and two generic
methods - GetString that returns a string and GetString that returns a List of
generic type.
Invoking Methods With Reflection
The first step to learn is how to invoke a regular method with reflection.
In order to invoke a method with reflection you need to do the following
things:
- Get the type of the class that you want to invoke it’s method.
- Get the MethodInfo by the GetMethod method of Type.
- Invoke the method on the class with the relevant parameters.
The following code will do the trick:
Type t = typeof(MyClass);
MethodInfo mi = t.GetMethod("GetHelloString");
MyClass c = new MyClass();
object obj = mi.Invoke(c, new object[] { "Scott" });
Console.WriteLine(obj.ToString());
Console.ReadLine();
These lines of code will write to the console “Hello Scott”.
There are more ways to invoke methods with reflection but for the simplicity
of the example I use this method.
Invoking Generic Methods With reflection
After we saw how to invoke a regular method lets discuss the difference with
generic methods. Generic methods are as their name indicate generic. That
means that they get some type as a parameter which dictate the type to work
with inside the method. The reflection mechanism in the .Net framework helps
us to “inject” the type to the MethodInfo and to construct a generic
MethodInfo to be invoke. We use the MakeGenericMethod method of
MethodInfo in order to “inject” the type or types to the method.
The following method can show you a way to use the MakeGenericMethod
method:
// build the generic method with the relevant type
private static MethodInfo InjectTypeToGenericMethod<T>(string methodName)
{
// get the type and the method
Type t = typeof(MyClass);
MethodInfo mi = t.GetMethod(methodName);
// make the generic method with the type
Type[] argTypes = { typeof(T) };
MethodInfo result = mi.MakeGenericMethod(argTypes);
return result;
}
The example shows how to “inject” one type to the generic method.
You can do the “injection” with multiple types as well.
The other things to do in order to invoke the generic method are the same
as in regular methods case.
So, in order to invoke a generic method we need to do the following things:
- Get the type of the class that you want to invoke it’s method.
- Get the MethodInfo by the GetMethod method of Type.
- Build a set of type arguments to the generic method.
- Inject the type arguments to the generic method using the
MakeGenericMethod. - Invoke the method on the class with the relevant parameters.
The following code will give you the example I wrote from the start:
class Program
{
static void Main(string[] args)
{
#region Regular Method Invokation
Type t = typeof(MyClass);
MethodInfo mi = t.GetMethod("GetHelloString");
MyClass c = new MyClass();
object obj = mi.Invoke(c, new object[] { "Scott" });
Console.WriteLine(obj.ToString());
Console.ReadLine();
#endregion
#region Generic Methods Invokation
// build the generic method with the relevant type
MethodInfo miWithType = InjectTypeToGenericMethod<int>(
"GetString");
// invoke the generic method on a set of arguments
Object result = miWithType.Invoke(new MyClass(),
new object[] { 1 });
// write to output the result
Console.WriteLine(result);
Console.ReadLine();
// build the generic method with the relevant type
miWithType = InjectTypeToGenericMethod<string>(
"GetList");
// invoke the generic method on a set of arguments
string[] parameters = { "first", "second" };
result = miWithType.Invoke(new MyClass(),
new Object[] { parameters });
List<string> list = (List<string>)result;
// write to output the result
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.ReadLine();
#endregion
}
// build the generic method with the relevant type
private static MethodInfo InjectTypeToGenericMethod<T>(
string methodName)
{
// get the type and the method
Type t = typeof(MyClass);
MethodInfo mi = t.GetMethod(methodName);
// make the generic method with the type
Type[] argTypes = { typeof(T) };
MethodInfo result = mi.MakeGenericMethod(argTypes);
return result;
}
}
Summary
Lets sum up the post, I showed how to invoke a generic methods with
reflection. The only difference between invoking a regular method and
invoking a generic one is the “injection” of the type (or types) to the
method to be invoked. If you want to get more information in area of
generics and reflection you can read the following links:
ASP.NET Ajax Basics Session
Yesterday’s evening
I delivered a one hour
session about
ASP.NET Ajax
basics in SRL office.
The session included the following topics:
- Old fashion Ajax
- Introduction to ASP.NET Ajax
- Introduction to ASP.NET Ajax server controls
- Partial page rendering without UpdatePanel
You can download the slide decks here.
ASP.NET Ajax PageMethods
Introduction
During the making of a
ASP.NET Ajax lecture
which I’m scheduled to
do on this Monday,
I made some code
examples that include
a PageMethods example.
In this post I’ll explain what
are PageMethods and how to
use them in your ASP.NET application.
What are PageMethods?
ASP.NET Ajax extensions came with full support for script services. But sometimes
you don’t want to build a web service for a small piece of code or for basic functionality.
This is why the
PageMethods feature was created.
The
PageMethods feature enables the using of code-behind page methods on the client
side. The
PageMethods can only be added to the page itself so don’t try to add them to
user controls or custom controls - it won’t work.
How to Use PageMethods?
Basically, all you need to do in order to use a PageMethod is to decorate your page
method with the ScriptMethod and WebMethod attributes or only with WebMethod
attribute. Another restriction is that the method have to be static (or else it won’t work).
After the decoration of the method and also the existence of ScriptManager in your page
the method can be accessed from the client side using the PageMethods object.
PageMethods Example
Lets look at a simple example of how to use PageMethods.
In the page I have put the following method which returns a “Hello” string:
[ScriptMethod, WebMethod]
public static string GetLabelText()
{
return "Hello";
}
On the client side I have the following scripts which run that method:
<script type="text/javascript">
function InsertLabelData() {
PageMethods.GetLabelText(onSuccess, onFailure);
}
function onSuccess(result) {
var lbl = document.getElementById('lbl');
lbl.innerHTML = result;
}
function onFailure(error) {
alert(error);
}
InsertLabelData();
</script>
Also, in the page I hold a single Html label with a lbl id.
When the page is loading the “Hello” string will be attached to the label.
Pay attention to the PageMethods.GetLabelText call which calls the server side
method from the client side.
Summary
Lets sum up the post, I explained what are PageMethods.
I also showed a simple example of how to use a PageMethod.
The PageMethods feature is a very nice feature that you can use in the arsenal
of Ajax tools.
Back to Basics – Nullable Value Type
In the following posts
I’m going to describe
basic tools which every
developer needs to
know. In today’s post
I’m going to explain
what is a
Nullable value type and how
to use it in your code.
What are Nullable Value Types?
Nullable value types are an instances of the Nullable<T> struct.
The nullable value type can hold the all the values that its underlining value type holds
and a null value. The main reason for this type came from the use of databases.
In databases, data can be null (or if you prefer DBNull) and when the developers
retrieved the null value instead of a value type they expected a problem was raised.
What can you do with the database null value? should you use the default value for
the value type which can cause logical or business errors? should you use minimum
value to indicate null? Using the nullable value type helped to remove those questions.
The use of nullable value type can grantee a simple and clean way to hold a value type
even if it was returned from the database as null.
How to Use Nullable Value Types?
There are two ways to create a nullable value type:
- By using the ? type modifier -
- By using the Nullable<T> struct -
The nullable value type has two properties that can be used by the developer:
- HasValue – returns true if the nullable value type holds a value.
Returns false if the nullable value type holds null. - Value – returns the value that the nullable value types holds.
The property should always be accessed after you check if the nullable value
type has a value otherwise an InvalidOperationException exception will be thrown.
Another thing to know about a nullable value type is the GetValueOrDefault method.
That method returns the value inside of the nullable value type or the default value for the
underlining value type if the nullable value type is null.
An example of how to read a value from a nullable value type can look like:
int? someNumber = null;
if (someNumber.HasValue)
{
Console.WriteLine("the value is {0}",
someNumber.Value);
}
else
{
Console.WriteLine("There is no value");
}
The code above will produce the “There is no value” sentence in the console.
A restriction that needs to be followed is that you can’t have nested nullable value types.
The following code won’t even compile:
Nullable<Nullable<int>> someNumber;
Summary
The post introduced the concept of nullable value types.
You should use the type on members that are being returned from a database columns
which can hold null values. The use of the nullable value types is simple and straight forward.
Common Service Locator Library
introduction
Yesterday Glenn Block introduced
a Common Service Locator library
which is described as “a shared
interface that applications and
frameworks can reference in
order to leverage
IoC containers / service location
mechanisms without taking
hard dependencies”.
The idea for the library is based
on a post that Jeremy Miller
wrote of having a shared interface
across several IoC implementation.
The benefit of such an interface is
obvious - no dependency on a
particular IoC implementation.
The result was the birth of a new library – the common service locator library.
The Common Service Locator Library
The library currently holds the IServiceLocator interface which describe the
following contract:
public interface IServiceLocator : IServiceProvider
{
IEnumerable<TService> GetAllInstances<TService>();
IEnumerable<object> GetAllInstances(Type serviceType);
TService GetInstance<TService>();
TService GetInstance<TService>(string key);
object GetInstance(Type serviceType);
object GetInstance(Type serviceType, string key);
}
That contract was agreed by the following IoC containers frameworks creators:
Unity, Spring.Net, Castle Windsor, Structure Map and Ninject.
Other parts of the library include a static ServiceLocator class which
exposes the current container, a ServiceLocatorImplBase which is an abstract class
for building your own service locator and a ServiceLocatorProvider which is the
method to get the current container by the ServiceLocator static class.
Conclusion
The new library suppose to solve the problem of hard dependencies on
IoC containers / service location mechanisms. The library’s birth can demonstrate
the power of the community which see the need and create a library that is agreed
upon. I join the hope of Glenn Block that the new library will encourage products
and frameworks to start leveraging IoC / Service location frameworks.
Download the Common Service Locator library here.
I downloaded it yesterday…