EF 4’s PluralizationService Class: A Singularly Impossible Plurality

Entity Framework’s new 4.0 designer does its best to generate correct plural and singular forms of object names. This magic is done through the PluralizationService Class found in the System.Data.Entity.Design.PluralizationServices namespace and in the System.Data.Entity.Design.dll assembly.

[Before you ask… Yes, I’ll post my example page, the service, and the project source code as soon as my ISP makes ASP.NET 4 RTM available. Stay tuned.]

Anyone who speaks English is brutally aware of the ridiculous and inconsistent rules for constructing the plural and singular forms of nouns. I’m not sure how non-native speakers ever get a handle on the bizarre and senseless exceptions.

With that in mind, I was curious as to how well Microsoft fared with applying computer logic and lookups to an illogical language. I worked up a little WCF service and a Web page to make it easy to test.

The service returns a SingularPluralInfo object that contains the essential information about a word you provide:

Public Class SingularPluralInfo
    Property PluralValue As String
    Property SingularValue As String
    Property IsPlural As Boolean
    Property ErrorMessage As String
End Class

Because the PluralizationService can determine whether a word is a plural or not, I was able to put all the code in one function called MakeSingleOrPlural(). Users pass either the singular or plural of a word and get back both forms.

Public Function MakeSingleOrPlural(ByVal word As String, _ 
             Optional ByVal culturestring As String = "en") _ 
             As SingularPluralInfo Implements _ 
             Iplservice.MakeSingleOrPlural


The first lines of the function initialize the variables and verify that the user has entered some text. Notice the use of the new String.IsNullOrWhiteSpace(word) function in .NET 4:

 

Dim spinfo As New SingularPluralInfo
Dim pls As PluralizationService = Nothing
spinfo.PluralValue = String.Empty
spinfo.SingularValue = String.Empty
spinfo.ErrorMessage = String.Empty
spinfo.IsPlural = False

If String.IsNullOrWhiteSpace(word) Then
    spinfo.ErrorMessage = "No input provided"
    Return spinfo
End If


The fun starts with calling the CreateService method which wants a CultureInfo object as a parameter. I wrapped this in a Try…Catch block because, out of the box, the service throws an exception if you try to feed it a non-English culture:

 Try
    pls = PluralizationService.CreateService _
        (New CultureInfo(culturestring))
    If pls.IsPlural(word) Then
        spinfo.IsPlural = True
        spinfo.PluralValue = word
        spinfo.SingularValue = pls.Singularize(word)
    Else
        spinfo.IsPlural = False
        spinfo.SingularValue = word
        spinfo.PluralValue = pls.Pluralize(word)
    End If
Catch ex As NotImplementedException
    spinfo.ErrorMessage = "Sorry, only English is supported"
Catch ex As Exception
    spinfo.ErrorMessage = ex.Message
End Try

The Singularize() and Pluralize() methods in the preceding code take the string into a black box and produce a result.

The client for the web service is a simple ASP.NET 4 page. While creating the page, I used new ASP.NET features to reduce the page’s overhead. For example, the declarations turn off ViewState, EventValidation, and use static element IDs. These three settings make a huge difference in the amount of data on the wire:

<%@ Page Language="VB" ViewStateMode="Disabled"
ClientIDMode="Static" EnableEventValidation="false" %>
<%@ Import Namespace="System.Globalization" %>

For markup, I added a TextBox, Button, DropDownList and some labels:

<asp:TextBox ID="txtInput" runat="server"></asp:TextBox>&nbsp;
<asp:DropDownList ID="ddlCulture" runat="server">
</asp:DropDownList><br /><br />
<asp:Button ID="btnConvert" runat="server"
    Text="Convert" OnClick="btnConvert_Click" /><br /><br />
Singular:
<asp:Label ID="lblSingular" runat="server"></asp:Label><br />
Plural:
<asp:Label ID="lblPlural" runat="server"></asp:Label><br />
Error:
<asp:Label ID="lblError" runat="server"></asp:Label>

The Page Load event fills the DropDownList with the names of the cultures available on the IIS machine. Rather than store the data in bloated ViewState, I decided to cache it and fetch it anew on each postback.

 

Protected Sub Page_Load(ByVal sender As Object, _
                        ByVal e As System.EventArgs)
    Dim q As System.Linq.IOrderedEnumerable(Of String)
    q = Cache("cultures")
    If IsNothing(q) Then
        q = From c In _
            System.Globalization.CultureInfo.GetCultures _
                (Globalization.CultureTypes.AllCultures) _
            Select c.Name Order By Name
        Cache("cultures") = q
    End If
    ddlCulture.DataSource = q
    ddlCulture.DataBind()
    ddlCulture.SelectedValue = "en-CA"
End Sub

The real action happens with the click of the button and the postback. The following code instantiates the WCF service, creates a SingularPluralInfo object to hold the results, and passes the user input to the MakeSingleOrPlural() method:

Protected Sub btnConvert_Click(ByVal sender As Object, _ 
                          ByVal e As System.EventArgs)
    Dim plsvc As New plservice
    Dim spi As New SingularPluralInfo
    Dim word As String = String.Empty
    word = txtInput.Text
    If String.IsNullOrWhiteSpace(word) Then
        lblError.Text = "Please enter a word"
    Else
        spi = plsvc.MakeSingleOrPlural _
            (word, ddlCulture.SelectedItem.Value)
        lblError.Text = spi.ErrorMessage
        lblPlural.Text = spi.PluralValue
        lblSingular.Text = spi.SingularValue
    End If
End Sub

Okay, it’s time to test the pluralization service with some eccentricities of the English language. In the table below, I’ve put some of the wrong results at the top. However, what’s remarkable is that the singularization and pluralization was correct for some very obscure words, such as viscus.

 

Input Result
movies movy
money moneys
moose mooses
census censu
movie movies
aircraft aircraft
goose geese
louse lice
woman women
tooth teeth
matrix matrices
phenomenon phenomena
species species
alumnus alumni
censuses census
viscus viscera
child children

Maybe the next version of EF will take on collection naming for us? For example, a collection of geese is a gaggle, and a collection of lions is a pride. Anyone who writes documentation will tell you that a collection of technical writers is known as a quarrel. <grin> 

BTW, you’re welcome to post words that you feel the PluralizationService mishandles. However, keep in mind that language is a shifting, argument-laden battleground where even experts contradict each other.

Ken

Microsoft MVP [ASP.NET]

3 Comments

  • The $64,000 question: does it know datum/data?

  • Why can't I find the System.Data.Entity.Design.dll in the GAC?
    I have .net4 installed and the service is probaboly ruuning fine as the EF4 generator uses it with grate success. Any ideas?

  • This could be really usefull as string extension methods, so:

    public static string Singularize(this string word)
    {
    if (string.IsNullOrWhiteSpace(word))
    return word;
    PluralizationService pls = PluralizationService.CreateService(new CultureInfo("en"));
    if (pls.IsPlural(word))
    return pls.Singularize(word);
    else
    return word;
    }

    public static string Pluralize(this string word)
    {
    if (string.IsNullOrWhiteSpace(word))
    return word;
    PluralizationService pls = PluralizationService.CreateService(new CultureInfo("en"));
    if (pls.IsSingular(word))
    return pls.Pluralize(word);
    else
    return word;
    }

Comments have been disabled for this content.