<![CDATA[BlogML 2.0 Sample Blog]]> <![CDATA[admin]]> <![CDATA[Category 1]]> <![CDATA[Category 2]]> <![CDATA[Category 3]]> <![CDATA[CS Dev Guide: Send Emails]]> Any web application needs a way to send emails to different kinds of its users.  This capability is provided in Community Server from early versions.  Sending emails is one of easiest parts of Community Server development.

To send emails in Community Serve you have two options: using pre-defined email templates for some common situations or create an email manually.

First option is easy to implement.  Community Server has provided several default templates for your emails and you can add your own templates via resource files as well.  CommunityServer.Components.Emails namespace has many methods that get some parameters then create and send an email based on the template they have.

For example you can send a notification email to user to let him know his account is created.

private void SendEmail(string username, string password

    , string email)

{

    User user = new User();

    user.Username = username;

    user.Password = password;

    user.Email = email;

 

    Emails.UserCreate(user, password);

}

These methods add emails to queue automatically.  You can use Emails.SendQueuedEmails() method to send all queued emails to recipients.  This method takes three parameters: an integer value for failure interval, an integer value that specifies the maximum number of tries of sending process failed and a SiteSettings object.

private void SendQueuedEmails()

{

    Emails.SendQueuedEmails(5, 5,

        CSContext.Current.SiteSettings);

}

Second option to send emails is manual option.  This is useful when you want to send an email but it doesn't fit to Community Server pre-defined templates.

This option is very similar to sending emails in ASP.NET 1.x.  You need to create an instance of System.Web.Mail.MailMessage object, set its appropriate properties and add it to emails queue using Community Server APIs.  You know MailMessage object is obsolete in ASP.NET 2.0 but Community Server accepts old objects to be able to work under ASP.NET 1.1.  If you need more information about sending emails in ASP.NET 2.0, read my post about my SMTP component, Gopi, from here.

Here is a sample of how to create a new MailMessage and add it to emails queue.

private void SendEmailManually()

{

    MailMessage mail = new MailMessage();

    mail.From = "sender@server.com";

    mail.To = "receiver@server.com";

 

    mail.Priority = MailPriority.Normal;

    mail.BodyFormat = MailFormat.Text;

 

    mail.Body = "Long Live Community Server!";

    mail.Subject = "Test Email";

 

    EmailQueueProvider.Instance().QueueEmail(mail);

}

You saw I used EmailQueueProvider.Instance().QueueEmail() method to add my email to queue.  You can use EmailQueueProvider.Instance() object to deal with emails queue in Community Server.  For instance you can remove an email from emails queue by passing its Guid to EmailQueueProvider.Instance().DeleteQueuedEmail() method.

private void DealWithEmails()

{

    EmailQueueProvider.Instance()

        .DeleteQueuedEmail(Guid.NewGuid());       

}

Now playing: Modern Talking - You are not alone

]]>
<![CDATA[re: CS Dev Guide: Send Emails]]>
<![CDATA[Asynchronous command execution in .NET 2.0]]> Asynchronous execution of commands was one of great new features in ADO.NET 2.0.  The ability to execute commands that take long time to execute asynchronously is very helpful for developers.

The process of executing a command asynchronously is very simple and easy and I talk about it in this post.

Database

Before anything I create two stored procedures for SQL Server Northwind sample database to have a final stored procedure that takes time to execute.

First I create a stored procedure, SelectOrders, to select all orders from Orders table.

CREATE PROCEDURE dbo.SelectOrders

AS

    SELECT * FROM dbo.Orders

Now it's time to create a stored procedure which takes time to execute.  To write this stored procedure (MyStoredProcedure) I use new WAITFOR statement in SQL Server 2005 to make a five seconds delay in my stored procedure before executing SelectOrders procedure.

CREATE PROCEDURE dbo.MyStoredProcedure

AS

    WAITFOR DELAY '00:00:05'

    EXEC dbo.SelectOrders

Executing Command

There are three new methods for command objects in ADO.NET 2.0 which enable the ability to execute them asynchronously: BeginExecuteReader(), BeginExecuteNonQuery() and BeingExecuteXmlReader().

First one will be used for my sample.  What I want is to get the list of orders via MyStoredProcedure and show them in a Windows Form GridView control.

I create a SqlConnection and SqlCommand to fetch my data and pass my connection string to SqlConnection object.  Note that I add "asyn=true;" or "Asynchronous Processing=true;" to my connection string to enable asynchronous execution for my commands.  If I don't add one of these strings, will get an error with following description when I want to call BeginExecuteReader() method:

This command requires an asynchronous connection. Set "Asynchronous Processing=true" in the connection string.

After opening a connection to database, I call BeginExecuteReader() by passing a delegate of my main logic method for asynchronous execution, my SqlCommand object and a CommandBehaviour enumeration to close the connection to database after finishing the execution automatically.

private void Execute(string connectionstring)

{

    SqlConnection connection =

        new SqlConnection(connectionstring + "async=true;");

    SqlCommand command =

        new SqlCommand("MyStoredProcedure", connection);

    command.CommandType = CommandType.StoredProcedure;

 

    connection.Open();

 

    command.BeginExecuteReader(Asynchronous, command,

        CommandBehavior.CloseConnection);

}

Now I want to work on main logic for my command execution.  I use Asynchronous() method to do this.  This method takes one argument of IAsyncResult type.  I convert AsyncState property of this parameter to a SqlCommand then create a SqlDataReader and set it to appropriate value via the SqlCommand I created and finally load the data of this SqlDataReader to a DataTable and pass this DataTable through a delegate to another method.

private void Asynchronous(IAsyncResult asyncResult)

{

    SqlCommand command =

        asyncResult.AsyncState as SqlCommand;

 

    SqlDataReader reader;

    reader = command.EndExecuteReader(asyncResult);

 

    DataTable table = new DataTable();

    table.Load(reader);

    reader.Close();

 

    Invoke(new MyBindDelegate(MyBind), table);

}

Below is latest bits of my sample code to define a MyBindDelegate delegate and MyBind() method to bind my DataTable to GridView control.

private delegate void MyBindDelegate(DataTable table);

 

private void MyBind(DataTable table)

{

    GridView.DataSource = table;

}

But why I used this delegate to bind my data to GridView control?

.NET uses threading to accomplish asynchronous execution of commands so I used a delegate to move my Asynchronous() method to a new thread and I must do same and go back to first thread to bind my data to GridView at the end because GridView is located on another thread and can't be accessed within my new asynchronous thread.

Sample Windows Form application has been attached to this post.

]]>
<![CDATA[re: Asynchronous command execution in .NET 2.0]]> <![CDATA[re: Asynchronous command execution in .NET 2.0]]>
<![CDATA[FeedBurner APIs component for .NET 2.0]]> FeedBurner Some ages ago, when new FeedBurner site had been launched, I wanted to write a component for FeedBurner APIs in .NET but we know "Tomorrow is far away"!

Now FeedBurner is a famous site and many users use their services around the web.  When Scott Watermasysk opened a feature item for Community Server to add external feed functionality to Community Server 2.1, I commented for him about category feeds and he replied back we should assume this feature just for main feeds.  The necessity to have something to deal with FeedBurner APIs in some cases I've seen, bit me to write a component for .NET community.  Hope it helps developers to manipulate their feeds on FeedBurner on fly.

Overview

FeedBurner APIs are rich enough to help every developer to deal with account feeds easily.  FeedBurner is written with PHP and some APIs are a bit confusing specially when you must pass an XML file through query!!

However, I started writing this component to implement all FeedBurner APIs in .NET.  I've coded this component with C# 2.0 and used .NET 2.0 new features in many parts so it's not possible to apply it in .NET 1.x.  On the other hand as I used WebClient object to Get or Post to FeedBurner server, probably you can't use this component under medium trust level in ASP.NET 2.0.

Most of the code implementation is around XML manipulation and reading the source code can be a good practice for those who want to learn more about XML in .NET.  Talking about source code is out of scope of this blog post and I just show how can you use this component.  You can view the source code from download package to find more about it.

This component throws appropriate exceptions for any pre-defined exception on FeedBurner and returns exception messages from what FeedBurner provides.  List of some standard FeedBurner errors can be found here.

How to use

My component has self explanatory classes, objects and methods.  To make things clear I give some samples in this post.

To work with my component, you need to create an instance of User object and set its username and password via constructor or its properties.  Although it's not necessary to create a User object to use my component but I recommend it because it saves you from passing credentials via each and every operation.

// Create a user and set its credentials via constructor

User user = new User("Username", "Password");

 

// Create a user and set its properties

User user2 = new User();

user2.Username = "Username";

user2.Password = "Password";

All six operations that my component supports have several overloads to get a Feed object or feed ID and feed Uri together.

To add a new feed, you should provide all properties for a Feed object after creating a new instance but leave the ID as null (although if you set it, no problem will occur) and call one of Feeds.Add() method overloads.  Result of this method is a Feed object but it just has three properties available: ID, Title and Uri.

