Skip to content

Instantly share code, notes, and snippets.

@rdingwall
Created September 19, 2012 10:07
Show Gist options
  • Save rdingwall/3748819 to your computer and use it in GitHub Desktop.
Save rdingwall/3748819 to your computer and use it in GitHub Desktop.
StandaloneSeqHiloGenerator - ported from NHibernate
using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlTypes;
using System.Runtime.CompilerServices;
using Oracle.DataAccess.Client;
namespace Foo
{
/// <summary>
/// Ported from NHibernate's SequenceHiLoGenerator - an ID generator that
/// combines a hi/lo algorithm with an underlying oracle-style sequence
/// that generates hi values.
/// </summary>
public class StandaloneSeqHiloGenerator
{
private readonly string sequenceName;
private readonly int maxLo;
private readonly SqlString sql;
private int lo;
private long hi;
public string SequenceName
{
get { return sequenceName; }
}
public StandaloneSeqHiloGenerator(string sequenceName, int maxLo)
{
if (String.IsNullOrWhiteSpace(sequenceName))
throw new ArgumentException("A sequence name is required.", "sequenceName");
this.sequenceName = sequenceName;
this.maxLo = maxLo;
lo = maxLo + 1; // so we "clock over" on the first invocation
sql = new SqlString(String.Format("select {0}.nextval from dual", sequenceName));
}
[MethodImpl(MethodImplOptions.Synchronized)]
public long Generate(OracleConnection connection)
{
if (connection == null) throw new ArgumentNullException("connection");
if (maxLo < 1)
{
//keep the behavior consistent even for boundary usages
var val = Convert.ToInt64(GetNextVal(connection));
if (val == 0)
val = Convert.ToInt64(GetNextVal(connection));
return val;
}
if (lo > maxLo)
{
var hival = Convert.ToInt64(GetNextVal(connection));
lo = 1;
hi = hival * (maxLo + 1);
}
return hi + lo++;
}
private object GetNextVal(OracleConnection connection)
{
try
{
using (var cmd = connection.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = sql.ToString();
return cmd.ExecuteScalar();
}
}
catch (DbException sqle)
{
throw new Exception(String.Format("could not get next sequence value from {0}", sequenceName), sqle);
}
}
}
}
using NUnit.Framework;
using Oracle.DataAccess.Client;
using SharpTestsEx;
namespace Foo.Tests
{
public class StandaloneSeqHiloGeneratorTests
{
[TestFixture, Category("Integration")]
public class When_generating_ids
{
[Test]
public void It_should_generate_new_ids()
{
var generator = new StandaloneSeqHiloGenerator("test_seq", maxLo: 10);
var builder = new OracleConnectionStringBuilder
{
DataSource = "(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=10000)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)))",
UserID = "....",
Password = "..."
};
using (var connection = new OracleConnection(builder.ConnectionString))
{
connection.Open();
var initialValue = generator.Generate(connection);
var nextValue = generator.Generate(connection);
nextValue.Should().Be(initialValue + 1);
for (var i = 0; i < 19; i++)
nextValue = generator.Generate(connection);
nextValue.Should().Be.GreaterThan(initialValue + 20);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment