Saturday, January 05, 2008

We have seen two types of inheritance modeling, at this point I'm interested in modeling an association. This actually turned out to be harder then I had expected, even though the documentation on this subject is good and plentyfull ;-)

However, since you are still reading, I will create a very simple example in the employee class, such that an employee needs to report to another employee.
Remember that employee inherits from person. I was happy to see that I was indeed able to create the relationship!

I've added a new column in the employee table, named: ReportsToID and a foreignkey relation that specifies the primary key base to be Employee/EmployeeID and the foreign key base to be Employee/ReportsToID. In database lingo this simple means that ReportsToID can be filled with an exact EmployeeID and that this relationship is verified (for instance, when deleting an employee, but still having other employees which report to the deleted employee).

 Let's look at the SSDL that I created to match this new foreignkey relationship:

In the EntityContainer section:
          <AssociationSet Name="FK_Employee_Manager"
                          Association="EntityFrameworkTestModel1.Store.FK_Employee_Manager">
            <End Role="Manager" EntitySet="Employee" />
            <End Role="Members" EntitySet="Employee" />
          </AssociationSet>
And the mentioned AssociationSet:
        <Association Name="FK_Employee_Manager">
          <End Role="Manager" Type="EntityFrameworkTestModel1.Store.Employee" Multiplicity="0..1" />
          <End Role="Members" Type="EntityFrameworkTestModel1.Store.Employee" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="Manager">
              <PropertyRef Name="EmployeeID" />
            </Principal>
            <Dependent Role="Members">
              <PropertyRef Name="ReportsToID" />
            </Dependent>
          </ReferentialConstraint>
        </Association>

As you can see, I named the roles: Manager and Members.
The association can be interpreted as follows: An employee (a Member) can have zero or one Manager. A Manager can have zero to infinite Members.

Let's take a look at the C-side of life.
In the EntityContainerSection:
          <AssociationSet Name="TeamMemberToTeamLeader"
                          Association="EntityFrameworkTestModel1.TeamMemberToTeamLeader">
            <End Role="TeamMembers" EntitySet="Person" />
            <End Role="TeamLeader" EntitySet="Person" />
          </AssociationSet>
And the Association is defined as:
        <Association Name="TeamMemberToTeamLeader">
          <End Type="EntityFrameworkTestModel1.Employee" Role="TeamLeader" Multiplicity="0..1" />
          <End Type="EntityFrameworkTestModel1.Employee" Role="TeamMembers" Multiplicity="*" />
        </Association>

I like to use real desciptive names, but this should be easy enough to follow.

Also, I added navigationProperties to my model, so I can easily follow an association:
        <EntityType Name="Employee" BaseType="EntityFrameworkTestModel1.Person">
          <NavigationProperty Name="TeamLeader" Relationship="EntityFrameworkTestModel1.TeamMemberToTeamLeader" FromRole="TeamMembers" ToRole="TeamLeader" />
          <NavigationProperty Name="TeamMembers" Relationship="EntityFrameworkTestModel1.TeamMemberToTeamLeader" FromRole="TeamLeader" ToRole="TeamMembers" />
        </EntityType>

Remember that an association is an independent entity, it only defines a relation that can be traversed. We need Navigationproperties to actually use the association. This way it can be shared between models.

The actual mapping between the store and the conceptual model is interesting:
          <AssociationSetMapping Name="TeamMemberToTeamLeader"
                                 TypeName="EntityFrameworkTestModel1.TeamMemberToTeamLeader"
                                 StoreEntitySet="Employee" >
            <EndProperty Name="TeamMembers">
              <ScalarProperty Name="PersonID" ColumnName="EmployeeID" />
            </EndProperty>
            <EndProperty Name="TeamLeader">
              <ScalarProperty Name="PersonID" ColumnName="ReportsToID" />
            </EndProperty>
            <Condition ColumnName="ReportsToID" IsNull="false" />
          </AssociationSetMapping>

Here you can see that I am mapping the ScalarProperty PersonID instead of EmployeeID; remember we are using inheritance here.

The condition is needed to solve some errors I was having. I think it can be read as: the association only works if there is a reportsID value set.

Some test code:

Employee e = new Employee();
e.Firstname = "Ian";
e.Lastname = "Mort";
context.AddToPerson(e);

Einstein smartNerd = new Einstein();
smartNerd.Firstname = "Albert";
smartNerd.Lastname = "Einstein";
smartNerd.Language = "C# 3.5";
smartNerd.TeamLeader = e;
context.AddToPerson(smartNerd);

Elvis elvis = new Elvis();
elvis.Firstname = "Elvis";
elvis.Lastname = "Presley";
elvis.Language = "C# 2.0";
elvis.TeamLeader = e;
context.AddToPerson(elvis);

At this point I can check that e.TeamMembers has a count of 2 and both employees have a teammember property! So, everything working as expected.

Reading back this post, I can see it was actually pretty simple. However, the syntax seems to be overly complex and a small mistake leads to weird errors!

 

Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):