Feed feed = new Feed();

feed.Title =

    "Keyvan Nayyeri's posts about .NET";

feed.Uri = "KeyvanNayyeri/DotNet";

feed.Url =

    "http://nayyeri.net/rss.aspx?Tags=.NET&AndTags=1";

 

Service service = new Service("BrowserFriendly");

service.Parameters.Add("style", "clear");

service.Parameters.Add("message", null);

feed.Services.Add(service);

 

Feed result = Feeds.Add(user, feed);          

 

Console.WriteLine("ID: " + result.ID.ToString());

Console.WriteLine("Uri: " + result.Uri);

Console.WriteLine("Title: " + result.Title);

The process for  modifying a feed is very similar to add as feed by calling Feeds.Modify() method except that if you want to change the Uri of a feed, must provide its ID.

You can also simply get the list of defined feeds for a specific user by calling Feeds.Find() method.  This method will return a List<Feed> but don't forget that Feed objects won't have all properties available as same as the result of Feeds.Add() or Feeds.Modify() methods.

foreach (Feed feed in Feeds.Find(user))

{

    Console.WriteLine("ID: " + feed.ID.ToString());

    Console.WriteLine("Uri: " + feed.Uri);

    Console.WriteLine("Title: " + feed.Title);

    Console.WriteLine();

}

Also you can get all details of a specific feed by passing its ID, Uri or a Feed object that contains these properties to Feeds.Get() method.  This method returns a Feed object with all properties available.

Feed feed = Feeds.Get(user, "KeyvanNayyeri/DotNet");

 

Console.WriteLine("ID: " + feed.ID.ToString());

Console.WriteLine("Uri: " + feed.Uri);

Console.WriteLine("Title: " + feed.Title);

Console.WriteLine("Url: " + feed.Url);

 

if (feed.Services.Count > 0)

{

    foreach (Service service in feed.Services)

    {

        Console.WriteLine("  (Service) Name: "

            + service.Name);

        if (service.Parameters.Count > 0)

        {

            foreach (KeyValuePair<string, string>

                parameter in service.Parameters)

                Console.WriteLine

                    ("      (Parameter) Name: " +

                    parameter.Key + " Value: " +

                    parameter.Value);

        }

    }

}

To delete or resync a feed, you can call Feeds.Delete() or Feeds.Resync() methods.  They don't return anything.

Feed feed = new Feed();

feed.Uri = "KeyvanNayyeri/DotNet";

 

Feeds.Delete(user, feed);

Console.WriteLine("Specific feed has been removed.");

Note that some operations may take time to be shown on FeedBurner website.

I put a sample Windows Console application in download package to show how to use my component APIs.  This is the output of my sample Console Application:

Output

And this is what my feed looks like after adding it via APIs:

Adding a feed to FeedBurner via APIs

Download

FeedBurner APIs component for .NET 2.0 is available here with source code.

]]>
<![CDATA[Give me a Trackback]]> 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
<![CDATA[Write Atomic Transactions in .NET]]> ACID theory is familiar to many programmers.  Those who work on Operating System design concern about it everyday.

Most important aspect of ACID is Atomic transactions.  But what's an Atomic transaction?  The best way to describe it is a famous common example: a bank accounting system.

Consider that bank A and bank B want to interact with someone's account on same time.  Both banks want to withdraw from account and account has $10.00 currently.  If bank A gets $7.00 and on same time bank B tries to get $5.00, what will happen?  One of annoying situations is when both banks start transactions on same time.  As they think there is $10.00 available in account, start their interaction but one of them finishes earlier and second one won't have enough money to get.

This scenario is common for computer systems and you can see it many times in memory management, IO operations and database interactions.

Atomic transactions are a way to avoid this problem.  They simply lock on a transaction and don't let any other transaction to interact with same resource.  If anything fails during the Atomic transaction, everything will return to the state before transaction's start.

In this post I want to give a sample to write an Atomic transaction in .NET and test to see it in action.  In this example I create a simple accounting system then in an Atomic transaction add values to an account and place a case to throw exceptions for some arguments to see what will happen if in an Atomic transaction something goes wrong.

Database

To write my sample, I create a simple table with three columns:

  • id (int)
  • Name (navarchar)
  • Value (int)

Consider each row is an account in a bank.  Now I create a stored procedure to Add a value to a specific account.

/* Add a value to an account */

CREATE PROCEDURE dbo.AddToAccount   

    (

    @id int,

    @value int

    )

AS

    UPDATE dbo.MyTable

    SET Value = (Value + @value)

    WHERE id= @id

To finish preparing my database, I add a sample account to my table.

Write Atomic Transaction

Writing an Atomic transaction is easy in .NET.  First I create a Class Library project then create a MyTransaction class to implement my logic in it.

MyTransaction has a Transfer() method that gets the identifier of an account, the integer value to add to that account and database connection string.  This method tries to add the value to the specific account in an Atomic transaction.  I'll produce an exception in this transaction to show what will happen if value wasn't valid according to sample constraints.  I throw this exception after successfully adding a value to an account.

What I need to build my Class Library project is adding a reference to System.EnterpriseServices, inheriting my class from ServicesComponent base class and call ContextUtil.SetComplete() method at the end of my Atomic transaction.

So initially my class looks like this:

using System;

using System.Collections.Generic;

using System.Text;

using System.EnterpriseServices;

using System.Data;

using System.Data.SqlClient;

 

namespace AtomicTransactions

{

    public class MyTransaction  : ServicedComponent

    {

Note that I can't provide a public default constructor for this class because later I can't register it as COM+ component service.

Below is the logic for Transaction() method.  After adding the value to database, I check a constraint and throw an exception if value wasn't between 0 and 10.  This is a sample constraint to show the result in test stages.

public void Transfer(int accountID, int value,

    string connectionString)

{

    try

    {

        AddValue(accountID, value, connectionString);

        if ((value < 0) || (value > 10))

            throw new

                ArgumentException("Value isn't valid!");

 

        // Atomic transaction ends here

        ContextUtil.SetComplete();

    }

    catch (Exception ex)

    {

        throw new Exception

            ("An error occurred in our transaction");

    }

}

Simple logic for AddValue() method is:

private void AddValue(int id, int value,

    string connectionString)

{

    try

    {

        using (SqlConnection connection =

            new SqlConnection(connectionString))

        {

            SqlCommand command =

                new SqlCommand("AddToAccount", connection);

            command.CommandType = CommandType.StoredProcedure;

 

            command.Parameters.AddWithValue("id", id);

            command.Parameters.AddWithValue("value", value);

 

            connection.Open();

            command.ExecuteNonQuery();

        }

    }

    catch (Exception ex)

    {

        throw ex;

    }

}

Register Component Service

After creating my simple class, I need to set its transaction behavior.  I can do it via an attribute in my code (which I'll do) or via Component Services MMC snap-in.

There are several options for TransactionOption enumerator but I choose RequiresNew to create a new transaction with my component.  You can find more about other options on MSDN.  I also set my ApplicationAccessControl for assembly attribute to true.

namespace AtomicTransactions

{   

    [assembly: ApplicationAccessControl(true)]

    [Transaction(TransactionOption.RequiresNew)]

    public class MyTransaction  : ServicedComponent

    {

Now I build my assembly but must make it COM Visible also create a strong name for this assembly.  Finally I compile my Class Library and register it as a Component Service via Visual Studio command prompt:

C:\Visual Studio 2005\Projects\AtomicTransactions\AtomicTransactions\bin\Debug>regsvcs AtomicTransactions.dll

Test Application

To test my Atomic transaction, I write a Windows Form application that uses my Class Library.  It simply gets the ID and value for an account and pass them to Transfer() method.  This Windows Form application must have a reference to System.EnterpriseServices as well.

This is the event handler for my btnTransfer button:

private void btnTransfer_Click(object sender, EventArgs e)

{

    MyTransaction transaction = new MyTransaction();

 

    try

    {

        transaction.Transfer(

            Convert.ToInt32(txtID.Text),

            Convert.ToInt32(txtValue.Text),

            "Data Source=KEYVANNAYYERI;Initial "

            + "Catalog=AtomicTransaction;"

            + "Integrated Security=True;Pooling=False");

    }

    catch (Exception ex)

    {

        throw ex;

    }

 

    MessageBox.Show("Finished!");

}

Test Results

Now I run my application to test my component.  But before doing this, I check current state of my database and Component Services Transaction Statistics:

 

I run my application and give valid values (1 for ID and 5 for value) to it.  Result is normal in database but look at Transaction Statistics.

 

Now I provide an invalid value, say 11, for my application to throw an exception in my Atomic transaction.  Nothing will change in database but Transaction Statistics show an Aborted transaction.  In normal form database must updated because value must be added to database before exception.

 

Note that an Atomic transaction can be applied to many kinds of transactions not only database transactions.  For instance you can apply it to files.

Now playing: Pink Floyd - Lost For Words

]]>