How to set NewId() for GUID in entity framework

2020-02-01 01:22发布

I am creating asp.net mvc4 sample.In this i created Id column as GUID in Sample table of datacontext.

public class Sample
{
    [Required]
    public Guid ID { get; set; }
    [Required]
    public string FirstName { get; set; }
}

This is entity table

CreateTable(
"dbo.Samples",
 c => new
 {
     ID = c.Guid(nullable: false),
     FirstName = c.String(nullable: false)                   
 })
 .PrimaryKey(t => t.ID);

Id pass 00000000-0000-0000-0000-000000000000.

How to set newid() to GUID and where i have to set.

7条回答
倾城 Initia
2楼-- · 2020-02-01 01:50

The answer of Paul is right but the implementation of the Sequential Guid can be improved. This implementation of sequential guid increments more often and prevents same numbers if created on the same server.

To prevent link rot, the code:

public class SequentialGuid
{

    public DateTime SequenceStartDate { get; private set; }
    public DateTime SequenceEndDate { get; private set; }

    private const int NumberOfBytes = 6;
    private const int PermutationsOfAByte = 256;
    private readonly long _maximumPermutations = (long)Math.Pow(PermutationsOfAByte, NumberOfBytes);
    private long _lastSequence;

    public SequentialGuid(DateTime sequenceStartDate, DateTime sequenceEndDate)
    {
        SequenceStartDate = sequenceStartDate;
        SequenceEndDate = sequenceEndDate;
    }

    public SequentialGuid()
        : this(new DateTime(2011, 10, 15), new DateTime(2100, 1, 1))
    {
    }

    private static readonly Lazy<SequentialGuid> InstanceField = new Lazy<SequentialGuid>(() => new SequentialGuid());
    internal static SequentialGuid Instance
    {
        get
        {
            return InstanceField.Value;
        }
    }

    public static Guid NewGuid()
    {
        return Instance.GetGuid();
    }

    public TimeSpan TimePerSequence
    {
        get
        {
            var ticksPerSequence = TotalPeriod.Ticks / _maximumPermutations;
            var result = new TimeSpan(ticksPerSequence);
            return result;
        }
    }

    public TimeSpan TotalPeriod
    {
        get
        {
            var result = SequenceEndDate - SequenceStartDate;
            return result;
        }
    }

    private long GetCurrentSequence(DateTime value)
    {
        var ticksUntilNow = value.Ticks - SequenceStartDate.Ticks;
        var result = ((decimal)ticksUntilNow / TotalPeriod.Ticks * _maximumPermutations - 1);
        return (long)result;
    }

    public Guid GetGuid()
    {
        return GetGuid(DateTime.Now);
    }

    private readonly object _synchronizationObject = new object();
    internal Guid GetGuid(DateTime now)
    {
        if (now < SequenceStartDate || now > SequenceEndDate)
        {
            return Guid.NewGuid(); // Outside the range, use regular Guid
        }

        var sequence = GetCurrentSequence(now);
        return GetGuid(sequence);
    }

    internal Guid GetGuid(long sequence)
    {
        lock (_synchronizationObject)
        {
            if (sequence <= _lastSequence)
            {
                // Prevent double sequence on same server
                sequence = _lastSequence + 1;
            }
            _lastSequence = sequence;
        }

        var sequenceBytes = GetSequenceBytes(sequence);
        var guidBytes = GetGuidBytes();
        var totalBytes = guidBytes.Concat(sequenceBytes).ToArray();
        var result = new Guid(totalBytes);
        return result;
    }

    private IEnumerable<byte> GetSequenceBytes(long sequence)
    {
        var sequenceBytes = BitConverter.GetBytes(sequence);
        var sequenceBytesLongEnough = sequenceBytes.Concat(new byte[NumberOfBytes]);
        var result = sequenceBytesLongEnough.Take(NumberOfBytes).Reverse();
        return result;
    }

    private IEnumerable<byte> GetGuidBytes()
    {
        var result = Guid.NewGuid().ToByteArray().Take(10).ToArray();
        return result;
    }
}
查看更多
劫难
3楼-- · 2020-02-01 01:51

When using Entity Framework Core 2.1.1 I use:

[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UserId { get; set; }

Then in the migration, add in the defaultValueSql parameter as below:

migrationBuilder.CreateTable(
    name: "Users",
    columns: table => new
    {
         UserId = table.Column<Guid>(nullable: false, defaultValueSql: "newsequentialid()"),
         DisplayName = table.Column<string>(nullable: true)
    },
       constraints: table =>
    {
        table.PrimaryKey("PK_Users", x => x.UserId);
    });

This ensures that the sql server is responsible for generating a sequential guid which is going to be better than rolling your own.

If you do not want the downsides of using sequential guids you can use "newid()" instead.

查看更多
我欲成王,谁敢阻挡
4楼-- · 2020-02-01 01:57

I would recommend just using long for your ID type. It "just works" with and has some performance gains over GUID. But if you want to use a GUID, you should use a Sequential GUID and set it in the constructor. I would also make ID a private setter:

public class Sample
{
    public Sample() {
        ID = GuidComb.Generate();
    }
    [Required]
    public Guid ID { get; private set; }
    [Required]
    public string FirstName { get; set; }
}

Sequential GUID

public static class GuidComb
    {
        public static Guid Generate()
        {
            var buffer = Guid.NewGuid().ToByteArray();

            var time = new DateTime(0x76c, 1, 1);
            var now = DateTime.Now;
            var span = new TimeSpan(now.Ticks - time.Ticks);
            var timeOfDay = now.TimeOfDay;

            var bytes = BitConverter.GetBytes(span.Days);
            var array = BitConverter.GetBytes(
                (long)(timeOfDay.TotalMilliseconds / 3.333333));

            Array.Reverse(bytes);
            Array.Reverse(array);
            Array.Copy(bytes, bytes.Length - 2, buffer, buffer.Length - 6, 2);
            Array.Copy(array, array.Length - 4, buffer, buffer.Length - 4, 4);

            return new Guid(buffer);
        }
    }
查看更多
甜甜的少女心
5楼-- · 2020-02-01 01:57

I know that question is quite old, but if someone has such problem I suggest such solution:

protected Guid GetNewId()
{
    SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["YourConnectionString"].ConnectionString);
    var query = "select newid()";
    conn.Open();
    SqlCommand com = new SqlCommand(query, conn);
    var guid = new Guid(com.ExecuteScalar().ToString());
    conn.Close();
    return guid;
}

You can get newid from SQL database when your new object is creating. For me it works. :) (but I don't know it is good practice)

How use it:

var myNewGuidValue = GetNewId();
查看更多
别忘想泡老子
6楼-- · 2020-02-01 01:59

I encountered the same problem with Nlog logging to database . What I did is to purposely open the migration file and did the following changes

CreateTable(
"dbo.Samples",
c => new
{
     ID = c.Guid(nullable: false,identity:true),
     FirstName = c.String(nullable: false)
})
.PrimaryKey(t => t.ID);

the identity parameter actually created the table with defaultvalue as newsequentialid() in the table .

查看更多
beautiful°
7楼-- · 2020-02-01 02:00

This can also be done with attributes:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid AddressID { get; set; }
查看更多
登录 后发表回答