Category: How-to

Getting Shamsi Date From Gregorian Date

Shamsi calendar, also known as Iranian, Persian or Jalāli Calendar is a solar calendar which is still in use in Iran and Afghanistan. If you need to convert date from Gregorian to Jalali calendar and vice versa, here is a handy Iraniar Calendar Converter. On the other hand, if you need to convert the dates in your own application, check the SQL Server function in this article.

So, let’s start. The main function is getShamsiDate, which receives Gregorian date and returns Shamsi date. I won’t get into astronomical explanations, but you can check this link to get more insight.

CREATE FUNCTION [dbo].[getShamsiDate] (@Date smalldatetime)
RETURNS nvarchar(20)
AS
BEGIN
    


DECLARE @Year int,
		@Month int,
		@Day int, 
		@ToEid int, 
		@Elapsed int, 
		@counter int,
		@retVal nvarchar(20),
		@nDay nvarchar(2),
		@nMonth nvarchar(2)

DECLARE @LeapSumOfDays TABLE 
(
	ID int,
	Val int
)

DECLARE @NonLeapSumOfDays TABLE(
	ID int,
	Val int
)

INSERT INTO @LeapSumOfDays
SELECT ROW_NUMBER() OVER (ORDER BY CAST([value] AS int)) AS ID, CAST([value] AS int) AS Val
FROM dbo.SplitStringList ('0,31,62,93,124,155,186,216,246,276,306,336,366')


INSERT INTO @NonLeapSumOfDays
SELECT ROW_NUMBER() OVER (ORDER BY CAST([value] AS int)) AS ID, CAST([value] AS int) AS Val
FROM dbo.SplitStringList ('0,31,62,93,124,155,186,216,246,276,306,336,365')


SET @Year = YEAR(@Date) - 621

IF dbo.isLeapYearShamsi(@Year - 1) = 1 AND dbo.isLeapYear(YEAR(@Date)) = 1
	SET @ToEid = 80
ELSE
	SET @ToEid = 79

IF DATEPART(dy,@Date) <= @ToEid
BEGIN
	SET @Year = @Year-1
	SET @Elapsed = 286 + DATEPART(dy,@Date)
	IF dbo.isLeapYearShamsi(@Year) = 1 AND dbo.isLeapYear(YEAR(@Date)) = 0
		SET @Elapsed = @Elapsed + 1
END
ELSE
	SET @Elapsed = DATEPART(dy,@Date) - @ToEid
     

IF dbo.isLeapYearShamsi(@Year) = 1
BEGIN
	SELECT TOP 1 @counter = ID FROM @NonLeapSumOfDays WHERE val >= @Elapsed
	SET @Month = @counter - 1
	SELECT @Day = @Elapsed - val FROM @NonLeapSumOfDays WHERE ID = @counter - 1
END
ELSE
BEGIN
	SELECT TOP 1 @counter = ID FROM @LeapSumOfDays WHERE val >= @Elapsed
	SET @Month = @counter - 1
	SELECT @Day = @Elapsed - val FROM @LeapSumOfDays WHERE ID = @counter - 1
END


IF LEN(@Day) = 1 
	SET @nDay = '0' + CAST(@Day AS nvarchar(2))
ELSE
	SET @nDay = CAST(@Day AS nvarchar(2))

IF LEN(@Month) = 1 
	SET @nMonth = '0' + CAST(@Month AS nvarchar(2))
ELSE
	SET @nMonth =  CAST(@Month AS nvarchar(2))


SET @retVal = CAST(@Year AS nvarchar(4)) + '/' + @nMonth + '/' + @nDay   

RETURN @retVal

END

There are couple of helper functions used by the main function, and their code follows.

SplitStringList function gets comma separated list of strings and returns one column table, having given string values as table records. It is useful in cases you want to get some values into the table without writing INSERT INTO statement n times.

