1

We're currently migrating our old OR Mapper to EF Core. Till now we used the http://www.castleproject.org/projects/activerecord or mapper with the HiLo algorithm. The explanations is: https://github.com/castleproject-deprecated/ActiveRecord/blob/master/docs/primary-key-mapping.md

Now we want to switch to EF Core and will try to use the same algorithm. But there isn't much explanation how the HiLo algorithm exactly works in Nhibernate/ActiveRecord. And I try to avoid Id collision.

As far as I see, the Hi value is configured in a Database: select next_hi from hibernate_unique_key with the value: 746708 I think the maxLow Value is Int16.MaxValue

In that case the Sequence for EFCore should be:

CREATE SEQUENCE [dbo].[DBSequenceHiLo] 
 AS [bigint]
 START WITH (select next_hi from hibernate_unique_key + Int16.MaxValue)
 INCREMENT BY Int16.MaxValue
 MINVALUE -9223372036854775808
 MAXVALUE 9223372036854775807
 CACHE 
GO

How does the ActiveRecord HiLo Algorithm exactly works? What is the Increment by value? What is the start with value? The migration will take some time, is it possible to run it parallel with the same HiLo algorithm?

wydy
  • 173
  • 14

1 Answers1

0

As far as I know. It's not possible to use the exact same algorithm for ActiveRecord and EF Core. One works with Sequence and the other works with a table. So you can't use both OR Mapper parallel. But you can create a Sequence for EF Core without ID collision, you just can't use ActiveRecord afterwards.

To get the INCREMENT BY value just start the current app. Create a DB Entry with the App. Stop it. Start it again and create a second entry. Because you stopped the app, the Lo/cache is empty and it gets the next hi value. The difference between those two ID's is the "INCREMENT BY" value of Active Record. It was 2^17 in my case. Default should be 2^15 I think, but I haven't seen any Infos about it.

To get a start value I created a SQL script, to get the highest Id of the database. Here is my script (It works only if your PK is named Id and it only works with sql.)

DECLARE @tables TABLE(tablename nvarchar(max) NOT NULL);
                DECLARE @name nvarchar(max)
                DECLARE @maxid bigint
                DECLARE @currentid bigint
                DECLARE @query nvarchar(max);
                        DECLARE @sSQL nvarchar(500);
                        DECLARE @ParmDefinition nvarchar(500);

                        set @maxid = 0

                insert into @tables
                SELECT TABLE_NAME
                FROM INFORMATION_SCHEMA.TABLES
                WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_CATALOG = 'BB_Vision'

                While(Select Count(*) From @tables) > 0
                Begin

                    Select Top 1 @name = tablename From @tables

                    IF EXISTS(SELECT 1 FROM sys.columns
                                WHERE Name = N'Id'

                                AND Object_ID = Object_ID(@name))

                    BEGIN
                        SELECT @sSQL = N'SELECT @retvalOUT = MAX(ID) FROM ' + @name;  
                        SET @ParmDefinition = N'@retvalOUT bigint OUTPUT';

                        EXEC sp_executesql @sSQL, @ParmDefinition, @retvalOUT=@currentid OUTPUT;

                        IF @currentid > @maxid
                        BEGIN

                            set @maxid = @currentid

                        END
                    END

                    Delete @tables Where @name = tablename
                End

                select @maxid+1

Now you can create your EF Core Sequence. Here is an explanation how to use it: https://www.talkingdotnet.com/use-hilo-to-generate-keys-with-entity-framework-core/

After that you shouldn't use ActiveRecord anymore or you have to create your sequence again with a higher start value.

Because the migration takes some time and you will mostly still create some Features/Bugfix for the current OR mapper, it's a good idea to set your ActiveRecord Hi value to a larger value on your local Database. So you can work with both on the same Database. But I wouldn't use it in production

update hibernate_unique_key set next_hi = next_hi + next_hi
wydy
  • 173
  • 14