Author: spairous

  • System.Net.Http.HttpRequestException: ‘An error occurred while sending the request.’

    Sudenly I received a strange error in a production website while trying to do a simple http request that was working fine the previous day. The error that was thrown was the following:

       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at CL.Helper.Api.MailChimpController.<AddUserToList>d__5.MoveNext() 

    This line was trying to make a call to an API endpoint at MailChimp that added an email to a list. Testing the code locally and using unit testing worked perfectly and without any issue.

    After debuging the error locally I found out that the details where hiding in the inner exception that were not exposed in the logs and the issue had to do with the server not being able to create a secure channel with the client. The message was: The request was aborted: Could not create SSL/TLS secure channel.

       at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
       at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)

    So investigating a bit I realised that most likely MailChimp stopped supporting older protocols and now only allowed TLS12. Good news there was a simple fix to that:

       ServicePointManager.Expect100Continue = true;
       ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

    Simply adding the two lines above before the request fixed the issue.

  • IIS 7+ Register and Execute ISAPI 32bit/64bit DLL

    I resently had to move an old project that I had from an Windows 2003 Server to a brand new Windows 2012 Server R2. The concern that I wad was with some ISAPI dlls that I was using to sync data from an SQL Server to some Windows Mobile devices using SQL Server CE. I was using merge replication to sync the devices and was doing it through the web (using sqlcesa30.dll).

    First I tried to simply copy the DLLs but that didn’t work. Then I decided to try to run the setup but that didn’t work either. That is when I started to play around with IIS to find out what is going on since I was getting that frustrating Internal Server Error (500) which gives you no clue as of what is going on!

    Here are the steps you need to take in order to register and execute a DLL on IIS 7+.

    1. Create a folder to put the the executable (this is not required but changes might affect other services running).
    2. Make that folder an application and create a new application pool for it.
    3. Open Internet Information Services (IIS) Manager and click on the server on the tree. Then click ISAPI and CGI Restrictions. There you need to add the executable you need to run and click Allowed.
    4. Then find and click the application folder on the tree and then go to Handler Mappings and then Edit Feature Permissions (which is on the right pane). There enable the Execute setting.
    5. Finally go to the application pool, select the application pool you crated and in advanced settings set Enable 32-Bit Applications to true. (this is only required if you are trying to run an x86 DLL but it does not do any harm to enable it either way)

    Hope this helps you register and run any DLL so that is at least executed and you don’t get that annoying Internal Server Error 500 that you have no clue as of what is going on.

  • Google Analytics Measurement Protocol – Track Events C#

    Recently I had a project that required to track events happening within the business layer and could not be tracked using JavaScript from the client. To do this a helper class was required in order to communicate with Google Analytics and track those extra events. Below you will find that class that can be used to do Page Tracking, Event Tracking, Ecommerce Tracking, Social Interactions and Exception Tracking within your C# code without requiring any JavaScript or any other script.

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Web;
    
    namespace Spyriadis.net
    {
        public class GoogleTracker
        {
            private string googleURL = "http://www.google-analytics.com/collect";
            private string googleVersion = "1";
            private string googleTrackingID = "UA-XXXX-Y";
            private string googleClientID = "555";
    
            public GoogleTracker(string trackingID)
            {
                this.googleTrackingID = trackingID;
            }
    
            public void trackEvent(string category, string action, string label, string value)
            {
                Hashtable ht = baseValues();
    
                ht.Add("t", "event");                   // Event hit type
                ht.Add("ec", category);                 // Event Category. Required.
                ht.Add("ea", action);                   // Event Action. Required.
                if (label != null) ht.Add("el", label); // Event label.
                if (value != null) ht.Add("ev", value); // Event value.
    
                postData(ht);
            }
            public void trackPage(string hostname, string page, string title)
            {
                Hashtable ht = baseValues();
    
                ht.Add("t", "pageview");                // Pageview hit type.
                ht.Add("dh", hostname);                 // Document hostname.
                ht.Add("dp", page);                     // Page.
                ht.Add("dt", title);                    // Title.
    
                postData(ht);
            }
    
            public void ecommerceTransaction(string id, string affiliation, string revenue, string shipping, string tax, string currency)
            {
                Hashtable ht = baseValues();
    
                ht.Add("t", "transaction");       // Transaction hit type.
                ht.Add("ti", id);                 // transaction ID.            Required.
                ht.Add("ta", affiliation);        // Transaction affiliation.
                ht.Add("tr", revenue);            // Transaction revenue.
                ht.Add("ts", shipping);           // Transaction shipping.
                ht.Add("tt", tax);                // Transaction tax.
                ht.Add("cu", currency);           // Currency code.
    
                postData(ht);
            }
            public void ecommerceItem(string id, string name, string price, string quantity, string code, string category, string currency)
            {
                Hashtable ht = baseValues();
    
                ht.Add("t", "item");              // Item hit type.
                ht.Add("ti", id);                 // transaction ID.            Required.
                ht.Add("in", name);               // Item name.                 Required.
                ht.Add("ip", price);              // Item price.
                ht.Add("iq", quantity);           // Item quantity.
                ht.Add("ic", code);               // Item code / SKU.
                ht.Add("iv", category);           // Item variation / category.
                ht.Add("cu", currency);           // Currency code.
    
                postData(ht);
            }
    
            public void trackSocial(string action, string network, string target)
            {
                Hashtable ht = baseValues();
    
                ht.Add("t", "social");                // Social hit type.
                ht.Add("dh", action);                 // Social Action.         Required.
                ht.Add("dp", network);                // Social Network.        Required.
                ht.Add("dt", target);                 // Social Target.         Required.
    
                postData(ht);
            }
    
            public void trackException(string description, bool fatal)
            {
                Hashtable ht = baseValues();
    
                ht.Add("t", "exception");             // Exception hit type.
                ht.Add("dh", description);            // Exception description.         Required.
                ht.Add("dp", fatal ? "1" : "0");      // Exception is fatal?            Required.
    
                postData(ht);
            }
    
            private Hashtable baseValues()
            {
                Hashtable ht = new Hashtable();
                ht.Add("v", googleVersion);         // Version.
                ht.Add("tid", googleTrackingID);    // Tracking ID / Web property / Property ID.
                ht.Add("cid", googleClientID);      // Anonymous Client ID.
                return ht;
            }
            private bool postData(Hashtable values)
            {
                string data = "";
                foreach (var key in values.Keys)
                {
                    if (data != "") data += "&";
                    if (values[key] != null) data += key.ToString() + "=" + HttpUtility.UrlEncode(values[key].ToString());
                }
    
                using (var client = new WebClient())
                {
                    var result = client.UploadString(googleURL, "POST", data);
                }
    
                return true;
            }
        }
    }

    All you need to do afterwards initialize the class using the code below:

    Spyriadis.net.GoogleTracker ga = new Spyriadis.net.GoogleTracker("UA-XXXXXXX-X");

    And then you can use all the functions by Google as described here. For example to track an event you use:

    ga.trackEvent("Category", "Action", "label", "value");

    You can pass null values for all the non-required fields.

  • Office share – Quickly and safely share links and notes with your colleagues

    IPShare.netHow many times have you sent an email simply to share a link or a note with the person next to you at the office or an internet cafe?

    Many times you will use Facebook Messenger or Google Chat or email to sent some text to the person next to you. Some of those time that person might be a complete stranger and maybe you don’t even want him/her to know your Facebook and/or email. This little web application allows you to trade small notes simply by copying and pasting your note on the page.

    It is safe, only people in your network will be able to see the notes, and you could even use a password to protect your notes from other people in your network.

    http://ipshare.net

  • Umbraco Xml Exception – Error Publishing Invalid character

    A general problem that I recently encountered for the second time is an Umbraco site not being able to generate any page without displaying an error or timeout. After examining the log (umbracoLog table on SQL server) you will see the error below:

      Error Republishing: System.Xml.XmlException: '', hexadecimal value 0x1F, is an invalid character. Line 1, position 2166.
       at System.Xml.XmlTextReaderImpl.Throw(String res, String[] args)
       at System.Xml.XmlTextReaderImpl.ParseCDataOrComment(XmlNodeType type, Int32& outStartPos, Int32& outEndPos)
       at System.Xml.XmlTextReaderImpl.ParseCDataOrComment(XmlNodeType type)
       at System.Xml.XmlTextReaderImpl.ParseElementContent()
       at System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace)
       at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc)
       at System.Xml.XmlDocument.Load(XmlReader reader)
       at System.Xml.XmlDocument.LoadXml(String xml)
       at umbraco.content.LoadContentFromDatabase()

    This means that somehow (usually if you copy/paste text from other sources directly to the editor) an invalid character has been inserted on a node. That invalid character prevents the XML document created by Umbraco to be loaded and therefore no pages can be generated. To find which node has the error you can use the code below:

    DECLARE @invalidCharacter nvarchar(100) = convert(nvarchar(100), 0x1F)
    
    SELECT * FROM [cmsContentXML]
    WHERE
    	xml like '%'+@invalidCharacter+'%'

    Now that you know where the problematic character is you can replace it either using Umbraco administrator or using the T-SQL below:

    DECLARE @invalidCharacter nvarchar(100) = convert(nvarchar(100), 0x1F)
    
    UPDATE [cmsPropertyData] SET
    	[dataNvarchar] = REPLACE(cast([dataNvarchar] as nvarchar(max)),@invalidCharacter,''),
    	[dataNtext] = REPLACE(cast([dataNtext] as nvarchar(max)),@invalidCharacter,'')
    WHERE
    	[dataNvarchar] like '%'+@invalidCharacter+'%' OR
    	[dataNtext] like '%'+@invalidCharacter+'%'
    
    UPDATE [cmsContentXML] SET
    	[xml] = REPLACE(cast([xml] as nvarchar(max)),@invalidCharacter,'')
    WHERE
    	[xml] like '%'+@invalidCharacter+'%'

    Depending on your error you need to change the hexadecimal value that creates the problem. In my case and the examples above I have used “0x1F“. This value is at the first line of the error message “.. , hexadecimal value 0x1F, …”.

     

  • Umbraco Clear Old Document Versions To Decrease Database Size And Improve Performance

    Recently I had a problem with an Umbraco 4.7.1 installation that had became 10GB in SQL Server and had about 20000 published pages. Since there is no way to do something like this from the umbraco administration panel I had to create a T-SQL to clear the database.

    DECLARE @createdDate Datetime = '2012-03-01'
    
    DELETE FROM cmsPropertyData WHERE
    	versionId NOT IN (SELECT versionId FROM cmsDocument WHERE updateDate > @createdDate OR published = 1 OR newest = 1) AND
    	contentNodeId IN (SELECT DISTINCT nodeID FROM cmsDocument)
    DELETE FROM cmsPreviewXml WHERE
    	versionId NOT IN (SELECT versionId FROM cmsDocument WHERE updateDate > @createdDate OR published = 1 OR newest = 1) AND
    	nodeId IN (SELECT DISTINCT nodeID FROM cmsDocument)
    DELETE FROM cmsContentVersion WHERE
    	versionId NOT IN (SELECT versionId FROM cmsDocument WHERE updateDate > @createdDate OR published = 1 OR newest = 1) AND
    	ContentId  IN (SELECT DISTINCT nodeID FROM cmsDocument)
    DELETE FROM cmsDocument WHERE
    	versionId NOT IN (SELECT versionId FROM cmsDocument WHERE updateDate > @createdDate OR published = 1 OR newest = 1) AND
    	nodeId IN (SELECT DISTINCT nodeID FROM cmsDocument)

    Warning if you do not have any TSQL experience be careful because you could easily delete all the data in your database. Always take a backup before making any changes this way.

  • Umbraco Fix – Empty recycle bin using SQL if backend fails

    Updated thanks to jeffreypriebe

    -- Uncomment below to verify the number of nodes returned is the same as the number of nodes that is in the Recycle Bin
    -- SELECT * FROM umbracoNode WHERE path LIKE '%-20%' AND id != -20
    
    --Setup the temporary table
    CREATE TABLE #temp ([id] int);
    INSERT #temp select id from umbracoNode where path like '%-20%' and id!=-20
    
    -- Delete all 'related' nodes and table contents...
    DELETE FROM cmsPreviewXml WHERE nodeId IN (SELECT id FROM #temp)
    DELETE FROM cmsContentVersion WHERE contentId IN (SELECT id FROM #temp)
    DELETE FROM cmsDocument WHERE nodeId IN (SELECT id FROM #temp)
    DELETE FROM cmsContentXML WHERE nodeId IN (SELECT id FROM #temp)
    DELETE FROM cmsContent WHERE nodeId IN (SELECT id FROM #temp)
    DELETE FROM cmsPropertyData WHERE contentNodeId IN (SELECT id FROM #temp)
    DELETE FROM umbracoUser2NodePermission WHERE nodeId IN (SELECT id FROM #temp)
    DELETE FROM umbracoRelation  where parentId in (select id from #temp) or childId in (select id from #temp)
    
    -- remove fk constraint so that umbracoNode can delete
    ALTER TABLE umbracoNode DROP CONSTRAINT FK_umbracoNode_umbracoNode
    -- delete the XML nodes...
    DELETE FROM umbracoNode WHERE id in (SELECT id FROM #temp)
    -- re add fk constraint to umbracoNode
    ALTER TABLE umbracoNode WITH NOCHECK ADD CONSTRAINT FK_umbracoNode_umbracoNode
    FOREIGN KEY (parentId)
    REFERENCES umbracoNode (id)
    
    --Cleanup temp
    DROP TABLE #temp;
    
  • Share a clipboard across all your devices in one network (copy/paste)

    IPShare.netHow many times you wanted to copy a message/phone/email from one device to another on your local network and in order to do it you had to share a notepad file or sent an email to yourself or logged in your google account to create a note etc. Since this had happend to me quite a loot latelly I decided to do something about it and create a siple web application to perform this task.

    All you have to do is open a web browser and type www.ipshare.net. No logins, signup or anything is required! Just like that you have a textbox that is shared accross all your local devices in your network. All you need is an active internet connection and you are good to go!

    Next time you want to share something just give it a try. You’ll love it.

  • Check replication synchronization status using Transactional SQL (TSQL)

    Below you will find a select that will help you check the state of all your publication on SQL server in order to know if all the subscribers are in synch or the syncronization agent has stopped running.

    SELECT
    (CASE
        WHEN mdh.runstatus =  '1' THEN 'Start - '+cast(mdh.runstatus as varchar)
        WHEN mdh.runstatus =  '2' THEN 'Succeed - '+cast(mdh.runstatus as varchar)
        WHEN mdh.runstatus =  '3' THEN 'InProgress - '+cast(mdh.runstatus as varchar)
        WHEN mdh.runstatus =  '4' THEN 'Idle - '+cast(mdh.runstatus as varchar)
        WHEN mdh.runstatus =  '5' THEN 'Retry - '+cast(mdh.runstatus as varchar)
        WHEN mdh.runstatus =  '6' THEN 'Fail - '+cast(mdh.runstatus as varchar)
        ELSE CAST(mdh.runstatus AS VARCHAR)
    END) [Run Status],
    mda.subscriber_db [Subscriber DB],
    mda.publication [PUB Name],
    right(left(mda.name,LEN(mda.name)-(len(mda.id)+1)), LEN(left(mda.name,LEN(mda.name)-(len(mda.id)+1)))-(10+len(mda.publisher_db)+(case when mda.publisher_db='ALL' then 1 else LEN(mda.publication)+2 end))) [SUBSCRIBER],
    CONVERT(VARCHAR(25),mdh.[time]) [LastSynchronized],
    und.UndelivCmdsInDistDB [UndistCom],
    mdh.comments [Comments],
    'select * from distribution.dbo.msrepl_errors (nolock) where id = ' + CAST(mdh.error_id AS VARCHAR(8)) [Query More Info],
    mdh.xact_seqno [SEQ_NO],
    (CASE
        WHEN mda.subscription_type =  '0' THEN 'Push'
        WHEN mda.subscription_type =  '1' THEN 'Pull'
        WHEN mda.subscription_type =  '2' THEN 'Anonymous'
        ELSE CAST(mda.subscription_type AS VARCHAR)
    END) [SUB Type],
    
    mda.publisher_db+' - '+CAST(mda.publisher_database_id as varchar) [Publisher DB],
    mda.name [Pub - DB - Publication - SUB - AgentID]
    FROM distribution.dbo.MSdistribution_agents mda
    LEFT JOIN distribution.dbo.MSdistribution_history mdh ON mdh.agent_id = mda.id
    JOIN
        (SELECT s.agent_id, MaxAgentValue.[time], SUM(CASE WHEN xact_seqno > MaxAgentValue.maxseq THEN 1 ELSE 0 END) AS UndelivCmdsInDistDB
        FROM distribution.dbo.MSrepl_commands t (NOLOCK)
        JOIN distribution.dbo.MSsubscriptions AS s (NOLOCK) ON (t.article_id = s.article_id AND t.publisher_database_id=s.publisher_database_id )
        JOIN
            (SELECT hist.agent_id, MAX(hist.[time]) AS [time], h.maxseq
            FROM distribution.dbo.MSdistribution_history hist (NOLOCK)
            JOIN (SELECT agent_id,ISNULL(MAX(xact_seqno),0x0) AS maxseq
            FROM distribution.dbo.MSdistribution_history (NOLOCK)
            GROUP BY agent_id) AS h
            ON (hist.agent_id=h.agent_id AND h.maxseq=hist.xact_seqno)
            GROUP BY hist.agent_id, h.maxseq
            ) AS MaxAgentValue
        ON MaxAgentValue.agent_id = s.agent_id
        GROUP BY s.agent_id, MaxAgentValue.[time]
        ) und
    ON mda.id = und.agent_id AND und.[time] = mdh.[time]
    where mda.subscriber_db<>'virtual' -- created when your publication has the immediate_sync property set to true. This property dictates whether snapshot is available all the time for new subscriptions to be initialized. This affects the cleanup behavior of transactional replication. If this property is set to true, the transactions will be retained for max retention period instead of it getting cleaned up as soon as all the subscriptions got the change.
    --and mdh.runstatus='6' --Fail
    --and mdh.runstatus<>'2' --Succeed
    order by mdh.[time]
  • Change the SQL Server Name after Changing the Computer Name

    Recently I changed the name of my SQL Server computer from the generic one given by the Windows installation to ‘SERVER’. In the beggining there was no apparent problem in the connections since I used IP addresses. After a while I wanted to use Replication and tried to create an SQL Server Publication only to find that the name of the SQL SERVER has not changed and I could not create a publication unless I connected with the correct computer name. It turns out that changing the computer name does not update the sql server name and you need to change it manually in order to be able to use replication or any other Sql Server feature that requires you to connect using the computer name!

    Below is a simple code that you can use to change the name of the SQL Server.

    sp_dropserver @@SERVERNAME
    GO
    sp_addserver 'newServerName', local
    GO

    You need to restart the SQL Server in order for the changes to take effect.