CREATE FUNCTION [dbo].[SplitStringList]
(
	@StrList nvarchar(4000)
)
RETURNS 
@ParsedList table
(
	value nvarchar(500) COLLATE Arabic_CI_AS
)
AS
BEGIN
	DECLARE @Value nvarchar(500), @Pos int

	SET @StrList = LTRIM(RTRIM(@StrList))+ ','
	SET @Pos = CHARINDEX(',', @StrList, 1)

	IF REPLACE(@StrList, ',', '') <> ''
	BEGIN
		WHILE @Pos > 0
		BEGIN
			SET @Value = LTRIM(RTRIM(LEFT(@StrList, @Pos - 1)))
			IF @Value <> ''
			BEGIN
				INSERT INTO @ParsedList (value) 
				VALUES (CAST(@Value AS nvarchar)) --Use Appropriate conversion
			END
			SET @StrList = RIGHT(@StrList, LEN(@StrList) - @Pos)
			SET @Pos = CHARINDEX(',', @StrList, 1)

		END
	END	
	RETURN
END

Function isLeapYear checks if given year is (Gregorian)leap year. The code is really simple – it is checked if February 29th exists in the given year.

CREATE FUNCTION [dbo].[isLeapYear] (@Year SMALLINT)
RETURNS BIT
AS
BEGIN
    DECLARE @leapDate SMALLDATETIME
    DECLARE @checkDay TINYINT
 
    SET @leapDate = CONVERT(VARCHAR(4), @Year) + '0228'
    SET @checkDay = DATEPART(d, DATEADD(d, 1, @leapDate))
    IF (@checkDay = 29)
        RETURN 1

    RETURN 0  
END

isLeapYearShamsi checks if the given year is Shamsi leap year. Once more, you can check the algorithm, if you want to find more.

CREATE FUNCTION [dbo].[isLeapYearShamsi]( @Year int )
RETURNS bit
AS

BEGIN 

	DECLARE @B int,
			@isLeap bit

	SET @B = @Year % 33

	IF @B IN (1, 5, 9, 13, 17, 22, 26, 30)
		SET @isLeap = 1
	ELSE
		SET @isLeap = 0
	
	RETURN @isLeap

END

So, to get Shamsi representation of, for example, today’s date, call the function:

SELECT dbo.getShamsiDate('20130430')

And the result is:

1392/02/10

Hope this helps.

Google Chart in ASP.NET Application – Quick & Easy (Part 2)

In one of my previous posts, I’ve explained how to easily integrate Google charts with ASP.NET application. Here is one more example related to that subject, having some (for me) very cool data filtering features.
Note: For the sake of simplicity, I’ve removed jQuery and AJAX code, so all the processing and chart drawing happens on page load.

Here is C# code:

using System;
using System.Collections.Generic;
using System.Web.Services;
using System.Web.Script.Serialization;

public partial class Default2 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            List<DataItem> dataList = new List<DataItem>();

            dataList.Add(new DataItem("Column 1", 100, "Male", 25));
            dataList.Add(new DataItem("Column 2", 150, "Male", 37));
            dataList.Add(new DataItem("Column 3", 250, "Female", 25));
            dataList.Add(new DataItem("Column 4", 400, "Female", 35));
            dataList.Add(new DataItem("Column 5", 450, "Male", 35));
            dataList.Add(new DataItem("Column 6", 460, "Female", 26));
            dataList.Add(new DataItem("Column 7", 470, "Female", 30));
            dataList.Add(new DataItem("Column 8", 500, "Male", 31));
            dataList.Add(new DataItem("Column 9", 550, "Male", 30));
            dataList.Add(new DataItem("Column 10", 600, "Female", 33));

            JavaScriptSerializer jss = new JavaScriptSerializer();
                        
            ClientScript.RegisterStartupScript(this.GetType(), "TestInitPageScript",
                string.Format("<script type=\"text/javascript\">drawVisualization({0},'{1}','{2}','{3}');</script>",
                jss.Serialize(dataList),
                "Text Example",
                "Name,Value,Gender,Age",
                "--Choose--"));
        }
    }   
}

public class DataItem
{
    #region Internal Members

    private string _ColumnName = "";
    private double _Value1 = 0;
    private string _Value2 = null;
    private int _Value3 = 0;     

    #endregion

    #region Public Properties

    public string ColumnName
    {
        get { return _ColumnName; }
        set { _ColumnName = value; }
    }
    public double Value1
    {
        get { return _Value1; }
        set { _Value1 = value; }
    }
    public string Value2
    {
        get { return _Value2; }
        set { _Value2 = value; }
    }
    public int Value3
    {
        get { return _Value3; }
        set { _Value3 = value; }
    }

