web-dev-qa-db-fra.com

Comment définir NewId () pour GUID dans un cadre d'entité

Je crée asp.net mvc4 sample.Dans cet exemple, j'ai créé la colonne Id en tant que GUID dans la table Sample of datacontext.

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

C'est une table d'entité

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.

Comment définir newid() à GUID et où je dois définir.

11
user2285613

Je recommanderais simplement d'utiliser long pour votre type d'identifiant. Il "fonctionne" avec et a quelques gains de performances par rapport au GUID. Mais si vous voulez utiliser un GUID, vous devez utiliser un Sequential GUID et le définir dans le constructeur. Je voudrais aussi faire un identifiant un private setter:

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

GUID séquentiel

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);
        }
    }
13
Paul

Cela peut aussi être fait avec des attributs:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid AddressID { get; set; }
7
TombMedia

J'ai rencontré le même problème avec la journalisation Nlog dans la base de données. Ce que j’ai fait est d’ouvrir délibérément le fichier de migration et d’apporter les modifications suivantes

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

le paramètre d'identité a en fait créé la table avec defaultvalue comme newsequentialid() dans la table.

5
Joy

La réponse de Paul est juste mais la mise en œuvre du Guid séquentiel peut être améliorée. Cette implémentation de guid séquentielle incrémente plus souvent et empêche les mêmes numéros si elle est créée sur le même serveur.

Pour empêcher link rot , le 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;
    }
}
2
Alex Siepman

Je sais que cette question est assez ancienne, mais si quelqu'un a un tel problème, je suggère une telle 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;
}

Vous pouvez obtenir newid de la base de données SQL lors de la création de votre nouvel objet. Pour moi ça marche. :) (mais je ne sais pas que c'est une bonne pratique)

Comment l'utiliser:

var myNewGuidValue = GetNewId();
0
Monic

Si nous ignorons la politique autour de si cela est une bonne idée ou non, alors la réponse de @TombMedia est probablement ce que vous recherchez.

Toutefois, si vous devez ajouter une nouvelle colonne à une table existante et que vous souhaitez spécifier newId () à utiliser pour la valeur par défaut car le champ n'est pas nullable, utilisez-le dans votre classe de migration:

AddColumn(
    "dbo.Samples",
    "UUID", 
    c => c.Guid(nullable: false, defaultValueSql: "newId()")
);

Remarque: il s'agit d'une vieille question qui reste pertinente dans EF6 et qui occupe une place de choix lorsque vous cherchez de l'aide sur l'utilisation de newId dans les migrations EF. C'est pourquoi cette réponse a été ajoutée.

0
Chris Schaller