Multi-blog Obsession

by Jacob 6. April 2009 05:26

TwoBlogsThe multi-blog data provider for BlogEngine.Net has been taking up a lot of my brain space lately—to the point that I’m able to announce that it is installed and working “in the wild” on a hosted site (though not in anything like a heavy-load situation). I now have a copy of both my dev site and my personal site up and running from the same directory (and the same database). Frankly, I didn’t think it’d be as easy as it was. This success prompted me to create a 2.0 release (that is now up on the CodePlex site).

Getting Static

My main fear was with the heavy use of static variables in BlogEngine.Net. You see, BE.Net loads all the data into memory using static List variables. I found this out when I went looking for the best way to store a BlogId (so that it didn’t have to parse against an Url every time a request came through).

While there are pros and cons to keeping your entire blog in memory (pro: speed and ease, con: memory bloat and a large delay on any request that triggers a data load), my concern was how an application would react when it had to serve two sets of data. Fortunately, it seems that even when two sites share an application pool on IIS, they still keep their static spaces separate. I’m not sure what I was going to do if it didn’t but I was spared the tragedy.

Configuration

Installing the blog provider mainly involves copying the binary into the /bin directory and then updating the web.config to point to the right driver. There are three providers in your web.config that are affected.

Blog Provider

The blog provider handles the blog data. Settings, posts, categories and suchlike. Add the provider and update the “defaultProvider” tag and you’re ready to go.

<BlogEngine>
  <blogProvider defaultProvider="SQLBlogProvider">
    <providers>
      <add name="SQLBlogProvider" type="BlogEngine.SQLServer.SqlBlogProvider, BlogEngine.SQLServer" connectionStringName="BE"/>
    </providers>
  </blogProvider>
</BlogEngine>

Membership Provider

The membership provider handles user authentication and management (stuff like changing passwords and such). Technically, you don’t need to change this, but if you don’t the users will be the same across blogs (not a problem if you aren’t multi-blogging). I frankly haven’t tested if a mixed-configuration actually works but it should. Again, add the provider and update the “defaultProvider” tag and you’re ready to go.

<membership defaultProvider="LinqMembershipProvider">
  <providers>
    <clear/>
    <add name="LinqMembershipProvider" type="BlogEngine.SQLServer.LinqMembershipProvider, BlogEngine.SQLServer" passwordFormat="Hashed" connectionStringName="BE"/>
  </providers>
</membership>

Role Provider

The role provider handles authorization and what users are assigned to which roles. Again, you don’t technically have to change this if you don’t need it. Also again, it’s simply a matter of adding the provider and changing the “defaultProvider” tag.

<roleManager defaultProvider="LinqRoleProvider" enabled="true" cacheRolesInCookie="true" cookieName=".BLOGENGINEROLES">
  <providers>
    <clear/>
    <add name="LinqRoleProvider" type="BlogEngine.SQLServer.LinqRoleProvider, BlogEngine.SQLServer" connectionStringName="BE"/>
  </providers>
</roleManager>

Multiple-blog Configuration

To set stuff up for multiple blogs, you’ll need to run a script or two in your database and add a tag to all the providers. There are two script files (included in both the binary and source files), one for setting up the initial database changes (DatabaseSchemaChanges.sql—mostly adds tables) and another for adding the base values for a new blog (AddNewBlog.sql).

I wanted to make this easier by having the driver do the updates for you. That may still happen in the future, but since BlogEngine.Net itself requires manually running a script if you want to use the database provider I decided not to sweat it too hard. Presumably, anyone running in a database has to be running scripts manually anyway so this isn’t going to be a show stopper.

The provider will run just fine after running either script, even if you aren’t using multiple blogs. In other words, just because the database changed doesn’t mean that the single-blog installation is hosed. The exception to this is the “be_Settings” table. If you’re going to run for a while with a single-blog after running the first script, you’ll want to add a default to the BlogId column so it doesn’t choke when you insert and update settings.DefaultBlogId

Both scripts are “templated” so you can change key factors (a table prefix on the first and a couple of blog values in the second). Filling in the template is a matter of hitting ctrl-shift-M in Query Analyzer or SQL Server Management Studio. That’ll bring up a prompt for what values you want those template variables to have.TemplatePrompt

The final thing to setup is to add a multiblog attribute on the providers. That’ll make your providers look something like this.

<add name="SQLBlogProvider" type="BlogEngine.SQLServer.SqlBlogProvider, BlogEngine.SQLServer" connectionStringName="BE" multiblog="true"/>

The provider selects the blog it wants to deliver based on three configured values.

  • Host is the base address. The provider matches the Host value against the end of the host (so rabidpaladin.com will match “rabidpaladin.com”, “www.rabidpaladin.com” and “blog.rabidpaladin.com”).
  • Path is the rest of the Url. The provider matches the Path value against the start of the requested path.
  • Port is the port (if any) in the Url. Honestly, I threw this one in there as much for my testing as for any real-world use I expect it to see.

Tags

One thing I added (at the provider level) is that when a post comes in without any tags, the provider takes a moment to scan for tags in the post body. This is a feature I did the initial work for in Subtext so porting it over was a matter of a couple minutes. Any time a post is inserted into the database, the provider checks if it has tags yet. If no tags are present, it will scan the content for appropriate anchor markup (like those produced for Technorati tags). That means that on import, my posts all had their tags correctly populated—saving me a lot of extra work (or face losing tags on imported posts). That I was able to avoid the brain-damaged tag handling of BlogEngine.Net is just a bonus (they lower-case tags on creation and then re-capitalize them when serving them up).

Other Stuff

As I said, this should get you set up. Since I used this blog provider from the start on both my blogs, I can verify that the import tool works just fine in a multi-blog configuration. As far as BlogEngine.Net is aware, it’s doing the same stuff it always has. Indeed, the only change I made from BlogEngine.Net’s standard v1.4.5 release was in UrlRewrite.cs to allow links produced by Subtext to still work (so I don’t throw errors on old links).

else if (url.Contains("/POST/") || url.Contains("/ARCHIVE/"))

I submitted a patch at one time to have this hit the base source code but apparently it wasn’t deemed worthy.

Also, I found that running the provider in IIS7 is a bit tricky. Since BlogEngine.Net loads extensions from the database on application start you’ll get errors if you are configured for “Integrated” mode. That’s because “Integrated” mode (quite properly) fires the application start event before the HttpContext.Request is populated (which is what I’m using to determine what blog is being requested). Setting the application pool to “Classic” mode will solve this “problem”.IIS7ClassicMode

Looking Forward

My blogs are still running Subtext at their base addresses. I’m still not quite ready to take the plunge on BlogEngine.Net.  I am, however, undoubtedly one step closer.

Tags: , , , , , , ,

Programming

Multiple Blog Data

by Jacob 29. March 2009 09:01

PartialSchema So I have a working LINQ to SQL provider for BlogEngine.Net. Now what? Given a little spare time, how about I see if I can’t use it to support running multiple blogs from the same installation? More importantly, see if I can use it to support running multiple blogs from the same database?

Doing just that turns out not to be all that difficult.