    #endregion

    #region Constructors

    public DataItem(string columnName, double value1, string value2, int value3)
    {
        _ColumnName = columnName;
        _Value1 = value1;
        _Value2 = value2;
        _Value3 = value3;
    }

    #endregion
}

ColumnName is the name of a specific item, for example person’s name, or name of the product our company makes. Values(Value1, Value2, Value3) closely represent the item, e.g. person’s age, salary, number of products sold etc. In a project I’m currently working on, I use Google chart to show which products are popular among different age/sex groups.

On page load, the data is compiled into JSON string using JavaScriptSerializer(line 29), and then the script is registered using ClientScript.RegisterStartupScript. This way, the script will be executed when loaded on client. Some additional information is also provided, including chart title(line 30), data column names(line 31), neutral value text in the category dropdown list(line 32).

ASPX & javaScript:


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Google Charts Example</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>
    <script type="text/javascript" src="//www.google.com/jsapi"></script>
    <script type="text/javascript">
        google.load('visualization', '1.1', { packages: ['controls'] });
    </script>
    <script type="text/javascript">  

        function drawVisualization(dataValues, chartTitle, columnNames, categoryCaption) {
            if (dataValues.length < 1)
                return;

            var data = new google.visualization.DataTable();
            data.addColumn('string', columnNames.split(',')[0]);
            data.addColumn('number', columnNames.split(',')[1]);
            data.addColumn('string', columnNames.split(',')[2]);
            data.addColumn('number', columnNames.split(',')[3]);

            for (var i = 0; i < dataValues.length; i++) {
                data.addRow([dataValues[i].ColumnName, dataValues[i].Value1, dataValues[i].Value2, dataValues[i].Value3]);
            }

            // Define a category picker control for the Gender column
            var categoryPicker = new google.visualization.ControlWrapper({
                'controlType': 'CategoryFilter',
                'containerId': 'CategoryPickerContainer',
                'options': {
                    'filterColumnLabel': columnNames.split(',')[2],
                    'ui': {
                        'labelStacking': 'horizontal',
                        'allowTyping': false,
                        'allowMultiple': false,
                        'caption': categoryCaption,
                        'label': columnNames.split(',')[2]
                    }
                }
            });

            // Define a Pie chart
            var pie = new google.visualization.ChartWrapper({
                'chartType': 'PieChart',
                'containerId': 'PieChartContainer',
                'options': {
                    'width': 600,
                    'height': 350,
                    'legend': 'right',
                    'title': chartTitle,
                    'chartArea': { 'left': 50, 'top': 15, 'right': 0, 'bottom': 0 },
                    'pieSliceText': 'label',
                    'tooltip': { 'text': 'percentage' }
                },
                'view': { 'columns': [0, 1] }
            });

            // Define a table
            var table = new google.visualization.ChartWrapper({
                'chartType': 'Table',
                'containerId': 'TableContainer',
                'options': {
                    'width': '300px'
                }
            });

            // Define a slider control for the Age column.
            var slider = new google.visualization.ControlWrapper({
                'controlType': 'NumberRangeFilter',
                'containerId': 'SliderContainer',
                'options': {
                    'filterColumnLabel': columnNames.split(',')[3],
                    'ui': { 'labelStacking': 'horizontal' }
                }
            });

            new google.visualization.Dashboard(document.getElementById('PieChartExample')).bind([categoryPicker,slider], [pie, table]).draw(data);
        }
        
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <div id="PieChartExample">
            <table>
                <tr style='vertical-align: top'>
                    <td>
                        <div id="CategoryPickerContainer"></div>
                        <div id="SliderContainer"></div>
                    </td>
                </tr>
                <tr>
                    <td >
                        <div style="float: left;" id="PieChartContainer"></div>            
                        <div style="float: left;" id="TableContainer"></div>    
                    </td>
                </tr>
            </table>
        </div>
    </form>
</body>
</html>

