0
1

I have Class which worked perfectly with only the ID column as Key. Now I added the Name column as a Second Key (Using the Key(0) and Key(1) Attributes). But now I'm getting the following error message on Domain.Build: Default generator can serve hierarchy with exactly one key field.

<HierarchyRoot()> _
<TableMapping("Projects")> _
Public Class Project
    Inherits Entity

    Sub New()
        MyBase.New()
    End Sub

    <Key(0), Field()> _
    Public Property ID() As Integer
        Get
            Return GetFieldValue(Of Integer)("ID")
        End Get
        Private Set(ByVal value As Integer)
            SetFieldValue(Of Integer)("ID", value)
        End Set
    End Property

    <Field(Length:=128), Key(1)> _
    Public Property Name() As String
        Get
            Return GetFieldValue(Of String)("Name")
        End Get
        Set(ByVal value As String)
            SetFieldValue(Of String)("Name", value)
        End Set
    End Property

    <Field(Length:=256, Nullable:=True)> _
    Public Property Description() As String
        Get
            Return GetFieldValue(Of String)("Description")
        End Get
        Set(ByVal value As String)
            SetFieldValue(Of String)("Description", value)
        End Set
    End Property

    <Field()> _
    Public Property Creator() As Guid
        Get
            Return GetFieldValue(Of Guid)("Creator")
        End Get
        Set(ByVal value As Guid)
            SetFieldValue(Of Guid)("Creator", value)
        End Set
    End Property

End Class

asked Oct 08 '10 at 02:39

Michiel%20Alders's gravatar image

Michiel Alders
506710

edited Oct 08 '10 at 06:19

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412


One Answer:

The error indicates the following:

  • You don't implement your own constructors for your entity. This means a default one will be provided by VB.NET or C# compiler.
  • Default constructor (with empty argument list) always calls default constructor of base type (Entity in this case).
  • Default Entity constructor assigns a key to Entity using default key generator.
  • In this case default key generator can't provide a key for you: it is capable of providing primitive keys only.

So what could be done in your case to resolve the issue?

Option 1: manual key assignment

Add the following constructor to your class (sorry, C# notation):

public Project(int id, string name)
  : base(id, name) // That's one of ways to create an entity with pre-defined key
{
  // Key is already assigned at this point
  Debug.WriteLine("Project.Key = {0}", Key);
}

Option 2: semi-automatic key assignment

As far as I can judge, you actually need to automatically generate a part of composite key - i.e. ID field value must be auto-generated, but Name field value must be manually assigned.

The simplest way to achieve this is to:

a) Declare a dummy entity type having the same primitive key type as the one you'd like to generate:

[HierarchyRoot]
public class IntKeyedEntity : Entity {
  [Key, Field]
  public int Id { get; private set; }
}

This is necessary to ensure that key generator providing key values for its Id field (in this case, a default key generator for keys of Int32 type) will be available when Domain is built, i.e. necessary data structures will be created, and runtime KeyGenerator instance is available.

b) Add the following constructor to your class:

public Project(string name)
  : base(Key.Create<IntKeyedEntity>().Value.GetValueOrDefault(0), name)
  // We use the same constructor of `Entity` type, but generate
  // a part of the key relying on key generator of another type.
{
  // Key is already assigned at this point
  Debug.WriteLine("Project.Key = {0}", Key);
}

That's it.

answered Oct 08 '10 at 03:48

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

edited Oct 08 '10 at 03:52

A brief explanation:

Key.Create<T>() generates new Key for Entity of T type. It relies on our high-level KeyGenerator objects (you can decompile this method in .NET Reflector, if you're interested), that use Hi-Lo algorithm to generate the keys by default. I.e. such call is normally quite cheap.

Key.Value returns our own Tuple object containing all Key fields.

Tuple.GetValueOrDefault(0) returns the value of field with zero index.

(Oct 08 '10 at 04:06) Alex Yakunin Alex%20Yakunin's gravatar image

And a remark about tuples:

Our Tuple objects differ from the ones in .NET:

  • Their field access methods are virtual. I.e. you don't need to know the exact type of Tuple to use it.
  • They maintain "is null" and "is unspecified" (wasn't set) flags for each field - that's quite convenient, if you use them to deal with DBs.
  • They don't have length limit (internally long tuples are combined of a set of smaller instances).
  • But they're also quite compact objects.
(Oct 08 '10 at 04:08) Alex Yakunin Alex%20Yakunin's gravatar image

We use these objects as low-level data storage - e.g. any Entity stores their state inside such objects, they're used as intermediate row-like structure in any queries. Moreover, our IMDB provider uses them to store index entries and perform any query-related computations.

Since they have an API based on virtual methods, they're a bit slower in comparison to .NET tuples; on the other hand, such an approach allows us to provide a nice set of inheritors there.

(Oct 08 '10 at 04:08) Alex Yakunin Alex%20Yakunin's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA