Enterprise Library



  • Long-time reader, first-time poster here. Be reasonably kind...

    I have to use Enterprise Library 3.1 to connect a .NET 3.5 application to a SQL Server 2005 database. No, I don't have a choice in the matter. I need to run a stored procedure within a transaction as a prepared/executed parameterized query. All very quaint.

    So, the C# code for such a database call looks something like this.

        using (DbConnection connection = LocalInstance.CreateConnection())
        {
            connection.Open();
            using (DbTransaction transaction = connection.BeginTransaction())
            {
                try
                {
                    using (DbCommand command = connection.CreateCommand())
                    {
                        command.CommandText = "SomeInsertStoredProcedure";
                        command.CommandType = CommandType.StoredProcedure;
                        command.Transaction = transaction;
                        command.Prepare();
    
                        foreach (var thing in thingsToAdd)
                        {
                            command.Parameters.Clear();
                            command.Parameters.Add(thing.IntProperty);
                            command.Parameters.Add(thing.BoolProperty);
                            // etc.
                            command.ExecuteNonQuery();
                        }
                    }
                    transaction.Commit();
                }
                catch (Exception)
                {
                    transaction.Rollback();
                    throw;
                }
            }                    
    

    In the above code, LocalInstance is a static property of type Database, and thingsToAdd is a collection of some sort of object. Also, for those unfamiliar with the abomination known as Enterprise Library, command.Parameters.Add takes an object (that is, a parameter of type "object" which is the base type of everything) as a parameter. So, maybe it's going to use ToString() internally or something? Or try some fancy type casting?

    No. The code compiles just fine, but on execution, the first command.Parameters.Add throws an InvalidCastException with the following message:

    The SqlParameterCollection only accepts non-null SqlParameter type objects,
    not Int32 objects.

    So, the parameter can be any object so long as it is of type SqlParameter? Wouldn't it be great if we had a way to specify that in the method signature by doing something like, I don't know, making the parameter type SqlParameter instead of object?

    But wait, there's more. Say I need to run a stored procedure to get some data within a transaction. The following (within the using blocks for connection and transaction) compiles just fine:

        using (DbCommand command = connection.CreateCommand())
        {
            command.CommandText = "SomeSelectStoredProcedure";
            command.CommandType = CommandType.StoredProcedure;
            command.Transaction = transaction;
    
        var whateverCollection =
            LocalInstance.ExecuteDataSet(command)
                         .Tables[0]
                         .AsEnumerable()
                         .Select<DataRow, Whatever>(row =>
                             new Whatever() { WhateverId = row.Field&lt;int&gt;("SomeInt") });
    }
    

    Does it work? Not according to the InvalidOperationException:

    The transaction is either not associated with the current connection or
    has been completed.

    No, I'm pretty sure the transaction I assigned to the command is still running and is associated with the correct connection. Oh, wait, that syntax doesn't work here? Apparently, I'm supposed to leave out command.Transaction = transaction; and use LocalInstance.ExecuteDataSet(command, transaction) here even though assigning the transaction to the command works in every other situation I have used so far.

    From Microsoft's Enterprise Library site, one of the goals of Enterprise Library is:

    Consistency. All Enterprise Library application blocks feature consistent design patterns and implementation approaches.

    Proven practices for predictable results? Really, Microsoft?



  • Can't you convince your boss(es) to let you use ADO.NET instead of EL? The code won't change much and THEN you'll get some consistency.



  • @Renan said:

    Can't you convince your boss(es) to let you use ADO.NET instead of EL? The code won't change much and THEN you'll get some consistency.

    No. No architecture changes during this project (whose completion date keeps slipping). Maybe later, if this project ever gets done. ADO.NET would certainly be an improvement. I'd rather use Entity Framework, which of course has its own WTFs, but Entity Framework has a bad name around here because of some contractors-of-the-month a while back (before I was employed here) that didn't know how to use it correctly. I'll post some of their code here if I get a chance -- it's atrocious, but it's still in the application. Using around eleven Include() calls to pull in most of the database on every stupid query, some of which are needlessly repeated for every row in a grid, is not the key to application performance.

    I just ran across one more Enterprise Library one. Now that I've fixed the SqlParameter thing, on an ExecuteDataSet call, I get this ArgumentNullException:

    Value cannot be null.
    Parameter name: connection
    

    Stack trace:

    at Microsoft.Practices.EnterpriseLibrary.Data.Database.PrepareCommand(DbCommand command, DbConnection connection)
    at Microsoft.Practices.EnterpriseLibrary.Data.Database.PrepareCommand(DbCommand command, DbTransaction transaction)
    at Microsoft.Practices.EnterpriseLibrary.Data.Database.LoadDataSet(DbCommand command, DataSet dataSet, String[] tableNames, DbTransaction transaction)
    at Microsoft.Practices.EnterpriseLibrary.Data.Database.LoadDataSet(DbCommand command, DataSet dataSet, String tableName, DbTransaction transaction)
    at Microsoft.Practices.EnterpriseLibrary.Data.Database.ExecuteDataSet(DbCommand command, DbTransaction transaction)

    So, let me get this straight. Both the transaction and the command were created from the connection, but when you internally call PrepareCommand, you can't figure out what the connection is, so you pass a null? Brillant.

    I guess I'll try setting some Connection parameters and see what happens.



  • The CreateConnection command is part of the Database class because it uses inheritence.  You can get a different connection based on which instance you use.  You can also get multiple connections and use them at different times.  This seems normal to me, and is perfectly industry standard.

    DbCommand.Parameters is simply a type of DbParameterCollection, which was created in .Net 1.1 and is an untyped collection... that's why the "Add" method takes an object.  It's not like they created a brand new method and put Object in there.



  •  Entity Framework really is pretty bad, though... I mean, no out-of-the-box support for enums?  I think MS built out 95% of the functionality and called it a day.



  • @ShatteredArm said:

     Entity Framework really is pretty bad, though... I mean, no out-of-the-box support for enums?  I think MS built out 95% of the functionality and called it a day.

    What do you mean by "no [. . .] support for enums" that makes EF "pretty bad"?


  • @ShatteredArm said:

     Entity Framework really is pretty bad, though... I mean, no out-of-the-box support for enums?  I think MS built out 95% of the functionality and called it a day.

     HUH?? Enterprise Library  != Entity Framework



  • OK, EL can be a pain, but if you wrap it in a base class then you should be fine.

    Also, about the second problem with the linq, I could be wrong but it seems you got trapped in the common miconception of how lambdas work and delayed execution.  By the time you access whateverCollection, the connection is probably closed which is when the lambda gets executed.  This has nothing to do with EL and more to do with the fact that you didn't RTFM.  Again, I could be wrong as I would need the code to test to be sure.



  • @C-Octothorpe said:

    Also, about the second problem with the linq, I could be wrong but it seems you got trapped in the common miconception of how lambdas work and delayed execution.  By the time you access whateverCollection, the connection is probably closed which is when the lambda gets executed.  This has nothing to do with EL and more to do with the fact that you didn't RTFM.  Again, I could be wrong as I would need the code to test to be sure.

    Good catch.


  • @C-Octothorpe said:

    Also, about the second problem with the linq, I could be wrong but it seems you got trapped in the common miconception of how lambdas work and delayed execution.  By the time you access whateverCollection, the connection is probably closed which is when the lambda gets executed.  This has nothing to do with EL and more to do with the fact that you didn't RTFM.  Again, I could be wrong as I would need the code to test to be sure.

    Yes, I was TRWTF on this one. I forgot the .ToList() call to run the query (I've done more than enough Linq to know better too). It had been a long week, and my brain was obviously a bit addled. I had already fixed this one, but I had not gotten around to coming back here to admin my shame.



  • No worries...  I've been burned by this too, and now I can usually spot this issue from orbit.



  • @corgimonster said:

    Consistency. All Enterprise Library application blocks feature consistent design patterns and implementation approaches.
    I see what's happened here! They got Nagesh to write their documentation. Translated into English, it should read:

    "All Enterprise Library applications block [any attempt at] consistency in features, design patterns, implementation or approach"



  • @TheCPUWizard said:

    @ShatteredArm said:

     Entity Framework really is pretty bad, though... I mean, no out-of-the-box support for enums?  I think MS built out 95% of the functionality and called it a day.

     HUH?? Enterprise Library  != Entity Framework

    I had mentioned a preference for using Entity Framework instead of Enterprise Library. He's right, though, no Enum support is an annoyance. Of course, the underlying DB (SQL Server) still doesn't have enumerated types (something even MySQL has had for who knows how long). A quick glance at the SQL Server 2012 preview documentation does not indicate that MS has added enumerated types, either.



  • @corgimonster said:

    I had mentioned a preference for using Entity Framework instead of Enterprise Library. He's right, though, no Enum support is an annoyance. Of course, the underlying DB (SQL Server) still doesn't have enumerated types (something even MySQL has had for who knows how long). A quick glance at the SQL Server 2012 preview documentation does not indicate that MS has added enumerated types, either.
    I've never really seen a need for enums...  I've used enums in my DB schemas in the past, but I enforce the set through a constraint.



  • @corgimonster said:

      He's right, though, no Enum support is an annoyance. Of course, the underlying DB (SQL Server) still doesn't have enumerated types (something even MySQL has had for who knows how long). A quick glance at the SQL Server 2012 preview documentation does not indicate that MS has added enumerated types, either.

     SQLSErver has had support for Enum's (and basically any other CLR type - with some restrictions) for many years.....  http://msdn.microsoft.com/en-us/library/ms254498(v=vs.80).aspx



  • @corgimonster said:

      He's right, though, no Enum support is an annoyance. Of course, the underlying DB (SQL Server) still doesn't have enumerated types (something even MySQL has had for who knows how long). A quick glance at the SQL Server 2012 preview documentation does not indicate that MS has added enumerated types, either.

     SQLSErver has had support for Enum's (and basically any other CLR type - with some restrictions) for many years.....  http://msdn.microsoft.com/en-us/library/ms254498(v=vs.80).aspx



  • @corgimonster said:

      He's right, though, no Enum support is an annoyance. Of course, the underlying DB (SQL Server) still doesn't have enumerated types (something even MySQL has had for who knows how long). A quick glance at the SQL Server 2012 preview documentation does not indicate that MS has added enumerated types, either.

     SQLSErver has had support for Enum's (and basically any other CLR type - with some restrictions) for many years.....  http://msdn.microsoft.com/en-us/library/ms254498(v=vs.80).aspx



  • @TheCPUWizard said:

    @corgimonster said:

      He's right, though, no Enum support is an annoyance. Of course, the underlying DB (SQL Server) still doesn't have enumerated types (something even MySQL has had for who knows how long). A quick glance at the SQL Server 2012 preview documentation does not indicate that MS has added enumerated types, either.

     SQLSErver has had support for Enum's (and basically any other CLR type - with some restrictions) for many years.....  http://msdn.microsoft.com/en-us/library/ms254498(v=vs.80).aspx

    True, but I'd rather not have to resort to the CLR just to have an enumeration. There are other workarounds of course, and I do see that constraints are enforced in my DB code (can't say the same for all the past developers on my current project, though), but I don't think I should need a workaround.



  • @corgimonster said:

    but I don't think I should need a workaround.
    Fair enough.  Just go the old-fashioned way and create a table and a FK.  I have also done this in the past where I will populate the "enum tables" with the set of enums, which are also hard-coded in the code base.  Sure, the real type on your object is an int, but you can easily cast to/from, and if your naming convention is good, its actually quite painless.



  • @C-Octothorpe said:

    Fair enough.  Just go the old-fashioned way and create a table and a FK.  I have also done this in the past where I will populate the "enum tables" with the set of enums, which are also hard-coded in the code base.  Sure, the real type on your object is an int, but you can easily cast to/from, and if your naming convention is good, its actually quite painless.

    That's exactly what I do, since that's the convention on the current project (except that they use SMALLINT for the PK type, so I have followed suit). I cast to Enums in the .NET code, which has unfortunately not been the convention on the current project (some use literal integers, some use constants, some go get the text value from the table and use string literals). No, we don't really have code standards.



  • @corgimonster said:

    No, we don't really have code standards.
    Believe it or not, I have heard that places like this actually exist!  I heard also that there are places where timelines are reasonable, your coworkers are experiences professionals who know their shit, and that naked women dance on your desk while feeding you beer and pizza (though I personally don't believe the first one).



  • @C-Octothorpe said:

    Believe it or not, I have heard that places like this actually exist!  I heard also that there are places where timelines are reasonable, your coworkers are experiences professionals who know their shit, and that naked women dance on your desk while feeding you beer and pizza (though I personally don't believe the first one).

    Knowing which contractor-past probably would have come up with the code standards if we had them, I'm actually kind of glad we don't have any. Of course, one of the current in-house developers says he uses consts instead of Enums for performance reasons, so not all our problems are gone. (Yes, this application has serious performance problems. No, Enums are not contributing to the problem.)

    In all fairness, I do believe my current employer is righting the ship. We no longer have any contractors, and we seem to be slowly moving in the right direction with some things. Unfortunately, most improvements are being put off while we finish this long-overdue project.



  • @corgimonster said:

    @C-Octothorpe said:

    Believe it or not, I have heard that places like this actually exist!  I heard also that there are places where timelines are reasonable, your coworkers are experiences professionals who know their shit, and that naked women dance on your desk while feeding you beer and pizza (though I personally don't believe the first one).

    Knowing which contractor-past probably would have come up with the code standards if we had them, I'm actually kind of glad we don't have any. Of course, one of the current in-house developers says he uses consts instead of Enums for performance reasons, so not all our problems are gone. (Yes, this application has serious performance problems. No, Enums are not contributing to the problem.)

    In all fairness, I do believe my current employer is righting the ship. We no longer have any contractors, and we seem to be slowly moving in the right direction with some things. Unfortunately, most improvements are being put off while we finish this long-overdue project.

    Just curious, which profiler are you guys using or planning to use?

    Also, the debate of contractor vs. FT has been raging for years.  I've been a contractor for about 95% of my professional career and I've seen some shite contractors and probably an equal amount of shite FTs.  I've found that the main difference between shite contractors and shite FTs is that shite contractor will eventually have their contract end, whereas the FT is a more permanent fixture.

    I find the places that have shite personnel (contractors/FTs/whathaveyou) generally don't have a very robust interview process, so in those cases the WTF is already at the company before the fucktard steps through the doors.



  • @C-Octothorpe said:

    Just curious, which profiler are you guys using or planning to use?

    Profiler? That's funny.

    Actually, I wouldn't mind some suggestions, but I know basically that the problems are occurring in data access stupidity. Among the tasks to be done if the current project ever ends is to look at the worst-offending pages (this is ASP.NET) and troubleshoot their performance.

    @C-Octothorpe said:

    Also, the debate of contractor vs. FT has been raging for years.  I've been a contractor for about 95% of my professional career and I've seen some shite contractors and probably an equal amount of shite FTs.  I've found that the main difference between shite contractors and shite FTs is that shite contractor will eventually have their contract end, whereas the FT is a more permanent fixture.

    I've been at my current job about six months now. My understanding is that IT was entirely contractors for a long time, then some FTEs were hired, eventually. There were a couple really good contractors and a slew of mediocre-to-awful ones. Officially, the contractors were all let go as a "cost-saving measure," but also, the business was sick of dealing with them. The head of IT for about three years was a contractor, and there were some definite problems. The current head of IT, who is an actual employee but has no technology experience, has actually been doing a pretty good job of housecleaning.

    Honestly, I might be able to make a little more money in contracting, but when I was looking for a position during my last job search, I wanted to be somewhere that I could "own" the product rather than just doing everything I'm told and nothing more just to save on billable hours. It remains to be seen whether I will be able to get the process changes I want done after this project, but the response from both the rest of the team and from management to my suggestions has been quite positive overall. I knew going into this job that I would be called on to make these kinds of suggestions, which is another reason I took this job over the other two offers I had at the time.

    @corgimonster said:

    I find the places that have shite personnel (contractors/FTs/whathaveyou) generally don't have a very robust interview process, so in those cases the WTF is already at the company before the fucktard steps through the doors.

    Very true. Every recruiting and/or contracting firm promotes every candidate as the best thing since sliced bread even when the candidate has no real useful knowledge. The trouble seems to come when management believes the sales pitch. I did not have to take a programming test at my current job, which scared me a little, though I was asked technical questions in the interview. At my last job, we had a very simple programming test (create a really simple ASP.NET database app in about three hours), and easily 95% of people who came in the door couldn't do it. The head of IT was TRWTF there, though, since he absolutely would not consider using any recruiters other than one he had some "credit" with (a guy hired the same day as me got canned for incompetence prior to 90 days, so the recruiting firm refunded their fee in the form of a credit) and another known to pad the ever-loving bejesus out of their resumes. One candidate didn't even recognize his resume, saying that he would love to learn the technologies that we were using but that he had no experience with them. His "resume" told a very different story.



  • @corgimonster said:

    Profiler? That's funny.

    Actually, I wouldn't mind some suggestions, but I know basically that the problems are occurring in data access stupidity. Among the tasks to be done if the current project ever ends is to look at the worst-offending pages (this is ASP.NET) and troubleshoot their performance.

    My experience with profilers is pretty much limited to perfmon and some ants profiler, which was actually pretty nice.

    One place I worked at had a crack DBA guy who ran a profiler on the database which watched our connection count, sp performance, etc. which was very useful.

    Depending on the application size, you'd probably save yourself a lot of time and effort by buying a profiler rather than just guessing where the bottlenecks are and optimizing in the wrong places.  You think it's DB access when really it could be that some idiot wrote code to loop through a collection of 50k items 4 times over before getting back to the page which turns out only renders the first 20 items of that collection.



  • @C-Octothorpe said:

    @corgimonster said:

    Profiler? That's funny.

    Actually, I wouldn't mind some suggestions, but I know basically that the problems are occurring in data access stupidity. Among the tasks to be done if the current project ever ends is to look at the worst-offending pages (this is ASP.NET) and troubleshoot their performance.

    My experience with profilers is pretty much limited to perfmon and some ants profiler, which was actually pretty nice.

    One place I worked at had a crack DBA guy who ran a profiler on the database which watched our connection count, sp performance, etc. which was very useful.

    Depending on the application size, you'd probably save yourself a lot of time and effort by buying a profiler rather than just guessing where the bottlenecks are and optimizing in the wrong places.  You think it's DB access when really it could be that some idiot wrote code to loop through a collection of 50k items 4 times over before getting back to the page which turns out only renders the first 20 items of that collection.

    Or caching the database table and getting information out of it every time instead of converting it to an object and caching that. 

    The ANTS profiler is pretty good.  2 weeks free (but you can get an additional 2 weeks by using "I NEED MORE TIME" as the registration code).



  • @Sutherlands said:

    but you can get an additional 2 weeks by using "I NEED MORE TIME" as the registration code.
    Nice!  Thanks for the tip.

    Also, regarding caching, from my experience (read: got burned) I never tell anybody about caching who doesn't know what thread safe means or how to create a double checked lock without googleing it first.  These always tend to create interesting bugs.



  • @C-Octothorpe said:

    @Sutherlands said:

    but you can get an additional 2 weeks by using "I NEED MORE TIME" as the registration code.
    Nice!  Thanks for the tip.

    Also, regarding caching, from my experience (read: got burned) I never tell anybody about caching who doesn't know what thread safe means or how to create a double checked lock without googleing it first.  These always tend to create interesting bugs.

    I'd fire anyone if they created a double-checked lock*... mostly because it's .Net, and you should be using one of the caching implementations available.


  • @C-Octothorpe said:

    Depending on the application size, you'd probably save yourself a lot of time and effort by buying a profiler rather than just guessing where the bottlenecks are and optimizing in the wrong places.  You think it's DB access when really it could be that some idiot wrote code to loop through a collection of 50k items 4 times over before getting back to the page which turns out only renders the first 20 items of that collection.

    That kind of thing is one of the known problems, actually. First off, one of the contractors-of-yore rolled his own pagination implementation, which doesn't work (he still transfers entire, large database tables just to get the first n records). Plus, there are grids that call the database on every row instead of just once (getting entire data sets just to filter down to one row). Plus, there are Entity Framework calls that use all kinds of irrelevant .Include() calls, thereby pulling in all manner of useless data. The contractors who pulled that one had no idea how to use Entity Framework appropriately (giving EF a bad name around here, which is why we're not allowed to use it in new code right now) and either didn't test their code or didn't care that it was slower than a turtle going for a leisurely stroll up a mountain.

    There might be other problems, too, that a profiler would identify, at least within the code. We have done a fair amount of profiling on the actual DB itself using plain old Microsoft tools. There are some long-standing WTFs in the schema design (it was great fun when the decision not to make UserName unique so that "migration would be easier" blew up after some database optimization), but stuff written in the last six months seems to be performing well overall.



  • @Sutherlands said:

    @C-Octothorpe said:

    @Sutherlands said:

    but you can get an additional 2 weeks by using "I NEED MORE TIME" as the registration code.
    Nice!  Thanks for the tip.

    Also, regarding caching, from my experience (read: got burned) I never tell anybody about caching who doesn't know what thread safe means or how to create a double checked lock without googleing it first.  These always tend to create interesting bugs.

    I'd fire anyone if they created a double-checked lock*... mostly because it's .Net, and you should be using one of the caching implementations available.
    Double checked locking isn't .net (or C# I think you meant) specific, AFAIK.  Also, I never said that this is the only way to cache, or even recommended, but it's a basic thread-safe initializing pattern that you should know how to implement and why (why being more important than how, in this case).  Just because your development framework of choice abstracts a lot from you doesn't mean you shouldn't know whats going on under the hood anyway.

    I know a lot of developers who have no idea what an http request header is, the difference between a get and a post, or the concept of more than just one thread accessing/modifying a variable.



  • @C-Octothorpe said:

    @Sutherlands said:

    @C-Octothorpe said:

    @Sutherlands said:

    but you can get an additional 2 weeks by using "I NEED MORE TIME" as the registration code.
    Nice!  Thanks for the tip.

    Also, regarding caching, from my experience (read: got burned) I never tell anybody about caching who doesn't know what thread safe means or how to create a double checked lock without googleing it first.  These always tend to create interesting bugs.

    I'd fire anyone if they created a double-checked lock*... mostly because it's .Net, and you should be using one of the caching implementations available.
    Double checked locking isn't .net (or C# I think you meant) specific, AFAIK.  Also, I never said that this is the only way to cache, or even recommended, but it's a basic thread-safe initializing pattern that you should know how to implement and why (why being more important than how, in this case).  Just because your development framework of choice abstracts a lot from you doesn't mean you shouldn't know whats going on under the hood anyway.

    I know a lot of developers who have no idea what an http request header is, the difference between a get and a post, or the concept of more than just one thread accessing/modifying a variable.

    No you're right, they should know about those things.  My point was simply that they had better use the tools and libraries available to them, such as caching in .Net (which is why they should not use a double-locking mechanism in .Net... well, also you shouldn't use a double-locking mechanism because it doesn't work the way current architectures handle memory barriers, but I don't expect junior developers to know all about that.)

    edit: I should note that double-checked locking is guaranteed to work in CLRv2, but not in the ECMA memory model.


Log in to reply