Learning in the Open: II – first relation and more ActiveRecord

It took a little longer than I planned but here we go again. In the meantime ActiveRecord 2.1 was released, and soon after that a minor update bringing one cool big feature. From now on we’ll be working on version 2.1.2. Picking up from where we left off last time. We have a user entity. Since we’re building a website where users can publish benchmark results, we’ll create now a benchmark entity, and create a relation between these two.

I want to see results!

Let’s start by adding an appropriate field to the User class:

private readonly ICollection<BenchmarkResult> benchmarkResults = new HashSet<BenchmarkResult>();

We also create a property:

public IEnumerable<BenchmarkResult> BenchmarkResults
{
    get
    {
        foreach (var result in benchmarkResults)
        {
            yield return result;
        }
    }
}

So far this is just a regular property. To map it as a one-to-many relation we use the HasManyAttribute.

[HasMany(Access = PropertyAccess.FieldCamelcase, 
    Cascade = ManyRelationCascadeEnum.SaveUpdate, 
    RelationType = RelationType.Set,
    Inverse = true)]
public IEnumerable<BenchmarkResult> BenchmarkResults

 

There’s quite a lot going on here, so let’s go over it piece by piece

  • Access property FieldCamelcase specify we want ActiveRecord (and NHibernate underneath it) to go to the field directly, which makes sense since we’re exposing it as mere enumerable.
  • Cascade specifies that when saving or updating our user, all new and changed benchmark results in the collection should also be appropriately saved or updated.
  • Usually we wouldn’t have to specify type of the relation. Usually it will infer it from the kind of collection we expose, and would use set for ISet, map for IDictionary, bag for ICollection etc. However since we’re exposing only IEnumerable it does not have enough information to decide, that’s why we have to be explicit here.
  • We also specify Inverse property to be true, which basically means that it’s child’s task to maintain the relationship. That also means that child needs to have a reference to the parent.

Let’s now build our BenchmarkResult class.

[ActiveRecord]
public class BenchmarkResult : ActiveRecordLinqBase<BenchmarkResult>
{
    protected BenchmarkResult()
    {
    }
 
    public BenchmarkResult(User user, string benmchmarkName, string computerModel, double score)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }
        if (benmchmarkName == null)
        {
            throw new ArgumentNullException("benmchmarkName");
        }
        if (computerModel == null)
        {
            throw new ArgumentNullException("computerModel");
        }
 
        User = user;
        BenmchmarkName = benmchmarkName;
        ComputerModel = computerModel;
        Score = score;
    }
}

So far there’s nothing new here. Computer configuration and benchmark will become entities themselves soon, but let’s not get ahead of ourselves.

The only interesting property at this point is the User.

[BelongsTo]
public User User { get; private set; }

 

It has a BelongsToAttribute to denote it points to another entity (on our case the ‘one’ end of our one-to-many).

Let’s now let our users to actually save benchmark results, and we’re more or less done:

public BenchmarkResult RunBenchmark(string benchmarkName, string computerModel, double score)
{
    var result = new BenchmarkResult(this, benchmarkName, computerModel, score);
    benchmarkResults.Add(result);
    return result;
}

Test

We now have all the logic in place, so let’s build a test:

[Fact]
public void Can_perform_benchmark_runs()
{
    var stefan = new User
    {
        Email = "stefan@gmail.com",
        Name = "Stefan",
        Password = "Super compilcated password!",
        About = "Stefan is a very cool."
    };
    stefan.RunBenchmark("Foo bar!", "AyeMack Pro", 3.2);
    stefan.Save();
 
    var user = User.FindAll().Single();
 
    Assert.NotEmpty(user.BenchmarkResults);
    Assert.Equal(1, user.BenchmarkResults.Count());
 
    var result = user.BenchmarkResults.Single();
 
    Assert.NotNull(result);
    Assert.Equal("Foo bar!", result.BenmchmarkName);
    Assert.Equal("AyeMack Pro", result.ComputerModel);
    Assert.Equal(3.2, result.Score);
}

If we run it now, it will fail. Good news is, that it does not have anything to do directly with our logic. Bad news is, that we have a passing test nonetheless, so let’s have a look at it.

Error

We get “Incorrect syntax near the keyword 'User'.” error message. Here’s the SQL that was sent to the database:

error_sql

Looks good doesn’t it? Well not – really, User is a SQL Server keyword, as we can’t just use it as identifier – we have to escape it. To do it, we have to specify the column name explicitly, and escape it by enclosing it within two ` characters (located above tab key on my keyboard).

Yes I’m aware of hbm2ddl.keywords auto-quote. However I had some issues getting it to work with ActiveRecord. Any help doing this will be appreciated.

[BelongsTo(Column = "`User`")]
public User User { get; private set; }

Now the test will pass, and the following SQL will be generated:

ok_sql

Now that we have the correct SQL, let’s look at what our schema looks like:

schema_inverse_true


Posted 02-02-2010 6:18 PM by Krzysztof Koźmic
Filed under:

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)