Notice that controls package is used here, instead of corechart, that was used in previous example.
drawVisualization is the function where all the magic happens. :)
Two classes are important in this example: ControlWrapper and ChartWrapper. ControlWrapper is a wrapper around a JSON representation of a configured control instance – in this case, the “filtering” controls: CategoryFilter (for choosing sex) and NumberRangeFilter (slider, for defining age range).
Finally, new instance od Dashboard class is created, and its bind method invoked.
(I strongly recommend experimenting in Code Playground, it’s very useful!)

As a result, we get something like this:

GoogleChartsExample

When an item is selected in gender drop down list, or the age range is changed, both pie chart and table values are filtered. It all happens on client and it’s very fast. Also, the design is lite and very Google-like, so users really like it!

Google Charts, ASP.NET, jQuery, AJAX – Quick & Easy

What to do when there is a requirement in your application to make a graphical representation of some statistical data? Well, there are a lot of controls you can use, depending on your needs.

In this post, I’ll show you how to quickly and easily introduce Google charts to your ASP.NET application, getting something like this as a result:

GoogleCharts

Also, I’ll be using jQuery and AJAX for fetching data, although they are not necessary, of course.

First of all, let’s define the data we want to show in the chart. This is really basic example, so my class looks something like this:

public class Data
{
    public string ColumnName = "";
    public int Value = 0;

    public Data(string columnName, int value)
    {
        ColumnName = columnName;
        Value = value;
    }
}

This is really all we need to display the chart: data caption and value.

Next, here is the method for getting the data from the server:

[WebMethod]
public static List<Data> GetData()
{
    List<Data> dataList = new List<Data>();

    dataList.Add(new Data("Column 1", 100));
    dataList.Add(new Data("Column 2", 200));
    dataList.Add(new Data("Column 3", 300));
    dataList.Add(new Data("Column 4", 400));

    return dataList;       
}

This is a web method, since I’ll make AJAX call from the client to get JSON object containing data to be displayed. Bear in mind that there is no need for serializing to JSON string – just make the method return the object, or in this case – list of objects.

As for the client script, let’s take a look at the AJAX method for fetching the data from the server:

  
$(document).ready(function() {
    $.ajax({
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        url: 'Default.aspx/GetData',
        data: '{}',
        success:
            function(response) {
                drawVisualization(response.d);
            }
        });
    })
})

Nothing new here – just defining which server method to invoke and what to do when the data is received.

We need to define where the chart will be drawn:

  
<div id="visualization" style="width: 600px; height: 400px;"></div>

To display the Google chart, we must load three libraries: the Google JSAPI API, the Google Visualization library and the library for the chart itself.

First of all, Google JSAPI API:

  
<script type="text/javascript" src="//www.google.com/jsapi"></script>

Next, load the Google Visualization and chart libraries:

  
<script type="text/javascript">
    google.load('visualization', '1', { packages: ['corechart'] });
</script>

Note:
- ‘visualization’ – loads the google.visualization library, which defines all the core utility classes and functions.
- ’1′ – visualization version to load (’1′ is current production version).
- { packages: ['corechart'] } – here we can define the list of all Google chart libraries needed. The ‘corechart’ library contains most basic charts, including pie chart, which we use in this example.

Finally, here is the function that will actually draw the chart:

  
function drawVisualization(dataValues) {
    var data = new google.visualization.DataTable();
    data.addColumn('string', 'Column Name');
    data.addColumn('number', 'Column Value');

    for (var i = 0; i < dataValues.length; i++) {
        data.addRow([dataValues[i].ColumnName, dataValues[i].Value]);
    }

    new google.visualization.PieChart(document.getElementById('visualization')).
        draw(data, { title: "Google Charts Example" });          
}

The code is self-explanatory: the DataTable is initialized, the columns are added to it, and then the data is entered. Finally, new PieChart is initialized and its draw method is invoked.

The whole example:
cs:

 
using System;
using System.Collections.Generic;
using System.Web.Services;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        
    }

    [WebMethod]
    public static List<Data> GetData()
    {
        List<Data> dataList = new List<Data>();

        dataList.Add(new Data("Column 1", 100));
        dataList.Add(new Data("Column 2", 200));
        dataList.Add(new Data("Column 3", 300));
        dataList.Add(new Data("Column 4", 400));

        return dataList;       
    }
}

public class Data
{
    public string ColumnName = "";
    public int Value = 0;