Scheming

The current architecture for BlogEngine.Net’s data already has a bit more cohesion than it technically needs. All the objects have their own individual Ids and those Ids are used to relate objects to each other (though there is one exception). Since every object already has its own Id (usually a Guid), splitting objects into separate blogs isn’t the chore it might otherwise have been.

There are two options when it comes to dividing items up into multiple blogs. First, each object can have a column added to its table to indicate which blog it is associated with. Second, you can create a cross-reference table that associates a blog Id with the object Id for the blog.

Columns
My initial impulse in most cases would be to add a BlogId column to the tables where it is needed. The reason is simple: objects belonging to the blog are in a true parent-child relationship and that relationship is generally best expressed as a field on the child indicating its parent. The relationship can (and really should) be enforced with a foreign key constraint on the column to ensure that the relationship is intact.

Cross-references
Having cross-reference tables is a bit more problematic and carries with it some maintenance and performance concerns. Not only does it force a join when you want to read the objects for a specific blog, but it means that insert, update, and delete commands now have to involve two tables instead of just one. One advantage of cross-reference tables is that they’re easier to extract back out if you need to devolve your data. Additionally, foreign key constraint integrity is triggered when the cross-reference entry is created instead of on your blog objects themselves—making your touch a bit lighter if you have other actors in the system.

Complicating Things 
No decision is best for every occasion, and when it came time to design how I wanted multiple blogs to work, I was really reluctant to mess with the native tables of BlogEngine.Net. I’m not sure if my hesitation is a matter of respect for a project I’m not involved in or if I’m just being unreasonably squeamish, but I eventually chose to go the cross-reference route. My main reasoning is that I wanted my intrusions to remain light and easily devolved.

I ♥ Linq

Now, normally, adding a super-structure on top of an existing infrastructure is a real pain. Editing all your SQL statements manually becomes an exercise in precision string manipulation and if you’re working through stored procedures… ugh. Linq made this really easy.

Here’s an example from the FillProfiles method of the blog provider.

var profileData = from p in context.Profiles
                  select p;
if (isMultiBlog)
{
    profileData = from p in profileData
                  join bp in context.BlogProfiles on p.ProfileID equals bp.ProfileId
                  where bp.BlogId == Utils.GetBlogId()
                  select p;
}

The initial select is good for the general case. It pulls all the objects from the Profiles table. Adding a filter when we have multiple blogs is added in the if clause. Note that the second select references the first (“from p in profileData”). Linq knows that the second “from” is a refinement of the first and puts them together logically. Since Linq defers execution of the query until it’s actually used, the query sent to the server includes the full constraint (i.e. filtering happens on the database). Here’s the statement that’s actually sent.

SELECT [t0].[ProfileID], [t0].[UserName], [t0].[SettingName], [t0].[SettingValue]
FROM [dbo].[be_Profiles] AS [t0]
INNER JOIN [dbo].[be_BlogProfiles] AS [t1] ON [t0].[ProfileID] = [t1].[ProfileId]
WHERE [t1].[BlogId] = @p0

This method ensures that you only take the hit of the join if you are in a multi-blog setup. And without pulling everything to the client.

Settings

I had some fun with the Settings table because it is an exception to BlogEngine.Net’s Id rigor. It has interesting impact on the Linq situation, but I think I’ll give it its own (short) post later.

Beta Available

So I tested this in my own home-grown environment and it seems to work as expected. In consequence, I’ve created a new release at the project homepage. I’m calling it a beta, though it barely warrants the label. I worry that it has only been tested in a single environment. If you’re a hearty soul and a BE.Net user, please give it a go. I’ll be spending some time getting it set up and tested in an actual public setting with my personal blogs here shortly. As always, I welcome feedback either at codeplex or comments or via email.

Tags: , , , , , , ,

Programming

WCF With GP Web Services

by Jacob 15. March 2009 12:29

I’m at Convergence this week in New Orleans. If you’re unfamiliar with the conference (and don’t want to follow the nifty link), all you really need to know is that it’s Microsoft’s convention for their business solutions products. For me, that means Dynamics Great Plains.

I bring this up because in the last session I attended yesterday, Louis Maresca mentioned a problem I remembered having with GP Web Services. GP WS has a serious problem when you first instantiate the proxy object: it can take seconds (over 30 on our older systems—I put a timer in just to verify) to instantiate the web service proxy. The reason for this is that .Net queries the service to pull down the available methods and objects on instantiation. Since there are very many of them available in GP Web Services, the query and xml serialization adds up to quite a lot.

Now, his solution was very clever, but involved creating a new web service to slim down the contract retrieval. My solution was to saddle up and use WCF. You see, WCF doesn’t do silly things like query for contracts it already knows full well about. I cracked how to use WCF with GP Web Services about a year ago and I haven’t looked back since.

In that session last night I realized that others might want to know what it took to get it working (and thus a blog post was born…) I’m not going to go though creating the WCF bit. It’s pretty straight forward and explained all over.

Crap, I find I can’t actually proceed without at least giving an overview.

  1. Right-click your project.
  2. Select “Add Service Reference”.
  3. Fill out the dialog:

GPWSServiceReference

Okay, now that I got that out of my system there are two things that prevent WCF and GP Web Services from playing nice together.

Security

Since GP WS uses your windows identity to validate things like roles and permissions, your client needs to send the correct identity or “bad things can happen”™. In VS 2005 web services, this was a simple matter of setting .UseDefaultCredentials to true. In WCF it’s a good bit more complicated. It’s a mirror of remotely printing Reporting Services using WCF, though, so techniques used there are applicable (though slightly different).

First, you have to let the binding know the correct security mode and transport. I did this in a basicHttpBinding in the <security> section:

<security mode="TransportCredentialOnly">
  <transport clientCredentialType="Ntlm" />
</security>

I came at this setting obliquely and after much trial and error. I’m not sure why clientCredentialType=“Windows” didn’t work against GP WS when it worked with Reporting Services. Probably something quirky in our environment.

This alone is not enough, however. The binding setting is just the contract. To actually use the correct credentials, your proxy has to be told what to do. Not difficult, but easy to overlook when you’re coming from a 2005 web services background. Here’s all it takes:

DynamicsGPSoapClient service = new DynamicsGPSoapClient();
service.ClientCredentials.Windows.AllowedImpersonationLevel 
    = TokenImpersonationLevel.Impersonation;

Once that’s all taken care of, you’re set to go. Those two lines of code are processed pretty much instantaneously on even our slowest clients, so problem solved. Almost.

Errors

Error handling hung me up for a while and was the final hurdle to being able to truly implement WCF with GP WS. I was so excited when I finally figured it out that I blogged it at the time. The key point is that GP WS wants to check a user’s authorization to view errors before giving up the details of what happened so you have to hit the web service again for details. Thus, while the status message is informative, you only get a GUID for detail in the initial error. This is not a bad thing, but it leads to difficulties when putting together your excuses to the user—particularly when WCF doesn’t make it easy to get at the details of an untyped FaultException.

Simple as That

