in

Platinum Bay

Peace, Love, and...

This Blog

Syndication


.NETicated

December 2008 - Posts

  • Quotes

    I like quotes, and those of you who follow me on Twitter see me post them from time to time. I've decided, in part due to an open request from Phil Haack for an inspirational quote, to catalog a few of my favorite quotes here:

    "Nothing will ever be attempted if all possible objections must first be overcome."
    - Samuel Johnson

    "Winning isn't everything, but wanting to win is."
    - Vince Lombardi

    "If everything seems under control, you're just not going fast enough."
    - Mario Andretti

    "Even if you are on the right track, you'll get run over if you just sit there!"
    - Will Rogers

    "What lies behind us, and what lies before us are tiny matters compared to what lies within us."
    - Ralph Waldo Emerson

    "If you do not hope, you will not find what is beyond your hopes."
    - St. Clement of Alexandra

    "Go confidently in the direction of your dreams. Live the life you've imagined."
    - Henry David Thoreau

    "Happiness is not achieved by the conscious pursuit of happiness; it is generally the by-product of other activities."
    - Aldous Huxley

    "Sometimes I think sensitive people are more capable of reflection because they have to shut the world out to function."
    - @mfeathers

    "What I think is interesting is that the people who are awed by greatness often become great themselves."
    - @mfeathers

    "We don't see things as they are -- We see things as we are."
    - Anais Nin

    "When we are unable to find tranquility within ourselves, it is useless to seek it elsewhere."
    - Francois de La Rochefoucauld (1613 - 1680)

    "Success is a lousy teacher. It seduces smart people into thinking they can't lose."
    - Bill Gates

    "If you don't risk anything you risk even more."
    - Erica Jong

    "The world is moving so fast these days that the man who says it can't be done is generally interrupted by someone doing it."
    - H.E. Fosdick

    "You cannot live a perfect day without doing something for someone who will never be able to repay you."
    - John Wooden

    "If you don't set goals for yourself, you are doomed to work to achieve the goals of someone else."
    - Brian Tracy

    "Never lose the childlike wonder. It's just too important. It's what drives us."
    - Randy Pausch (1960-2008)

    "If you lead your life the right way, the karma will take care of itself. The dreams will come to you."
    - Randy Pausch (1960-2008)

    "There is only one success - to be able to spend your life in your own way."
    - Christopher Morley

    "It's fine to celebrate success but it is more important to heed the lessons of failure."
    - Bill Gates

    "We can do no great things, just small things with great love. It is not how much you do, but how much love you put into doing it."
    - Mother Teresa

    "I always learn more from failure than from success."
    - Nick Malik

    "Never doubt that a small group of thoughtful committed citizens can change the world. Indeed it's the only thing that ever has."
    - Margaret Mead

    "Change your thoughts and you change your world."
    - Norman Vincent Peale

    "Reality is a lot like an expensive wine, it won't appeal to children."
    - Unknown

    "Imagination is more important that knowledge"
    - Albert Einstein

    "I do not believe my energy and enthusiasm to be simply a factor of youth, but I am still young."
    - Unknown

    "No one is useless in this world who lightens the burden of it to anyone else."
    - Charles Dickens

    "It was in my heart to help a little because I was helped much."
    - Kahlil Gibran

    "We can't solve problems by using the same kind of thinking we used when we created them."
    - Albert Einstein

    "Never trade reputation for money."
    - Warren Buffet

    “Learn from yesterday, live for today, hope for tomorrow.”
    - Albert Einstein

  • Windows Live Writer

    So I finally caved into pressure and am writing this post from Windows Live Writer. After some hassle with the install on Win2K8 (thanks to @ninob for pointing me here), I set about to write my first post. I do what I normally do; open the program and hit Maximize. Clearly Windows Live Writer wasn't designed to be maximized (see screenshot), at least not at 1920x1200. And yes, I am going to be highly critical of it, at least compared to my experience using Word 2007 for writing posts.

    image

    No test would be complete without the obligatory code snippet. First results were that all the formatting were lost. Weird. Word does this out of the box. Fortunately I found the Paste from Visual Studio add-in, which gives me this:

    public static string LabelFor(this HtmlHelper helper, string target, string text)
    {
        return String.Format("<label for='{0}'>{1}</label>", target, text); 
    }

    Now to see how everything looks online...

  • DevEvents – The Generic Repository

    Let it be said that I strongly dislike code duplication. For that matter, I also strongly dislike repetitive typing. Yet as I started to build out DevEvents, I found myself frequently duplicating similar code across object repositories, and typing the same base statements. In these cases, I typically create a base class to take care of the duplicated functionality, and inherit the dependant classes from the base. With the object repositories however, each duplicated section of code was slightly dependant on the object type. Not to be deterred, I ended up creating a custom generics-based repository implementation that virtually eliminated the code duplication. But let's start from the beginning.

    DevEvents is built on top of a LINQ to SQL model which lives in its own assembly. That assembly is then referenced from a Components layer in which the repositories live. A typical repository implementation might look like this:

    public class OldLogRepository : IOldLogRepository
    {
        private DEDataContext db;
    
        public OldLogRepository()
        {
            db = new DEDataContext();
        }
    
        internal OldLogRepository(DEDataContext context)
        {
            db = context;
        }
    
        public int GetLogEntryCount()
        {
            return db.Logs.Count();
        }
    
        public void ClearLogEntriesBefore(DateTime dateTime)
        {
            var query = (from le in db.Logs
                        where le.EventDate <= dateTime
                        select le);
    
            db.Logs.DeleteAllOnSubmit(query);
        }
    }

    I feel this is a pretty standard repository implementation, and it helps promote loose coupling at several levels. First, the repository itself is interface-based which means it can be swapped out if necessary. Second, the internal constructor allows for dependency injection of a non-default data context.

    My frustration arose when I had to duplicate a lot of the standard functionality across dozens of repositories, such as Count, Create, Delete, Update, Select, etc. The first idea was to create a base class to house that functionality, but as shown in the ClearLogEntriesBefore method above, there was some object specific logic in the methods. I realized however, that the method could be broken up into different parts: the query, the context, and the table. A quick check of the model code confirmed this, and I set about to create a generic repository implementation to reduce or eliminate the amount of redundant code. I started with an abstract generic class:

    public abstract class BaseRepository<TModel> : IBaseRepository<TModel>

    Next, I created the core fields and constructor:

    protected DEDataContext db;
    protected Table<TModel> _table;
    
    public BaseRepository(Table<TModel> table)
    {
        db = new DEDataContext();
        _table = table;
    }

    Note that the default constructor tables a parameter of type System.Data.Linq.Table<TEntity>. This is the base type of all entity objects, and allows the base class to handle generic table operations on the correct table.

    So far I have taken care of the context and the table parts of my original duplicated methods. Next, I needed to solve the query problem. When I examined the method signature for operations such as Where and Select, I discovered that they all took a parameter of type Expression<Func<TModel, bool>>. What this means is that it is possible to pass parameters as a known base type. The base Contains method could now be written as:

    public bool Contains(Expression<Func<TModel, bool>> predicate)
    {
        return _table.Any(predicate);
    }

    From the top level repository class, I could then call Contains as follows:

    return Contains(le => le.SomeProperty == someValue);

    Using this methodology I was able to create a number of base methods, such as Count. The next step was basic CRUD operations; Create, Select, Update and Delete.

    Create is fairly straightforward:

    public void Create(TModel model, bool save)
    {
        _table.InsertOnSubmit(model);
        if (save)
            _table.Context.SubmitChanges();
    }
    
    public void Create(IEnumerable<TModel> models, bool save)
    {
        _table.InsertAllOnSubmit(models);
        if (save)
            _table.Context.SubmitChanges();
    }

    You might be wondering about the save parameter. In LINQ to SQL, entities added to the context are not saved to the database until SubmitChanges() is called. There are certain instances in the application where it is preferable to add a group of entities, and then commit them to the database.

    Delete and select got a little more complicated, since I am optionally passing a predicate of type Expression<Func<TModel, bool>>. The answer lies in the fact that IQueryable types are just that until they are invoked, a query. This means that I can refine the query, such as is done in the Delete method:

    protected void Delete(Expression<Func<TModel, bool>> predicate)
    {
        var list = (from e in _table
                    select e);
    
        if (predicate != null)
            list = list.Where(predicate);
    
        _table.DeleteAllOnSubmit(list);
        _table.Context.SubmitChanges();
    }

    You will notice that the Delete method is marked as Protected. This is to prevent it from being called outside of the repository, thereby enforcing interaction with the top-level repository methods to perform repository operations. For that matter, the db field in BaseRepository should be marked as private to ensure it is the single point of responsibility for the data context, but there is some cleanup to do, and I digress.

    Select is similar, except that it is implemented across multiple methods: LoadByQuery, LoadAllByQuery, LoadTopByQuery and LoadAll, all of which have overrides to optionally take a DataLoadOptions object. There are also a couple specialized methods to pull data in a serializable format. LoadAllByQuery is shown here implementing query refinements:

    protected IQueryable<TModel> LoadAllByQuery(Expression<Func<TModel, bool>> predicate)
    {
        return LoadAllByQuery(predicate, null);
    }
    
    protected IQueryable<TModel> LoadAllByQuery(Expression<Func<TModel, bool>> predicate, DataLoadOptions dlo)
    {
        if (dlo != null)
            db.LoadOptions = dlo;
    
        var list = (from e in _table
                    select e);
    
        if (predicate != null)
            list = list.Where(predicate);
    
        return list;
    }

    With the generics-based repository implementation in place, I can now rewrite the original LogRepository as follows:

    public sealed class LogRepository : BaseRepository<Log>, ILogRepository
    {
        public LogRepository()
            : base(new DEDataContext().Logs)
        {
        }
    
        public void ClearLogEntriesBefore(DateTime dateTime)
        {
            Delete(l => l.EventDate < dateTime);
        }
    }

    First, you will notice that there are significantly fewer lines of code (eleven versus twenty-three), and what is there is much less complex. Second, the Count method is no longer included. Since the Count method is in the base class and marked as Public, it can be called directly from the instantiated object.

    Here is another method from the Log repository, made simpler through its interaction with the base methods:

    public IQueryable<Log> GetLogEventsPaged(int pageID)
    {
        return LoadAllByQuery(null)
                .Skip(((pageID - 1) * DEContext.Current.PageSize))
                .Take(DEContext.Current.PageSize);
    }

    I have found a generics-based repository to be incredibly useful and efficient for my purpose. So my question to you, dear reader, is what do you think? How would you improve upon this implementation to make it better?

  • What’s In Your Quick Launch Bar?

    Heartland Developer Evangelist Jeff Blankenburg blogged today about his quick launch bar and the apps he has positioned there. He also encouraged others to do the same, I'd imagine out of pure curiosity.

    Here is mine, broken up because I run at 1900x1200:

    And here is it in list view with names, and sorted alphabetically:

    The crazy part? I use all this stuff! In case you're wondering, I run Windows Server 2008.

    So what does your quick launch bar look like?

  • Geek Food Drive

    It is estimated that over 33.5 million people in the United States and Canada will go hungry during the holidays this year. According to my data, which is a bit old, there are well over 180,000 registered user group members in the United States and Canada.

    My challenge is simple: for all user groups to hold a food drive at their meeting this month. There is plenty of need, especially this year!

    To this end, I've put up a simple website: http://www.GeekFoodDrive.com

    I'm coordinating with sponsors to provide swag based on donations relative to attendees, to be fair to all groups large and small.

    If you can help at all, let me know! Together, we can make a serious dent in hunger this holiday season!

    Posted Dec 07 2008, 08:13 PM by Steve with no comments
    Filed under:
Powered by Community Server (Commercial Edition), by Telligent Systems
© Platinum Bay | Some Rights Reserved Creative Commons License

Disclaimer: The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Feel free to challenge me, disagree with me, or tell me I'm completely nuts in the comments section of each blog entry, but I reserve the right to delete any comment for any reason whatsoever (abusive, profane, rude, or annonymous comments) - so keep it polite, please.