    public Data(string columnName, int value)
    {
        ColumnName = columnName;
        Value = value;
    }
}

aspx:

 
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>
    <script type="text/javascript" src="//www.google.com/jsapi"></script>
    <script type="text/javascript">
        google.load('visualization', '1', { packages: ['corechart'] });
    </script>
    <script type="text/javascript">
        $(document).ready(function() {
            $.ajax({
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json',
                url: 'Default.aspx/GetData',
                data: '{}',
                success:
                    function(response) {
                        drawVisualization(response.d);
                    }

            });
        })

        function drawVisualization(dataValues) {
            var data = new google.visualization.DataTable();
            data.addColumn('string', 'Column Name');
            data.addColumn('number', 'Column Value');

            for (var i = 0; i < dataValues.length; i++) {
                data.addRow([dataValues[i].ColumnName, dataValues[i].Value]);
            }

            new google.visualization.PieChart(document.getElementById('visualization')).
                draw(data, { title: "Google Charts Example" });          
        }

        
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <div id="visualization" style="width: 600px; height: 400px;"></div>
    </form>
</body>
</html>

Google charts are easy to implement and highly customizable. I will be writing more about different types of charts and how to customize them, in my future posts.

Entity Framework and Setting Primary Keys on Views

When using Entity Framework, it’s important to set primary keys on table and view objects in SQL Server. If you skip this step, EF will define composite primary key made of all non-nullable columns belonging to the object. That way you can end up with some strange results and spend hours finding out what went wrong (trust me, I’ve been there). So, first of all: set primary keys in the database!

OK, with tables, that’s just it – set the primary keys and you’re good to go. Now, what about views? In case of views having primary keys defined in underlying tables, that should also be enough. But what to do if there are simply no columns to use as PK? Well, this can be achieved by using ROW_NUMBER() function. Here is the actual example from one of my projects:

CREATE VIEW [dbo].[vw_Action]
AS

SELECT *, ROW_NUMBER() OVER(ORDER BY ActionDate DESC) AS RowID
FROM
(
   SELECT User1ID AS UserID, NULL AS FriendID, Object1ID, Object2ID, ActionType, ActionDate
   FROM vw_ActionBase
   WHERE ActionType = 1
   GROUP BY User1ID, Object1ID, Object2ID, ActionType, ActionDate

   UNION

   SELECT User1ID AS UserID, NULL AS FriendID, Object1ID, Object2ID, ActionType, ActionDate
   FROM vw_ActionBase
   WHERE ActionType = 2
   GROUP BY User1ID, Object1ID, Object2ID, ActionType, ActionDate

   UNION

   SELECT User1ID AS UserID, NULL AS FriendID, Object1ID, Object2ID, ActionType, ActionDate
   FROM vw_ActionBase
   WHERE ActionType = 3
   GROUP BY User1ID, Object1ID, Object2ID, ActionType, Object1ID, ActionDate

)x

Now, here’s something that made me confused: I’ve expected RowID column to be defined as NOT NULL. I thought that if there was a row, it should have a number. But I was wrong, as you can see:

vw_Action

So, the column I wanted to use as primary key (RowID) was nullable, and the other (ActionType) that shouldn’t be primary key was in fact the only PK candidate for Entity Framework. As expected, when I performed Update From Database option, the result in edmx file was:

<EntityType Name="vw_Action">
    <Key>
        <PropertyRef Name="ActionType" />
    </Key>
    <Property Name="UserID" Type="int" />
    <Property Name="FriendID" Type="int" />
    <Property Name="Object1ID" Type="int" />
    <Property Name="Object2ID" Type="int" />
    <Property Name="ActionType" Type="int" Nullable="false" />
    <Property Name="ActionDate" Type="datetime" />
    <Property Name="RowID" Type="bigint" />
</EntityType>

If you get tempted to set keys manually in EF, don’t! You will run into errors similar to these:

EFModel.Model.msl(266,10) : error 3002: Problem in Mapping Fragment starting at line 266: Potential runtime violation of table vw_Action’s keys (vw_Action.ActionType): Columns (vw_Action.ActionType) are mapped to EntitySet vw_Action’s properties (vw_Action.ActionType) on the conceptual side but they do not form the EntitySet’s key properties (vw_Action.RowID).\r\n\r\nEFModel.Model.msl(266,10) : error 3003: Problem in Mapping Fragment starting at line 266: All the key properties (vw_Action.RowID) of the EntitySet vw_Action must be mapped to all the key properties (vw_Action.ActionType) of table vw_Action.\r\n