From here, everything is pretty much the same. You have your objects in the domain you specified in the “Add Service Reference” dialog given above (GPService in my screenshot). Your proxy object has the methods you can use.

Tags: , , , , ,

Programming

Gratuitous Use of Linq

by Jacob 7. March 2009 06:10

PowerShovel Every now and then I get to doing something just because... well, because I can. These projects usually atrophy before becoming anything usable and serve more as a way to explore and practice than anything else. Usually. My latest tangent actually got to a state where I can let it loose in the wild and it'll probably actual do what it is supposed to do.

BlogEngine.Net

Let me be perfectly clear up front: I don't actually use BlogEngine.Net at all. Anywhere. I'm still a Subtext guy when it comes to blogging software. BlogEngine.Net still lacks critical features and that prevents me from using it as yet (primarily running multiple blogs from a single installation/database).

That said, BlogEngine.Net is a lovely little product with a lot to like about it. The extensibility model is extremely easy to use and flexible. The theming doesn't suck. And its architecture is easy to navigate/understand even while it makes investments in areas I consider likely to payoff.

Data Access

While I like the product, the data access bugs me more than a little. It uses a provider model and includes built-in XML and database providers. These are good things. For flexibility, the database provider uses System.Data.Common and the DbProviderFactory with string-built commands. This structure allows BlogEngine.Net to be able to use any database that has a .Net data provider (including things like MySQL, SQLite, or VistaDB). Incidentally, they downplay (unintentionally?) this feature on their website saying in their FAQ that they support XML and “the SQL Server provider”.

At any rate, here's their SelectPage implementation as an example

public override Page SelectPage(Guid id)
        {
            Page page = new Page();
 
            string connString = ConfigurationManager.ConnectionStrings[connStringName].ConnectionString;
            string providerName = ConfigurationManager.ConnectionStrings[connStringName].ProviderName;
            DbProviderFactory provider = DbProviderFactories.GetFactory(providerName);
 
            using (DbConnection conn = provider.CreateConnection())
            {
                conn.ConnectionString = connString;
 
                using (DbCommand cmd = conn.CreateCommand())
                {
                    string sqlQuery = "SELECT PageID, Title, Description, PageContent, DateCreated, " +
                                        "   DateModified, Keywords, IsPublished, IsFrontPage, Parent, ShowInList " +
                                        "FROM " + tablePrefix + "Pages " +
                                        "WHERE PageID = " + parmPrefix + "id";
 
                    cmd.CommandText = sqlQuery;
                    cmd.CommandType = CommandType.Text;
 
                    DbParameter dpID = provider.CreateParameter();
                    dpID.ParameterName = parmPrefix + "id";
                    dpID.Value = id.ToString();
                    cmd.Parameters.Add(dpID);
 
                    conn.Open();
                    using (DbDataReader rdr = cmd.ExecuteReader())
                    {
                        if (rdr.HasRows)
                        {
                            rdr.Read();
 
                            page.Id = rdr.GetGuid(0);
                            page.Title = rdr.GetString(1);
                            page.Content = rdr.GetString(3);
                            if (!rdr.IsDBNull(2))
                                page.Description = rdr.GetString(2);
                            if (!rdr.IsDBNull(4))
                                page.DateCreated = rdr.GetDateTime(4);
                            if (!rdr.IsDBNull(5))
                                page.DateModified = rdr.GetDateTime(5);
                            if (!rdr.IsDBNull(6))
                                page.Keywords = rdr.GetString(6);
                            if (!rdr.IsDBNull(7))
                                page.IsPublished = rdr.GetBoolean(7);
                            if (!rdr.IsDBNull(8))
                                page.IsFrontPage = rdr.GetBoolean(8);
                            if (!rdr.IsDBNull(9))
                                page.Parent = rdr.GetGuid(9);
                            if (!rdr.IsDBNull(10))
                                page.ShowInList = rdr.GetBoolean(10);
                        }
                    }
                }
            }
 
            return page;
        }

I want to reiterate that I'm not ripping on their choice to use this data access methodology. If you want the flexibility to use any .Net supported data provider without a third-party dependency, this how you get it. You could optimize some of the stringiness, but with trade-offs.

Linq, Linq, Baby

That said, I don't mind being tied to SQL Server and if I'm going to muck with the data layer (like, say, if I'm going to attempt multiple blogs from a single application instance) I want something simple that I can use without all that extra goo. I looked for any hint that this might have been done already, but I couldn't find anything. It looks like I'm the only one with this particular manifestation of brain damage, however.

Since configurable table prefixes are a desirable feature and since that's easier to do in Linq to SQL I figured it'd be best to go that route. It was good to get the table prefix stuff into a working project and have it work about as I expected it to.

Implementing the BlogEngine.Net blog provider turns out to be pretty easy in Linq. Ditto the Role and Membership providers. I tried to stay as close as possible to the DbBlogProvider. Even so, I found that some of the admin components are picky enough that even little things could bite me (the category editing page blows up if you leave category descriptions null, for example).

For compare and contrast, here's my SelectPage method using Linq to SQL

public override Page SelectPage(Guid id)
{
    Page page = null;
    using (Data.BlogDataContext context = getNewContext())
    {
        Data.Page pageData = (from p in context.Pages
                              where p.PageID == id
                              select p).FirstOrDefault();
        if (pageData != null)
        {
            page = new Page()
            {
                Id = pageData.PageID,
                Title = pageData.Title,
                Description = pageData.Description,
                Content = pageData.PageContent,
                Keywords = pageData.Keywords,
                DateCreated = pageData.DateCreated.HasValue ? pageData.DateCreated.Value : DateTime.MinValue,
                DateModified = pageData.DateModified.HasValue ? pageData.DateModified.Value : DateTime.MinValue,
                IsPublished = pageData.IsPublished.HasValue ? pageData.IsPublished.Value : false,
                IsFrontPage = pageData.IsFrontPage.HasValue ? pageData.IsFrontPage.Value : false,
                Parent = pageData.Parent.HasValue ? pageData.Parent.Value : Guid.Empty,
                ShowInList = pageData.ShowInList.HasValue ? pageData.ShowInList.Value : false
            };
        }
    }
    return page;
}

BlogEngine.Net for SQL Server

So I got the thing working and thought I'd open it for “the community”. Since BlogEngine.Net is on CodePlex, that was a natural choice. Anyone sharing my peculiar proclivity is invited to head on over, take a poke and let me know what can change for the better. Or better yet, submit a patch. Or better, better yet, join the project. (also: CodePlex's recent transparent svn compatibility is awesome! When did that happen?)

If you do poke at the project, some forewarning. First, I don't have unit tests for this and don't plan on any (I'm not against using them, I just don't want the headache of creating them myself). Data access is more in the realm of “integration testing”, so I'm not sure there's much you can really do that's actually useful. It might be different if BlogEngine.Net had a suite of tests I could use to validate my providers against, but...

Second, I haven't gone out of my way to do a lot of commenting. This is deliberate. These methods are short, should be self-explanatory, and since they should be hidden behind a provider I didn't even bother with the typical XDoc stuff. Anything directly accessing them such that intellisense comes to bear is doing something wrong...

Room to Improve

