[Version] attribute seems to need some Love.

Issue #69 closed
Todd Flora created an issue
  1. The Version attribute and the Column attribute cannot both be declared on the same property or field as this caused a duplicate exception when the column is being added twice to the underlying column Dictionary.

  2. Since number 1 above seems to be true the Version attribute should expose the ability to set the column attribute values for it. The version attribute does derive from the Column attribute as it should, but because Delphi Attributes do not allow inherited constructors to be called, the column constructors should be redeclared in the Version attribute. This way the user can define the column name and other attributes for this column accordingly.

  3. When using the [Version] attribute it seems to make sense that the seed value for the version column on an insert should be 1 not zero. Alternatively it would be nice to have a way to define the initial seed value in the Version Attribute definition.

I have most of this coding done so if I can provide a pull request I would be happy to do so.

Comments (10)

  1. Todd Flora reporter

    Linas,

    Here is the VersionAttribute code snippet from the Spring.Persistence.Mapping.Attributes unit. Maybe you can use it. It does two things

    1. Exposes all constructors from Parent ColumnAttribute so that the Version field can be configure.
    2. Provides a Starting Number option so that the user can start from one or wherever they wish.
      VersionAttribute = class(ColumnAttribute)
      protected
        FStartValue : Integer;
        function GetIsVersionColumn: Boolean; override;
      public
        constructor Create(StartValue : Integer = 0); overload;
        constructor Create(properties: TColumnProperties; StartValue : Integer = 0); overload;
        constructor Create(properties: TColumnProperties;
          length, precision, scale: Integer; const description: string = ''; StartValue : Integer = 0); overload;
        constructor Create(const columnName: string; properties: TColumnProperties = []; StartValue : Integer = 0); overload;
        constructor Create(const columnName: string; properties: TColumnProperties;
          length, precision, scale: Integer; const description: string = ''; StartValue : Integer = 0); overload;
        constructor Create(const columnName: string; properties: TColumnProperties;
          length: Integer; const description: string = ''; StartValue : Integer = 0); overload;
        constructor Create(const columnName: string; properties: TColumnProperties;
          precision, scale: Integer; const description: string = ''; StartValue : Integer = 0); overload;
        property StartValue : Integer read FStartValue write FStartValue;
      end;
    
    Implementation
    
    { VersionAttribute }
    
    constructor VersionAttribute.Create(StartValue : Integer);
    begin
      inherited Create('_version');
      FStartValue := StartValue;
    end;
    
    constructor VersionAttribute.Create(const columnName: string;
      properties: TColumnProperties; StartValue : Integer);
    begin
      inherited create(ColumnName, Properties);
      FStartValue := StartValue;
    end;
    
    constructor VersionAttribute.Create(properties: TColumnProperties; length,
      precision, scale: Integer; const description: string; StartValue : Integer);
    begin
      inherited Create('_version', properties, length, precision, scale, description);
      FStartValue := StartValue;
    end;
    
    constructor VersionAttribute.Create(properties: TColumnProperties; StartValue : Integer);
    begin
      inherited Create('_version', properties);
      FStartValue := StartValue;
    end;
    
    constructor VersionAttribute.Create(const columnName: string;
      properties: TColumnProperties; precision, scale: Integer;
      const description: string; StartValue : Integer);
    begin
      inherited Create(ColumnName, properties, Precision, Scale, Description);
      FStartValue := StartValue;
    end;
    
    constructor VersionAttribute.Create(const columnName: string;
      properties: TColumnProperties; length: Integer; const description: string; StartValue : Integer);
    begin
      inherited Create(ColumnName, Properties, Length, Description);
      FStartValue := StartValue;
    end;
    
    constructor VersionAttribute.Create(const columnName: string;
      properties: TColumnProperties; length, precision, scale: Integer;
      const description: string; StartValue : Integer);
    begin
      inherited Create(ColumnName, Properties, Length, Scale, Description);
      FStartValue := StartValue;
    end;
    
    function VersionAttribute.GetIsVersionColumn: Boolean;
    begin
      Result := True;
    end;
    

    Also the Spring.Persistence.SQL.Commands.Insert.pas unit has to be modified to use this new Start Value feature.

    procedure TInsertExecutor.Execute(const entityWrapper: IEntityWrapper);
    var
      statement: IDBStatement;
      resultSet: IDBResultSet;
      queryText: string;
      value: TValue;
    begin
      if EntityData.HasVersionColumn then
        entityWrapper.SetColumnValue(EntityData.VersionColumn, EntityData.VersionColumn.StartValue);
    
      value := GetAutogeneratedPrimaryKeyValue(entityWrapper);
      if not value.IsEmpty then
        entityWrapper.SetPrimaryKeyValue(value);
      BuildParams(entityWrapper.GetEntity);
      queryText := GetInsertQueryText;
      statement := Connection.CreateStatement;
      statement.SetSQLCommand(queryText);
      statement.SetParams(SQLParameters);
      resultSet := statement.ExecuteQuery(false);
      value := GetIdentityValue(resultSet);
      if not value.IsEmpty then
        entityWrapper.SetPrimaryKeyValue(value);
    end;
    

    So then with this in place the user can do the following when defining the version column.

        [Version('ROW_VERSION',[cpRequired,cpNotNull],10,0,'',1)]
        property RowVersion: Int64 read FRowVersion write FRowVersion;
    

    And it works awesomely.

  2. Todd Flora reporter

    Linas,

    Can you please comment on this suggestion? In order for our existing row_version column to be used for this we need these changes as the following is true for us:

    1) We use a column name of row_version that is already built into all our tables, not _version. 2) We always start our version number at 1 not 0.

    Thanks for your effort.

    Todd.

  3. Linas Naginionis

    I don't think we need so many constructor overloads for such a simple attribute. I guess we could provide one additional overload where you would set column's name and initial value.

  4. Todd Flora reporter

    OK that would be sufficient, but since the version attribute is a column with special purpose it seemed to make sense to me anyway that the version attribute work the same way as a column. That is why I exposed all of the Column attributes constructors.

    I get it that you would likely never set the Scale, since it has to be an integer anyway. Also it should not be null etc. so it seems that the column options would not need to be changed, so if you just want to add the ability to name the column and give a start value this would satisfy our current requirement.

  5. Log in to comment