Posted on December 12, 2008 13:57 by swilliams

I kind of left my last post on this subject dangling, but now I'm ready to come clean.  I was proceeding merrily along, removing the tightly coupled LINQ to SQL generated code into something nice and loose, but ran into some serious snags when trying to update the database objects that had joins associated with it.

The code became more and more unwieldy; I was creating extra loader classes for longer inheritance chains, and more and more abstractions to get everything to work properly. At some point, a little voice in the back of my head started saying, this is supposed to be EASIER? Finally it reached a point where it simply did not appear that I would be able to meet my goal of defining my class models separately from LINQ to SQL's generated ones.

So, I gave up and let the idea die while I focused on other things. Occasionally I did revisit it, but only met with the same conclusion.

About a month ago I became interested in the new ASP.NET MVC framework. Rob Conery has been publishing a series of videos that detail his process of creating an online store. While watching those, I realized that he was implementing the very idea that I had wanted to do here. And amazingly, the code to do it was surprisingly simple and small.

After studying it and poking around, here I am today with the answer! It lies in what Rob calls the Repository pattern. It boils down to this: you have your model classes defined somewhere (in the sample app, I'm using LinqExample.Core), an IRepository interface that states the methods that will interact with the database, and the implementation (LinqRepository here). Rob goes on to create a Service class that sits between the UI and Repository, but for this example, I'm leaving that out.

public interface IPersonRepository {
    IQueryable<Person> GetAll();
    Person GetSingle(int personId);
    IQueryable<TpsReport> GetReports(int personId);
    
    int SavePerson(Person person);
    int SaveReport(TpsReport report, Person forThisPerson);
}

This also makes testing easy. I can cook up a TestPersonRepository that returns dummy data, allowing any UI testing to avoid touching the database.

Next, we create the implementation of this interface using LINQ to SQL. Add the .dbml CropperCapture[13] file and drag the two tables onto the workspace as you would normally. The key part here is to change the Context Namespace and Entity Namespace to something other than the default (an empty string). I used LinqExample.Data.Repository. This has the effect of namespacing each generated class. Thus, they will not conflict with the model classes we have already defined.

Our example actually looks similar to what I laid out in Part 3, however I do not use the Entity types for the joining objects, but rather IQueryable<T>. This allows a lazy evaluation of the objects, and let's LINQ to SQL's binding work better. Additionally, I reverted all of the properties in the classes to the basic auto-properties that C# 3.0 brings us.

public class Person {
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public IQueryable<TpsReport> Reports { get; set; }
}

The key piece from Rob's code is how he pulls the data using LINQ to SQL. Rather than just returning the auto-generated classes, he selects the actual Person or TpsReport class and the initializers to populate them:

public IQueryable<LinqExample.Core.Person> GetAll() {
    var people = from pe in this.db.Persons
                 select new Person {
                     Id = pe.id,
                     FirstName = pe.fname,
                     LastName = pe.lname,
                     Reports = this.GetReports(pe.id)
                 };
    return people;
}

At this point you may be thinking "All you did is create a class and just do a bunch of right/left property setting." While that is true (and the right/left bit does get tedious), it does more than that by return IQueryable<T>.

Returning as an IQueryable<T> has an extra advantage of letting you do further queries without having to throw away parts of the initial return set. Thus, if you chose to implement the Service layer, you could keep GetAll() in the Repository implementation, and have GetSingle(), GetWithLastName(), and others in the Service assembly.

If you returned an IList<T> instead, you would need to call ToList(), which would run the whole query right then, and forsake all the lazy evaluation benefits from IQueryable<T>. Of course, you may want to do that, but that is what the Service Layer is for.

Updating the database is not quite as simple, but doesn't involve black magic either. The gist of it is to check to see if this is an update or an insert call, and to perform accordingly. Here is SavePerson(), but SaveReport() is very similar

public int SavePerson(Person person) {
    using (Repository.LinqExampleDbDataContext saver = new Repository.LinqExampleDbDataContext()) {
        Repository.Person pe = saver.Persons.SingleOrDefault(p => p.id == person.Id);
        bool isNew = false;
        if (pe == null) {
            isNew = true;
            pe = new Repository.Person();
        } 
        pe.fname = person.FirstName;
        pe.lname = person.LastName;
        if (isNew) {
            saver.Persons.InsertOnSubmit(pe);
        }
        saver.SubmitChanges();
        return pe.id;
    }
}

And, you're done! This is a little easier to swallow than what I had going in Part 3, no XML to muck around with, and is far simpler than the code soup I was cooking up before abandoning that approach.

Should you decide to go with another technology, be it the Entity Framework, NHibernate, or even a non SQL Server database, you would only need to create an IRepository implementation for that particular framework/database. It can be tedious for large quantities of tables, but that is the price you might have to pay for loose coupling and a friendlier design. Of course, you could refactor that to automatically set properties through Reflection or something, but that is another article altogether.


Source code for this post is located here.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted on July 26, 2008 11:47 by baugustine

Most of us application developers have worked with SQL Server databases and especially tables with IDENTITY property. We have all written stored procedures to insert records into tables with columns that have IDENTITY property set.

In case you are not familiar with IDENTITY property, this property can be set on a column that has a data type of decimal, int, numeric, smallint, bigint, or tinyint. When this property is set SQL Server automatically populates that column with a next higher number in the sequence when a record is inserted. So it has been somewhat of a common practice for application developers to set the IDENTITY property for an Id column of a table and let SQL Server manage the unique primary key values. More information about IDENTITY property can be found here

Normally one of the requirement when you write a stored procedure to insert a record into a table with IDENTITY column is that you will have to pass the Id value of the newly created record back to the application. Lot of developers use the @@identity variable in SQL Server to do that. And most of the time this will work because the @@identity variable holds the last identity value generated by the insert statement. Run the sql1.sql file from the attached zip file to see this working.

So far so good. Now all that is needed for this nicely working proc to fail to return the correct Id value is to add a trigger to our table, and that trigger inserts a record into another table (say an auditing table) which also happens to have a column that has its IDENTITY property set. Run the sql2.sql file from the attached zip file to see this behavior.

So why did the incorrect id values are returned after adding the trigger. The reason is that @@identity does not have a concept of scope. It always returns the identity value of last inserted record in the database. In this case it happens to the audit table record that got inserted last. So to overcome this behavior we need to modify the proc to use the scope_identity() function to return the last identity value instead of the @@identity variable. Run the sql3.sql file from the attached zip file to see the modified proc and the correct behavior.

So always use @scope_identity() function in your insert proc to return identity column values even though you may not have triggers on your table during development. Because adding a trigger on your table is beyond your control. All it takes is for your proc and application to not work properly or even worse cause data corruption is for someone else to add a trigger on one of your tables for some reason or the other.

ScopeIdentity.zip (1.70 kb)



Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted on June 30, 2008 10:26 by lbrown

Ever wonder how that magical tag called ‘[Serializable]’ placed at the top of a class enables it to be deconstructed into XML that can be tranmitted through firewalls or persisted in a files or database tables? Not to get too technical but attribute classes are embedded into the tagged classes’ metadata at compilation and can be reflected on to perform any function you desire. 

The following example provides a real world scenario where custom attributes can enhance one’s  coding experience.  I can’t tell you how many times I have to use stored procedures that have a million parameters each having the capacity to be null.  Think of a search procedure that has ten criteria.  If I was coding this with the SqlCommand class, I would have to write a method that creates each SqlParameter and populates it with DBNull or some value. There are over ninety nine combinations in this scenario.  That really cuts into my ebay time.

If I wrote an attribute that could turn a class into a SqlCommand with populated values and could be serialized in session or view state and reduce my time coding, life would be wonderful.  In addition, if I wrote a tool that generated the code for the attributed class, my evil plan to do no work would be complete!

I have attached a solution that contains all the code mentioned above.

AtrributeSolution.zip (51.43 kb)



Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted on March 25, 2008 20:43 by mcollins

While I'm developing, I'm always making changes to code and data, and not necessarily keeping very good or accurate records in the heat of the moment about what I'm changing.  Source code is easy to resolve using modern version control tools, because I can always run a diff to see the changes that I made.  But what do I do about databases?  If I ran some ALTER statements in the database, what happens if I accidentally closed the database session and lost my changes, or if I want to go back and review what I did at the end of an iteration?  What do I do then?

Fortunately, using a trick with triggers in SQL server, I can let SQL Server keep track of the changes that I make in my database.  In my databases, the first thing that I usually create is a table for storing DDL changes to my databases and a trigger that automatically populates the table when DDL statements are executed.  By using the table and trigger, I can go back at any point and review all of the changes that were made to the database.

The table that I usually define is below:

   1: CREATE TABLE dbo.DatabaseModification (
   2:     DatabaseModificationId INT NOT NULL IDENTITY(1,1) CONSTRAINT
   3:         PKNC_DatabaseModification PRIMARY KEY NONCLUSTERED WITH
   4:         FILLFACTOR = 90 ON "default",
   5:     EventType NVARCHAR(256) NOT NULL,
   6:     PostTime DATETIME NOT NULL,
   7:     Spid INT NOT NULL,
   8:     ServerName NVARCHAR(256) NOT NULL,
   9:     LoginName NVARCHAR(256) NOT NULL,
  10:     UserName NVARCHAR(256) NOT NULL,
  11:     DatabaseName NVARCHAR(256) NOT NULL,
  12:     SchemaName NVARCHAR(256) NULL,
  13:     ObjectName NVARCHAR(256) NULL,
  14:     ObjectType NVARCHAR(256) NULL,
  15:     TSqlCommand NVARCHAR(MAX) NOT NULL,
  16:     Note NVARCHAR(MAX) NOT NULL DEFAULT N''
  17: ) ON "default";

 

Most of these fields are populated automatically from information gathered by the trigger.  The Note field was added by me.  This field allows me to annotate the changes in the database while I'm reviewing the database changes.  More and more often, I find that I'm adding this table to my database definitions and including the table in the production databases.  On my applications, I usually also create an administrator user interface for this table because I think that it's handy for a system administrator of an application to be able to look at what changes occurred to a database if something breaks or goes wrong for no apparent reason.  The administrative UI also allows the administrator to edit the contents of the Note field.

Here's the source code for the trigger that populates this table:

   1: CREATE TRIGGER trgDatabaseModificationInsert ON DATABASE FOR
   2:     DDL_DATABASE_LEVEL_EVENTS
   3: AS
   4:     SET NOCOUNT ON;
   5:     
   6:     DECLARE @data AS XML;
   7:     SET @data = EVENTDATA();
   8:     INSERT INTO dbo._zsDatabaseModification (EventType, PostTime, 
   9:         Spid, ServerName, LoginName, UserName, DatabaseName, 
  10:         SchemaName, ObjectName, ObjectType, TSqlCommand) VALUES (
  11:         @data.value('(/EVENT_INSTANCE/EventType)[1]', 
  12:             'NVARCHAR(256)'),
  13:         @data.value('(/EVENT_INSTANCE/PostTime)[1]', 'DATETIME'),
  14:         @data.value('(/EVENT_INSTANCE/SPID)[1]', 'INT'),
  15:         @data.value('(/EVENT_INSTANCE/ServerName)[1]', 
  16:             'NVARCHAR(256)'),
  17:         @data.value('(/EVENT_INSTANCE/LoginName)[1]', 
  18:             'NVARCHAR(256)'),
  19:         @data.value('(/EVENT_INSTANCE/UserName)[1]', 
  20:             'NVARCHAR(256)'),
  21:         @data.value('(/EVENT_INSTANCE/DatabaseName)[1]', 
  22:             'NVARCHAR(256)'),
  23:         @data.value('(/EVENT_INSTANCE/SchemaName)[1]', 
  24:             'NVARCHAR(256)'),
  25:         @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 
  26:             'NVARCHAR(256)'),
  27:         @data.value('(/EVENT_INSTANCE/ObjectType)[1]', 
  28:             'NVARCHAR(256)'),
  29:         @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 
  30:             'NVARCHAR(MAX)'));
  31:     
  32:     SET NOCOUNT OFF;
  33: GO

 

By creating this trigger for DDL_DATABASE_LEVEL_EVENTS, any DDL statements that would modify the database by creating, altering, or deleting database objects will get logged in the DatabaseModification table.

Technorati tags: , , , ,


Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5