In the end, this is the ground-work to remove an (admittedly trivial) barrier in working with BlogEngine.Net. I hope to help move as much as possible into the database in order to support things like multi-blog configurations. BlogEngine.Net hasn't been very disciplined in its storage layer and XML really is the default medium. Things like referrer tracking don't use a provider at all so you really can't (yet) get away from XML files in your App_Data directory (and hence, read/write permission configuration). Since I want that to change, I'm going to see if I can't get that going a bit.

Things to do
  • Decide on a database update methodology (so far, I'm using the already-existing tables and hence piggy-backing on the BlogEngine.Net scripts)
  • Replace the built-in ReferrerModule (use/create provider model access for referrers?)
  • Comb the project for any other errant XML dependencies
  • Work on multi-blogging configuration

Tags: , , , , , ,

Programming

So You Think You're An Admin?

by Jacob 14. February 2009 22:56

Access Button I had an interesting problem crop up trying to run my own application this week. We have a routine that uses an excel spreadsheet to import orders into Dynamics GP that includes some twists that aren't handled well by Integration Manager. Since the application runs from the network (using ClickOnce) and because these orders can be substantial and represent a commitment of corporate resources, we want some control over who can run them. Specifically, we use Active Directory group membership with hard-coded/defined groups.

One of the groups I want to allow is Domain Admins. And yes, this is a kludge. All three members of our small IT shop are Domain Admins—mainly so that we can act as backup when the others are unavailable. It's a handy kludge, though, so lump it. Unfortunately, when running from my machine (running Vista), the user token being used to check Identity.IsInRole() wasn't admitting that I am, in fact, in the Domain Admins domain group

This is, by the way, the first I've run into an inversion of Works on My Machine™.
 works-on-my-machine-starburst-not

The Problem

It wasn't terribly difficult to figure out what was wrong. The key to the problem is that Vista UAC (which I actually rather like because I want to know when programs undertake certain activities) creates a “split token” when you login using an account with admin privileges. The user actually runs using the filtered token that removes the dangerous things and only elevates (with user notification and approval) when those privileges are actually needed.

So when I asked WindowsIdentity.IsInRole("COMPANY\Domain Admins") it told me that it's never heard of that role and certainly I'm not a member of it. This was disconcerting.

Now the problem goes away if you start Visual Studio with “Run as Administrator” or start an application with a shortcut with that setting. Which works fine (not great) while developing (if you remember to start VS as administrator) but eventually I got tired of it and sometimes I want to run the deployed app from my box. There's just one small hitch. Remember that I mentioned that we deploy the app to the network using ClickOnce? It turns out that there's no good way to start a ClickOnce app with elevated privileges. Googling around (and even checking Stack Overflow) I found some people who wrote what were essentially batch files or ran services that could then be used to elevate processes either to run ClickOnce apps or to allow ClickOnce apps to do stuff that requires elevation. But really, that's a lot of hassle for something I just knew had to be simpler.

The Solution

After beating my head on the problem for a bit, I eventually took a step back and asked myself that crucial dev question: “What am I actually trying to accomplish here?” I need to remind myself to do that sooner when I find myself “brought to Point Non Plus” (as Georgette Heyer's characters might say). It turns out to be a good question and one that led to the “Duh” moment I share with you now.

Since I'm not actually doing anything that requires admin privileges, going for process elevation is a complete waste of time. All I really want to know is if the current user is part of a specific Active Directory group. Didn't I see something about .Net Framework 3.5 and managed domain objects? Why yes! Yes I did!

The nifty little buggers are in System.DirectoryServices.AccountManagement and if you do anything with Active Directory domains you owe it to yourself to give this namespace a once over. Here's what I ended up with:

bool isAllowed = false;
WindowsIdentity wi = WindowsIdentity.GetCurrent();
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "COMPANY"))
{
    UserPrincipal up = UserPrincipal.FindByIdentity(pc, wi.Name);
    GroupPrincipal gp = GroupPrincipal.FindByIdentity(pc, "Domain Admins");
    if (up.IsMemberOf(gp))
        isAllowed = true;
}

This worked right out of the chute. Well, getting the right AD group membership didn't want to work when using the Principal.IsMemberOf(PrincipalContext, IdentityType, string) overload, but pulling down the actual GroupPrincipal looks cleaner anyway, I find.

Tags: , , , ,

Programming

Changing Table Names in an OR/M

by Jacob 27. August 2008 11:33

Empty Sign I spent some quality time googling this and even went and asked the nascent Stack Overflow community and didn't come up with a satisfactory answer. Being the intrepid sort, I opened up a test project and started poking around, compiling information from a number of sources and playing until I got something that worked. For your amusement and/or edification, I'll document what I found.

What I Want to Do

The basic scenario is that many typical “commodity” web applications use databases to store their information. Since most web hosting services come with a single database but charge extra for additional databases, it is common for web-type products to add identifier text to their table and stored procedure names*. The .Net blog software I sometimes contribute to, Subtext, is an example. Take the table I added to hold tags associated to posts a while back, “subtext_Tag”. Using the “subtext_” prefix means that we won't run into naming collisions on our tables if someone has a wiki or forum application that also contains a table for tag entities named “Tag”.

 

* And yes, as one user on Stack Overflow suggested, you could use schema as a differentiator so we could as easily have used a “subtext” schema and our tables would be “subtext.Tag” instead of “dbo.Tag”. That solution is much trickier to setup than a simple table naming convention, though, so I think I prefer the table prefix for now.

 

The .Net Way

While I was initial open to any .Net OR/M the fact of the matter is that the only one I know anything about is SubSonic. Now I like SubSonic a lot, but their tools are geared towards code generation and I couldn't find a handle into runtime manipulation of table names (such might exist, but I was unable to find it).

Since I'm even less familiar with the other third party .Net OR/M data tools (like LLBLGen or NHibernate) and nobody on Stack Overflow (who tend to be knowledgeable about these things) spoke up, I decided to check out what it would take to monkey with the table names at runtime using stuff I have actually used. Namely the Entity Framework and LINQ to SQL. It turns out to be possible in either, though I have to admit to being surprised that it is easier in LINQ to SQL than in EF.

My Test Database

To keep things simple, I created a test database. Since I am a highly creative professional, I named it “Test” and created a table there named “TestTable”.

Test Schema 

LINQ to SQL

Instantiating a new DataContext object in LINQ to SQL includes a constructor that allows you to feed in a MappingSource derivative. By default, L2S uses an attribute mapping object that pulls the metadata from attributes on your classes—in this case the “Name” property of a TableAttribute.

[Table(Name="dbo.TestTable")]
public partial class TestTable : INotifyPropertyChanging, INotifyPropertyChanged
 

Since attributes are immutable at runtime, using the default isn't an option here. Fortunately there's an XmlMappingSource object that can use an XML file (or fragment) to do what I need. Unfortunately, generating an initial mapping file is a touch cumbersome and requires use of the SQLMetal.exe tool provided with Visual Studio.

