Orchard CMS 1.5.1 – Filtering Content by Taxonomies


Filtering contents by Taxonomies or categories in Orchard can be a challenge. I’ve found articles talking about cycling through taxonomies in a specific Content Type, but not one to get a list of Content Items for a custom filter. The reason I couldn’t use other methods is that I’m creating a custom search based on several other criteria, including a text field entered by the user.

Taxonomy terms are of type System.Linq.Enumerable.WhereSelectEnumerableIterator<Contrib.Taxonomies.Models.TermContentItem,Contrib.Taxonomies.Models.TermPart> which is difficult to work with. So after some trial and error, I found a way to do this.

In this example, I have a Content Type named Product. Inside the Product Content Type, I have a Product Part which contains the Brand taxonomy.

The method below takes in a brand name, and returns a IEnumerable<dynamic&gt list of Product Content Type with the brand name specified by the argument

private readonly IOrchardServices _orchardServices;

private IEnumerable&lt;dynamic&gt; GetFilteredItemsByTaxonomy(string taxonomyTerm)
{
    // Filtering for only product Content Type
    //   Using the dynamic type to access the taxonomy data directy
    IEnumerable&lt;dynamic&gt; products = _contentManager.Query(VersionOptions.Published
        , new string[] { &quot;Product&quot; }).List();

    // Filtering the products by brand where it contains the taxonomyTerm
    return products.Where(x =&gt;
      ((IEnumerable&lt;Contrib.Taxonomies.Models.TermPart&gt;)x.ProductPart.Brand.Terms.Value)
        .Select(y =&gt; y.Name).Contains(taxonomyTerm));
}

I would like to mention that PluralSight has a great Orchard Tutorial on Orchard modules.

Programmatically Attaching an Excel Document to an Email in .NET

Periodically clients will ask me to send them a report from a database. Instead of creating an Excel spreadsheet from the database and then attaching it to an email, I have automated the process. The following function will take in a MailMessage object by reference along with a DataGrid object and filename string. The function then attaches an Excel file containing the DataGrid contents to the MailMessage object.

This function uses the following inclusions: System.Net, System.IO, System.Net.Mail, System.Collections, System.Collections.Generic

    // Function attaches a DataGrid to Email in Excel Format
    private void AttachDataGridToEmail(ref MailMessage mailMessage, DataGrid dataGrid, String filename)
    {
        MemoryStream ms = new MemoryStream();
        System.IO.StringWriter stringWriter = new System.IO.StringWriter();
        System.Web.UI.HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);
        dataGrid.RenderControl(htmlWriter);
        byte[] excelFile = StrToByteArray(stringWriter.ToString());
        ms.Write(excelFile, 0, excelFile.Length);
        ms.Position = 0;
        Attachment attachment = new Attachment(ms, filename + ".xls", "application/vnd.xls");
        mailMessage.Attachments.Add(attachment);
    }

Multithreading in .NET

Multithreading is a technique of executing multiple threads at once, to minimize processing time. Multithreading is not used very often in websites, but it does have its place in even simple sites.

Working on a website for one of my clients, I noticed the database connection very slow. The site had a contact form that emailed the administration in event of a contact form, but it also kept the information in a database. In this case, I set a separate thread to take care of the database, while the system processed the email request. The user could receive their “Thank you” page without waiting for the database.

Microsoft has a nice msdn post about Threading, but here is a quick example to get you started, using System.Threading:

private void insertContact(object o)
{
    if (o.GetType() == typeof(ContactUs))
    {
        ContactUsData.insertContactUs((CO_ContactUs)o);
    }
}

public void BeginInsertContact(ContactUs contact)
{
    WaitCallback callBack;

    callBack = new WaitCallback(insertContact);

    ThreadPool.QueueUserWorkItem(callBack, contact);
}

From your code, you can call the BeginInsertContact method with your object. The method will put the callBack function into the ThreadPool to execute when it has time. Since the method calls a thread, this function is non-blocking, so the user does not have to wait for it to execute.

Programmatically Querying MS SQL in C#

There are several ways to query in C#. Sometimes I see very creative ways of using built in controls. But, if you want a simple method to select or insert data from and to the database, I found this to me the most straight-forward.

For this example, we’ll assume you have a table called customers already set up in MS SQL. The table is composed of three columns, an auto-generated id column, a cust_name column, and a entry_date column.

customers
id cust_name entry_date