Also, changes will be lost on next update from database.

So, what’s the solution?

The solution is to explicitly define columns you want for PK in SQL server as non-nullable using ISNULL function. Similary, if there is an actual non-nullable column that you want excluded from the PK, use NULLIF, like this:

CREATE VIEW [dbo].[vw_Action]
AS

SELECT UserID, FriendID, Object1ID, Object2ID, NULLIF(ActionType, -1) AS ActionType,
ActionDate, ISNULL(ROW_NUMBER() OVER(ORDER BY ActionDate DESC), -1) AS RowID
FROM
(
    ...
)x

What will be the result of this code? SQL Server will understand that RowID could never be NULL – it will be set to -1 in case of null value (although this will never happen in my case anyway). On the other hand, ActionType column, that can actually never be NULL since it has values 1, 2 and 3, will be interpreted as nullable.

Here is how it all looks in the end:

vw_Action_1

After updating model from the database, the right column will be set as entity primary key in edmx file.

——— EDIT ———
Related post: A Quick Note on COALESCE and ISNULL

GMail, GoDaddy and Me Giving Up

In one of my previous posts, I have explained how to send mail through GMail using ASP.NET C#. Well, for me it all looked good until I wanted to upload the application to GoDaddy server. I have Windows hosting plan there, and wanted to set the app for test purposes on it.

I’ve uploaded to GoDaddy both the application and the Web service I use to send mail. In service Web.config, the settings were something like this:

<system.net>
    <mailSettings>
        <smtp from="TEST" >
            <network host="smtp.gmail.com" port="587" userName="someuser@gmail.com" password="somepassword" defaultCredentials="false" />
        </smtp>
    </mailSettings>
</system.net>

But, I received the error message:
System.Web.Services.Protocols.SoapException: Server was unable to process request. —> System.Configuration.ConfigurationErrorsException: Insufficient permissions for setting the configuration property ‘port’.

So, I’ve Googled the exception in ‘GoDaddy context’ and learned that you can’t send e-mail through Gmail account using GoDaddy servers. Apparently, they want to prevent using their servers for sending spam. But, it wasn’t all so easy to understand it since I found some (for me) pretty vague answers. At first, I was convinced that you CAN send e-mails through GMail, because I found posts explaining how – but it just didn’t work for me! I even spoke to some friends who were sure this was working some time ago, when they needed to set up mail sending on GoDaddy. I tried several different configurations, but kept getting exceptions, different from the one I mentioned here.

Hours and hours later, I finally decided to give up on trying to use GMail. After all, this was all supposed to be just for testing. (When I make release version, I will buy ‘proper’ hosting.) I’ve created new e-mail account on my domain (also hosted on GoDaddy). I found out that, if you have the shared hosting plan, you must use their relay-hosting.secureserver.net on port 25. It’s important to say that this server doesn’t support SSL. So, in C# code, instead of

mailClient.EnableSsl = true;

it should be:

mailClient.EnableSsl = false;

In Web.config, the configuration will now be:

<system.net>
    <mailSettings>
      <smtp from="TEST" >
        <network host="relay-hosting.secureserver.net" port="25" userName="someuser@yourdomain" password="somepassword" defaultCredentials="false" />
      </smtp>
    </mailSettings>
  </system.net>

Make sure to use the e-mail from your domain as sender, not the GMail! Also, set both username and password! I’ve found couple of comments from GoDaddy support team, explicitely saying you DON’T have to use credentials when configuring relay-hosting.secureserver.net. However, I couldn’t make it all work until I’ve set both username and password.

I must say that it took me a while to figure this out. I haven’t even thought of contacting GoDaddy support since I’ve been doing it a lot in last couple of months on various subjects and I’m pretty much tired of their responses ‘within 12 hours’. This is my first time trying to send mail from their server and it was all new to me, but I’ve discovered that this is a well known problem.

Hope this post will help people not to waste their precious time on a configuration issue like this.