Here's how (after adding a TestLINQ.dbml file and dragging the table onto it—pre-name-change of course):

  1. I opened the VS Command Prompt (it's in a Visual Studio Tools folder in the start menu).
  2. Changed the directory to my project.
  3. Entered the command “sqlmetal /map:TestLINQ.map /code TestLINQ.dbml”. This generates a TestLINQ.map file.
  4. Right-clicked on the generated file in my project and selected “Include in Project”.
  5. Set the file's “Copy to Output Directory” property to “Copy Always”.

The mapping file is pretty simple. The relevant bit is the Name attribute of the Table element:

<Table Name="dbo.test_TestTable" Member="TestTables">
  <Type Name="LINQ.TestTable">
    <Column Name="TestId" Member="TestId" Storage="_TestId" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
    <Column Name="TestOne" Member="TestOne" Storage="_TestOne" DbType="VarChar(50)" />
    <Column Name="TestTwo" Member="TestTwo" Storage="_TestTwo" DbType="VarChar(50)" />
  </Type>
</Table>
 

Make sure the name is what you want it to be and you're golden. Here's the code I used to test it out after the name change.

XmlMappingSource source = XmlMappingSource.FromUrl("TestLINQ.map");
using (LINQ.TestLINQDataContext context = new LINQ.TestLINQDataContext(Properties.Settings.Default.TestConnectionString, source))
{
    LINQ.TestTable table = new LINQ.TestTable()
    {
        TestOne = "firstLINQ",
        TestTwo = "secondLINQ"
    };
    context.TestTables.InsertOnSubmit(table);
    context.SubmitChanges();
 
    table.TestOne = "thirdLINQ";
    context.SubmitChanges();
 
    context.TestTables.DeleteOnSubmit(table);
    context.SubmitChanges();
}
 

XmlMappingSource even has a .FromXml() method that will create your map from an Xml fragment string.

Entity Framework

As I mentioned before this one is harder. This is a surprise because EF is ostensibly created to make it easier to keep your object definitions separate from your storage definitions. The reason it isn't easier is understandable once you realize that EF is made to be highly configurable and thus its definition files are much more complex than the L2S mapping.

The first problem with EF, though, is that the documentation is schizophrenic. Also confusing. That's because MS rolled the original three configuration files into the .edmx definition file so references on the web imply that those files are easily seen and edited. Even more confusing is that EF actually still uses the .csdl, .ssdl, and .msl files at runtime—it just generates those files from the .edmx and either packs them in the assembly as a resource (by default) or as files in your output directory.

Well, to monkey with the tables at runtime, you have to have access to the configuration files. To do so you need to change the default in the “Metadata Artifact Processing” property of your ConceptualEntityModel and rebuild the project. That'll put perfectly good .csdl, .ssdl, and .msl files in your output bin directory (You don't have to leave it at "Copy to Output Directory" once you have saved these files off for your own use and abuse.)

EF did another funky thing by deviating from the norm in what it pulls from the connection string that you feed it. If you look at your EF connection string in app.config (or web.config) you'll see something like this:

<add name="TestEntities" 
connectionString="metadata=res://*/TestEF.csdl|res://*/TestEF.ssdl|res://*/TestEF.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=localhost;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=True&quot;"
providerName="System.Data.EntityClient" />
 