The first step is to include the database location and login information in your web.config file inside the <appSettings> tag, replacing localhost, my_userid, my_password, and my_database with your database information:

<add key="connection" value="server=localhost;Persist Security Info=True;uid=my_userid;pwd=my_password;Initial Catalog=my_database;"/>

Create a new class called Customers.cs inside the App_Code folder to store the row values:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Collections;
using System.Collections.Generic;

public class Customers
{
    private int id = -1;
    private String cust_name;
    private DateTime entry_date;

    public Customers()
    {

    }

    public int Id
    {
        get
        {
            return id;
        }

        set
        {
            id = value;
        }
    }

    public String Cust_name
    {
        get
        {
            return cust_name;
        }

        set
        {
            cust_name = value;
        }
    }

    public DateTime Entry_date
    {
        get
        {
            return entry_date;
        }

        set
        {
            entry_date = value;
        }
    }
}

Then create a new class named CustomersData.cs inside the App_Code folder:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Generic;

public static class CustomersData
{
    private static string connection = ConfigurationManager.AppSettings.Get("connection");
    private static SqlConnection myConnection = new SqlConnection(connection);

    // Get all customers in arraylist
    public static ArrayList getAllCustomers()
    {
        SqlDataReader reader = null;
        ArrayList customersArray = new ArrayList();
        try
        {
            if (myConnection.State == ConnectionState.Closed)
            {
                myConnection.Open();
            }

            SqlCommand command = new SqlCommand("select * from customers;");
            command.Connection = myConnection;
            reader = command.ExecuteReader();

            while (reader.Read())
            {
                Customers customers = new Customers();
                customers.Id = Int32.Parse(reader["id"].ToString().Trim());
                customers.Cust_name = reader["cust_name"].ToString().Trim();
                customers.Entry_date = DateTime.Parse(reader["entry_date"].ToString().Trim());

                customersArray.Add(customers);
            }
        }
        catch(Exception air)
        {
            System.Console.WriteLine(air.ToString());
            myConnection.Close();
            return null;
        }
        myConnection.Close();
        return customersArray;
    }

    // Get single customer by id
    public static Customers getCustomersById(int id)
    {
        SqlDataReader reader = null;
        Customers customers = new Customers();
        try
        {
            if (myConnection.State == ConnectionState.Closed)
            {
                myConnection.Open();
            }

            SqlCommand command = new SqlCommand("select * from customers where id=@id;");
            command.Connection = myConnection;
            command.Parameters.AddWithValue("@id", id);
            reader = command.ExecuteReader();

            while (reader.Read())
            {
                customers.Id = Int32.Parse(reader["id"].ToString().Trim());
                customers.Cust_name = reader["cust_name"].ToString().Trim();
                customers.Entry_date = DateTime.Parse(reader["entry_date"].ToString().Trim());

            }
        }
        catch(Exception air)
        {
            System.Console.WriteLine(air.ToString());
            myConnection.Close();
            return null;
        }
        myConnection.Close();
        return customers;
    }

    // Insert new customer row and returns -1 if fails
    public static int insertCustomers(Customers customers)
    {
        Int32 newId = -1;

        try
        {
            if (myConnection.State == ConnectionState.Closed)
            {
                myConnection.Open();
            }

            SqlCommand cmd = new SqlCommand("insert into customers values(@cust_name, getDate());select CAST(scope_identity() AS int);");
            cmd.Connection = myConnection;

            cmd.Parameters.AddWithValue("@cust_name", customers.Cust_name);

            newId = (Int32)cmd.ExecuteScalar();
            myConnection.Close();
        }
        catch(Exception air)
        {
            System.Console.WriteLine(air.ToString());
            myConnection.Close();
            return newId;
        }
        return newId;
    }
}

If you are wondering why we use the @cust_name in the query instead of just adding the string, it’s to protect your database against someone trying to add sql code to your database. For example, someone could say their name is “a, getDate());drop table customers;” and erase your table from the database.

With the two classes implemented, you can now make database calls from your code-behind file. For example, in your Default.aspx.cs file, you can have:

// Creating new customer instance c1
Customers c1 = new Customers();

// Setting the c1 name to "John"
c1.Cust_name = "John";

// Inserting c1 into database, and storing the generated id in newId
int newId = CustomersData.insertCustomers(c1);

// Getting the customers info into c2 instance
Customers c2 = CustomersData.getCustomersById(newId);