Notice that there's a “provider connection string” embedded in the connectionString attribute—that's the “normal” connection string that tells EF what database to use for storage. Also notice that the metadata property tells EF where to go for those configuration files (in this case, it's telling EF to look in the assembly for resources TestEF.csdl, TestEF.ssdl and Test.msl).

Armed with this information, I was able to add a set of generated config files to the project stolen from those generated in the output directory. Once there, you have to edit the storage file and the mapping file to use the altered names. The table name used by EF is taken from the Name attribute of the EntitySet element in the .ssdl file. This is unfortunate because the Name attribute is a reference used by things like associations. Which means that you have to make sure you alter all the references to that EntitySet as well (fortunately, these are generally referenced using an EntitySet attribute on the relevant association and thus are relatively easy to find).

<EntitySet Name="test_TestTable" EntityType="TestModel.Store.TestTable" store:Type="Tables" Schema="dbo" />
 

It's not necessary to alter the EntityType, though I found that if you alter it consistently across the file it will work if you do.

The mapping configuration in the .msl file just has to be updated so that the objects use the correct EntitySet for storage.

<EntitySetMapping Name="TestTable">
  <EntityTypeMapping TypeName="IsTypeOf(TestEF.TestTable)">
    <MappingFragment StoreEntitySet="test_TestTable">
      <ScalarProperty Name="TestId" ColumnName="TestId" />
      <ScalarProperty Name="TestOne" ColumnName="TestOne" />
      <ScalarProperty Name="TestTwo" ColumnName="TestTwo" />
    </MappingFragment>
  </EntityTypeMapping>
</EntitySetMapping>
 

Once those changes are made your code will work with the altered table name, though you need to alter the connection string to look for the correct configuration files. Here's the final code I used to test it out.

string connection = "metadata=TestEF.csdl|TestEF.ssdl|TestEF.msl;provider=System.Data.SqlClient;provider connection string=\"Data Source=localhost;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=True\"";
using (TestEntities entities = new TestEntities(connection))
{
    TestTable table = new TestTable()
    {
        TestOne = "firstEF",
        TestTwo = "secondEF"
    };
    entities.AddToTestTable(table);
    entities.SaveChanges();
 
    table.TestOne = "thirdEF";
    entities.SaveChanges();
 
    entities.DeleteObject(table);
    entities.SaveChanges();
}

So Which is Better?

Well, that depends on what you want to accomplish but then, doesn't it always? For me, the LINQ to SQL solution is much cleaner because I'm simply not going to use all the other goo that the Enterprise Framework includes. Plus, the LINQ to SQL solution can use an XML fragment so I can bury that mapping piece wherever I want to, including in inline code. EF requires a file reference so those files have to be either in the assembly resources or on the file system. EF also allows you to leverage Asp.Net Data Services but that's a topic for another post entirely...

Tags: , , , , , , ,

Programming

Getting at the Details

by Jacob 28. January 2008 18:56

wcf This should be pretty short, but I could not find this information anywhere so here’s something that will save you hours of frustration if you ever run into the same situation.

WCF Myopia

Windows Communication Foundation is nice and all, but it suffers from a really large myopia: it tends to assume that you control both the service and the client. This is a stupid assumption, but try finding information for a situation where a service throws a FaultException and you want to get at the details of the exception and you’ll see what I mean.

The Setup

Dynamics Great Plains Web Services allow you to manipulate common business objects. I’ve recently been trying to use WCF in communicating with those services and it has been an adventure. A recent post detailed my solution for customized warehousing. What I left out last time is moving to WCF’s error handling seriously messed up my error condition reporting.

GP Web Services sends a fault for both warnings and errors with a relatively informative status message and a detail section that is just a GUID that identifies the fault in the server log. The theory is that if you want more details, you can query the service again to pull further information in.

The Problem

All the solutions I could find for getting fault details out of WCF involve creating contracts on the service that the WCF client can read to create strongly typed objects in the generic version of FaultException. Well, I can’t control the web service to do this. I could probably have hacked or wrapped the wsdl for the service but who wants that much low-level headache? And before you go there, the faults that the GP Web Service sends didn’t trigger on what was billed as the default exception with generic details:

catch (FaultException<ExceptionDetail> ex)
{
}

Unfortunately, the regular FaultException doesn’t expose a Detail property or methods that you can use to get at any details sent. This is a stupid oversight. I mean, yeah, anything can be in there, but whatever is there has to be XML serializable pretty much by definition. They couldn’t throw us a bone and expose it as a string or XML fragment?

The Solution

So what do you do with WCF if you cannot change the service but need to get at a fault detail without a specific .Net-friendly contract?

The solution I came up with is actually extremely easy once I realized I had to go through a couple of classes to get there (don’t ask how long this took to ferret out).

  • Snag the message itself using MessageFault.
  • Get an XmlDictionaryReader that can get at the contents of the details sent.
  • Read the detail content.

Since I know that the detail content is always a GUID, this is really simple:

catch (FaultException soapEx)
{
    MessageFault mf = soapEx.CreateMessageFault();
    if (mf.HasDetail)
    {
        XmlDictionaryReader reader = mf.GetReaderAtDetailContents();
        Guid g = reader.ReadContentAsGuid();
    }
}

Amazingly, this works.

Tags: , , , ,

Programming

TDD Proven Effective! Or is it?

by Jacob 16. January 2008 21:33

skeptic One of the most useful classes I took in college was an introductory statistics class that was intended to discourage Poli. Sci. majors from continuing in their course of study (I was one at the time). The interesting thing about the class is that it included study and analysis of research as a core part of the class. We learned how to put a study together, how to develop controls, and different formulas used to confirm whether we had statistically significant results or not.

So when Phil Haack announced that Research Supports the Effectiveness of TDD I was more than a little interested in seeing what the linked report actually contained. Phil quotes from the abstract.

We found that test-first students on average wrote more tests and, in turn, students who wrote more tests tended to be more productive. We also observed that the minimum quality increased linearly with the number of programmer tests, independent of the development strategy employed.

Phil has obviously read the rest of the report and provides his favorite pieces that seem to do as his title suggests. One of the things I worry about when I see things supporting the latest and greatest software development practices, however, is a strong tendency towards confirmation bias—of looking for confirmation of current theories and overlooking counter-indicators.

So, being the curious type and since TDD is something I’m keeping an eye on to see if its something I might want to adopt myself some day, I went into the report.

The Report

The report itself is remarkably well thought-out. Though their sample size is small (a mere 24 students completed the exercise), it looks like they were very careful to make their study relevant to how software development actually works. They created tasks that built on and eventually altered previous functionality, communicated requirements as user stories (meaning the participants had to design the projects, including class layout and interfaces etc.) and the administrators had a large suite of black-box tests to confirm functional implementation (i.e. tested actual functionality delivered and ignored the structure, design, and tests).

It’s important to note that both the TDD and non-TDD groups created and used unit tests. Indeed, the groups are described as "Test First" and "Test Last" throughout the paper.

The authors include their data and results and even mention counter indications (as good report authors should). This is a good thing. If you have a turn for that kind of thing, I recommend reading through it. It isn’t long, particularly as these things go.

Lies, Damn Lies, and . . .

Unfortunately, the authors disappoint me when I compare the abstract to the data in the paper. Indeed, the report becomes an example of why I don’t give much credence to abstracts anymore. Clever academics have long used abstracts to support the conclusions they desire and this seems to be one such. If you’re careful (as these authors are) you can even do it without actually lying. Each of the following statements from the abstract is true:

  • The test-first students on average wrote more tests.
  • Students who wrote more tests tended to be more productive.
  • The minimum quality increased linearly with the number of tests.

Note that only the first is TDD-"Test First"-specific. The other two stand alone, though most readers won’t catch that. Here are some equally true statements based on the data (pages 9 and 10 of the report if you want to read along with me in your book):

  • The control group (non-TDD or "Test Last") had higher quality in every dimension—they had higher floor, ceiling, mean, and median quality.
  • The control group produced higher quality with consistently fewer tests.
  • Quality was better correlated to number of tests for the TDD group (an interesting point of differentiation that I’m not sure the authors caught).
  • The control group’s productivity was highly predictable as a function of number of tests and had a stronger correlation than the TDD group.

So TDD’s relationship to quality is problematic at best. Its relationship to productivity is more interesting. I hope there’s a follow-up study because the productivity numbers simply don’t add up very well to me. There is an undeniable correlation between productivity and the number of tests, but that correlation is actually stronger in the non-TDD group (which had a single outlier compared to roughly half of the TDD group being outside the 95% band).

My Interpretation

One of the things my statistics class pounded home is that correlation doesn’t equal causation. That’s particularly important to remember in reports like this one. Indeed, I think causation is a real problem with this study, at least insofar as "proving TDD effective" is concerned. It actually serves to undercut many common TDD claims for superiority.

Productivity is an example where causality is far from certain. It makes sense to me that more productive programmers write more tests if only because productive programmers feel like they have the time to do things the way they know they should. Even with "Test First" the emotional effect of "being productive" is going to have an impact on the number of tests you create before moving on to the next phase. (note: you have to be careful here because the study measures tests per functional unit so it’s not that productive programmers are creating more tests over all, but rather that they’re creating more tests per functional unit.) Frankly, I think it’s natural to feel (and respond to) this effect even without a boss hanging over your shoulder waiting to evaluate your work.

In other words, you create more tests because you are a productive programmer rather than being a more productive programmer because you create more tests. At least, it makes sense to me that it would be so.

Truly problematic, however, are the quality results. That’s simply a disaster if you propound "Test First" as a guarantor of quality. I mean, sure, the number of unit tests per functional unit suggests a minimum quality through increased testing, but that’s only interesting in a situation where minimum quality is important (like, for example, at NASA or in code embedded in medical equipment). The lack of any other correlation here is pretty pronounced any way you care to slice the data. Having a big clump in that upper left quadrant is troubling enough but then having the "Test Last" group almost double your "Test First" group in the over 90% quality range is something that should be noticed and highlighted.

While correlation doesn’t equal causation, the lack of correlation pretty much requires a lack of causation.

Since quality is obviously not related to number of tests (at least in this study), I think it is highly disingenuous to highlight the possible connection between number of tests and minimum quality. While it’s the only positive thing you can say about quality and unit testing, it is hardly the only thing revealed. The lack of correlation between number of tests and quality is incredibly important and of broad general interest whether you are using formal TDD or not.

Thoughts and Wishes

I really wish that the authors had included a third group that did no unit testing. Since they broadened their scope post-facto to make statements about the efficacy of unit testing over all, it would be nice to have a baseline that didn’t do any unit testing yet went through the same process. The study setup was ideal for including such a group as both the productivity and quality measurements were entirely independent of whatever testing was done (or not done).

That said, something occurred to me while reading this study that hasn’t before: quality can be a property of unit tests as well as of production code. That’s a huge duh, but consider what that could mean. For one, it means that I’d love to see a follow-up that considers the quality of the tests themselves. I would expect to see a relationship between the quality of the product and the quality of the tests if only because quality programmers will be competent in both arenas. This might be such a duh, though, that people assume that test quality matches production quality. Still, I wonder if that is actually the case.

More interesting still would be the correlation between "Testing First" and unit test quality. Ask yourself this, which unit tests are going to be better, the ones created before you’ve implemented the functionality or those created after? Or, considered another way, at which point do you have a better understanding of the problem domain, before implementing it or after? If you’re like me, you understand the problem better after implementing it and can thus create more relevant tests after the fact than before. I know better where the important edge-cases are and how to expose them and that could mean that my tests are more robust.

And yes, I realize after the fact that this is an inversion of section 6.2 which is quoted at length by Phil. I wish the authors had spent as much time in section 6.1 explaining reasons for the quality results instead of what looks to me like an attempt to excuse them. That’s the problem with confirmation bias, those results that run counter to expectations aren’t as interesting and receive cursory exploration at best.

Anyway, without question, testing first leads to having more tests per functional unit. The question is if this is valuable. This study would seem to indicate that this is probably not the case, at least if quality is your intended gain. But then, I’m not that surprised that number of tests doesn’t correspond to quality just as I’m not surprised that the number of lines of code doesn’t correspond to productivity.

Tags: , , ,

Programming

Custom Dynamics Warehousing

by Jacob 4. January 2008 03:25

Warehouse Prior developers and others who should have known better at my company decided many years ago that our warehouse people simply could not do their jobs unless we unleashed the ability for them to use multiple "bin" locations for each item. Now, strictly speaking, this was not really the case, because our needs simply aren’t that complex. We manufacture reading glasses so we don’t need inventory aging and all the warehouse people really needed were different sites to separate receiving from QA from shipping. In other words, we have product staging, but each stage is physically as well as conceptually separate and each item in each stage exists really only in a single location.

As in so many real-life situations, however, there is enough cruft built up around the current process that it is simply not worth expending the political capital to change. Which means that I’m stuck trying to automate order fulfillment for multiple bins with multiple locations.

The Tools

Now, Dynamics has a number of interesting tools and my company long ago purchased all the neat bits that I need to use them all. The typical default tool that people use to customize Dynamics is a hoary old beast called "Dexterity". I’m sure that the name once fit, but in an honest world the name would have long ago been changed to "Decrepit". I’ll use Dexterity if I absolutely have to, but I’ll avoid it more assiduously than, say, rabid ninja porcupines.

Fortunately, Microsoft, feeling their oats with cool technologies like the .Net framework and web services and all, has released developer tools that includes a .Net toolset to create addins and a set of web services that can be used to perform most business tasks. I also have the Database schema if I have to resort to direct table access, but I tend to avoid that if I can (though not at the rabid ninja porcupine level).

There are some important things to know about these tools.

.Net Integration

The .Net integration can hook into Dynamics forms and events and play with field values. You can also create your own forms and hook them into a special "Extras" menu when a given Dynamics form is active (or have them pop up during an existing event handler I suppose).

They didn’t skimp on the event handles, either. Each field has before and after change and click events, buttons have before and after click events, and forms and windows have activate, open, close, and print (all with before and after handles).

Web Services

The web services are more complicated to set up and are best off installed on your database server. They use some of the older E-Connect com objects in the background which makes it a pain to set up, but they’re pretty useful once you do.

The web services expose a large number of create, update, get, and delete methods for all the stuff you’d expect—customers, invoices, sales orders, purchase orders, that kind of thing. They can be a bit temperamental when it comes to setting up the policies for the operations, but once you’ve got them working, they work well.

The Problem

When we receive orders from Nordstrom, we have two factors that complicate our work flow. The main difficulty is that printing our shipping and invoice labels is a fiddly piece of work that can result in fines from Nordstrom if we get it wrong. Some long forgotten "genius" managed to get them formatted correctly using our EDI software. This is the software that is still using FoxPro as their interface and data storage. It’s a hunk of junk, but it works. More or less.

Unfortunately, for those labels to print correctly, the EDI software has to do the Dynamics import itself (instead of using my cool new EDI processing service). If the EDI software doesn’t do the import, it doesn’t know all the right invoice ids and associated stuff to print on those labels.

Also unfortunately, the EDI program knows nothing about our multiple bin fulfillment requirements. Adding to the pain is the fact that Dynamics cannot handle automated fulfillment for multiple bins on its own—particularly when certain sites are to be allowed, others avoided, and one or two bins excluded manually (yeah, we did all that to ourselves).

Since we get orders for hundreds of items at a time, this largely manual process for Nordstrom is taking a single very kind and patient person (who may or may not ever read this) about four hours. The bulk of this time is manually assigning bins to all the items on each order in the batch.

My Solution

We’ve been working on making all of our customizations accessible within Dynamics, so it’s best if an automation solution kicks off from there. The .Net tools make this part relatively easy.

public class OrderFulfillmentAddIn : IDexterityAddIn
{
    // IDexterityAddIn interface
 
    public void Initialize()
    {
        Dynamics.Forms.SopBatchEntry.AddMenuHandler(AddOrderFulfillmentMenu, "Allocate Bins");
    }
 
 
    static FulfillmentForm form = new FulfillmentForm();
    static void AddOrderFulfillmentMenu(object sender, EventArgs e)
    {
        if (form == null || form.Created == false)
        {
            form = new FulfillmentForm();
        }
 
        form.BatchId = Dynamics.Forms.SopBatchEntry.SopBatchEntry.BatchNumber.Value;
        form.Show();
        form.Activate();
    }
}

This inserts an "Allocate Bins" menu item on the extras menu when the Batch Entry form is open. The menu item opens my own form (which isn’t any great shakes). However, because the .Net integration tools are only able to handle user-initiated events and already open form fields actual bin allocation has to happen some other way.

Yeah, I chose to use the web services.

Complicating this further, the web services don’t expose anything that will give me all the bins that contain a specific item so I’ll need to pull that information from the tables directly. Fortunately, Dynamics has a handy Resource Descriptions tool that helps here.

Dynamics Resources 

It can take getting used to, but it tells me that I need table IV00112.

ItemSiteBinMaster Table

With that information, this becomes a simple Linq query.

You Want Tests With That?

Now, this is where things get tricky because I like to test my code once or twice before deployment. Oh yeah, and sometimes I’ve even been known to create unit tests and such. Which means that I have to be able to run this in both our test company as well as the live one.

Since Dynamics keeps each company in a separate database, this means that I need to know both the Company Id to feed the web service and the database name for the Linq query. This actually held me up for quite a while. My first impulse (that actually worked out relatively well) was to put both into the app config.

Linq already has it’s connection string there so I’d just need to add a CompanyId setting.

<connectionStrings>
  <add name="Integration.GPWebServices.Properties.Settings.myConnectionString"
      connectionString="Data Source=MyServer;Initial Catalog=MyDB;Integrated Security=True"
      providerName="System.Data.SqlClient" />
</connectionStrings>
<applicationSettings>
  <FulfillOrders.Properties.Settings>
    <setting name="CompanyId" serializeAs="String">
      <value>2</value>
    </setting>
  </FulfillOrders.Properties.Settings>

This worked well on the surface, but it was kind of clunky when it came to developer testing where I went into the test company within Dynamics and created batches and orders with the Visual Studio debugger attached.

That’s when the apple fell out of the sky and I realized that, duh, I have the Dynamics environment right here already. It took me a while to figure out that the database name is held in the "IntercompanyId" global property, but once I’d tracked that down, we were golden.

Orders.FulfillByBatch(BatchId
    , (int)Dynamics.Globals.CompanyId.Value
    , Dynamics.Globals.IntercompanyId.Value);

The unit test can still pull it from the config file, but my form can simply use the company currently open.

Doing the Work

I’ll give you my worker class just for kicks here. Note that allocating bins for hundreds of items takes long enough that we definitely need UI feedback (we love thee oh progress bar). That means that much of the class real estate is taken up with events that help keep my users from freaking out.

using System;
using System.Collections.Generic;
using System.Text;
using Integration.GPWebServices;
 
namespace FulfillOrders
{
    public static class Orders
    {
        public static event EventHandler OrderProcessed;
        public static event EventHandler<BeginFulfillingEventArgs> BeginFulfillment;
        public static void FulfillByBatch(string OriginalBatchId, string NewBatchId, DateTime CancelDate, int CompanyId, string CompanyDb)
        {
            Utility.DataBase = CompanyDb;
            Utility.CompanyId = CompanyId;
            SalesDocumentSummary.BeginLoadingSummary += new EventHandler<BeginSummaryLoadEventArgs>(SalesDocumentSummary_BeginLoadingSummary);
            SalesDocumentSummary.DocumentLoaded += new EventHandler(SalesDocumentSummary_DocumentLoaded);
            List<SalesDocument> orders = SalesDocumentSummary.GetSalesDocumentsByBatch(OriginalBatchId);
 
            if (orders.Count > 0)
            {
                OnBeginFulfillment(orders.Count);
                foreach (SalesDocument order in orders)
                {
                    order.BatchId = NewBatchId;
                    order.Date = order.UserDate1 = order.UserDate2 = order.ReqShipDate = CancelDate.Date;
                    order.AllocateBins();
                    order.CommitDocument();
                    OnOrderProcessed();
                }
            }
        }
 
        static void SalesDocumentSummary_DocumentLoaded(object sender, EventArgs e)
        {
            OnOrderProcessed();
        }
 
        static void SalesDocumentSummary_BeginLoadingSummary(object sender, BeginSummaryLoadEventArgs e)
        {
            OnBeginFulfillment(e.TotalOrders);
        }
 
        private static void OnBeginFulfillment(int total)
        {
            EventHandler<BeginFulfillingEventArgs> begin = BeginFulfillment;
            if (begin != null)
            {
                begin(null, new BeginFulfillingEventArgs(total));
            }
        }
 
        private static void OnOrderProcessed()
        {
            EventHandler processed = OrderProcessed;
            if (processed != null)
            {
                processed(null, new EventArgs());
            }
        }
    }
    public class BeginFulfillingEventArgs : EventArgs
    {
        internal BeginFulfillingEventArgs(int total)
        {
            Total = total;
        }
        public int Total { get; set; }
    }
}

Wrapping it Up

Integration.GPWebServices is my own library and it uses shell classes to simplify working with the GP web service objects (including the static Utility class used to store the Company Id and Database).

Wrapper Classes

Enlarge to see it in full, including the nifty methods used to put the web service objects together.

Isolating the web services also means that the WCF objects are fully encapsulated and I don’t have to worry about referencing those libraries or be concerned when they cross app boundaries. It does mean that I have to be careful with the app.config files that contain my WCF endpoint connectivity, however.

Integration.GPWebServices probably deserves it’s own post. It’s certainly large enough and it is getting relatively well polished at this point. Also, it has become great personal example of the benefits of ruthless refactoring in a case where the initial design was solid to begin with.

Tags: , , , , ,

Programming

My DI Failure

by Jacob 31. December 2007 22:10

Work Stress A couple of months ago, I mentioned a project I had coming up that might benefit from using Dependency Injection. The use-case on this is simple enough. We receive all of our EDI text files to a specific directory. Those files need to be processed into standard, internal temporary tables. Since each of our vendors uses the EDIFACT X12 fields differently, we need to customize parsing the files according to vendor.

This is actually one of the larger projects I undertake at our small in-house development shop so it needs more up-front architecture than most and it seemed like a good time to test my understanding of Dependency Injection and maybe develop some familiarity with a DI framework. For one, I know in advance that I’m going to want an interface for my EDI processors and I know for certain sure that I’m going to add file processors in the future.

Those of you familiar with DI might be able to see the ripples around the reefs I eventually cracked up on from here.

The Basic Solution

The solution isn’t that complex in theory.

  • I’ll want a service that monitors a given directory for new files with a given file mask. Both the directory and file mask are in a config file that reloads when altered (all handled by built-in .Net processes).
  • I’ll want a simple interface that the service can share with implementing classes.
  • I’ll want classes to process the files that are found.
EDI Solution

See, simple.

Failure

As much as this looked like a DI shoo-in at the time, it turns out to be a bad fit. I figured this out after digging through the Castle Windsor documentation trying to find a way to iterate through all the registered objects that implement a specific interface. I was frustrated at not being able to find one. It took me a while to realize that I was trying to bend the framework to do something it hadn’t been intended to do—invoke methods on arbitrary registered objects. A DI framework is supposed to handle injecting a class that implements a given interface into the class that depends on that interface.

Indeed, DI frameworks in general are meant to abstract and centralize the complexities of implementation object selection. As such, they are built around the assumption that the run-time already knows what class it wants to use for a given interface (either in a configuration file or determined programmatically at run-time).

It took me a while to figure out that my use of the pattern itself is at fault in this mismatch. I’m not injecting a dependency at all. My service isn’t dependent on classes that implement IEDIProcess, it just determines if there are any available that apply to a newly created file and invoke it correctly if there are. In other words, I’ve already architected around the conceptual dependency here and Dependency Injection is an additional complexity with no purpose.

If DI should be used anywhere in this solution, it’s in the processes themselves to separate out the data updates. And, as I’ve mentioned before, there’s no payoff for me in worrying about my data dependencies.

Busted

So it turns out that shoe-horning a design pattern just to play with it didn’t work out too well. Who’d have thought that could happen?

The broader take-away, at least for me, is that you look for a solution/pattern/tool based on the attributes of your problem. In other words the problem comes first and the solution flows from it. Having a pattern looking for a problem to solve is like having a hammer looking for something to fix—both methods are hit or miss.

Tags: , , , , ,

Programming

scruffylookingcatherder.com

Information

    Recent Posts

    Calendar

    <<  September 2010  >>
    MoTuWeThFrSaSu
    303112345
    6789101112
    13141516171819
    20212223242526
    27282930123
    45678910

    View posts in large calendar
    Disclaimer
    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    © Copyright 2010 Scruffy-looking Cat Herder