1

How I can generete primary key by object's properties? I need something like hash. But stable hash;

public Object1 {
   public Object1(string property1, DateTime property2)
   {
      Property1 = property1;
      Property2 = property2;
      StableHashID = GetStableHash();
   }
   
   [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
   public int StableHashID { get; private set; } 

   public string Property1 { get; private set; }

   public DateTime Property2 { get; private set; }

   public int GetStableHash()
   {
      return ...; // ???
   }

}
xxramm
  • 149
  • 8
  • 1
    Can you explain better? – Iria Jul 13 '20 at 15:04
  • StableHashID must be unique and computed by properties. Standart function GetHashCode return different values after restart application. So, I need stable hash in database for event defenitions. – xxramm Jul 13 '20 at 15:05
  • Hashes are not unique. You can use incremental GUID with `[DatabaseGenerated(DatabaseGeneratedOption.Identity)]`. Also, when it's database generated, EF will be smarter (e.g creating both INSERT and Update queries with `Update(entity)`). Hash cannot be "stable" in the meaning of unique – Nikolai Jul 13 '20 at 15:07
  • The ONLY reason to use a guid as PK is in case you need to merge 2 databases in the future. A guid PK is measurable slower and takes up more disk space. Why do you need to do it? – Neil Jul 13 '20 at 15:13

4 Answers4

2

Guid is enough for you. If you will use Id column just an index, Guid.NewGuid() would generate a great hash for you.

Guid.NewGuid() makes an actual guid with a unique value, what you probably want.

Guid.NewGuid() vs. new Guid()

However, if you find object values after decryption of hash, you can check;

Encrypting & Decrypting a String in C#

1

Without reflection:

   public int GetStableHash()
   {
      var now = DateTime.Now;
      var nowInt = (int) now.Kind;
      var nowTicks = (ulong) now.Ticks;
      return (((ulong) nowInt) << 62) | nowTicks;
   }

With properties: https://stackoverflow.com/a/12393880

EDIT:

But I still think that GUID is good enough - auto-generated, incremental, have benefits with EFCore (eg. Update method), etc.

Fluent API:

public override void Configure(EntityTypeBuilder<Entity> entityBuilder)
{
      entityBuilder.Property(entity => entity.Id).HasDefaultValueSql("NEWID()");   
}

Or with annotation: [DatabaseGenerated(DatabaseGeneratedOption.Identity)]

Nikolai
  • 555
  • 7
  • 17
1

You want to generate primary keys based on other properties values, then you need to generate different hash values from these two properties values in GetStableHash method.

  public class Object1
    {
        public Object1(string property1, DateTime property2)
        {
            Property1 = property1;
            Property2 = property2;
            StableHashID = GetStableHash();
        }

        [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int StableHashID { get; private set; }

        public string Property1 { get; private set; }

        public DateTime Property2 { get; private set; }

        public int GetStableHash()
        {
            var result = default(byte[]);

            using (var stream = new MemoryStream())
            {
                using (var writer = new BinaryWriter(stream, Encoding.UTF8, true))
                {
                    writer.Write(Property2.Ticks);
                    writer.Write(Property1);
                    writer.Write(Guid.NewGuid().ToString());
                }

                stream.Position = 0;

                using (var hash = SHA256.Create())
                {
                    result = hash.ComputeHash(stream);
                }
            } 

            return BitConverter.ToInt32(result, 0); 
        }
LouraQ
  • 6,443
  • 2
  • 6
  • 16
-1

I find another solutions.

 public override int GetStableHash()
        {
            int result = GetDeterministicHashCode(ErrorCode.ToString());
            result = (result * 397) ^ (GetDeterministicHashCode(Property1.ToString()));
            result = (result * 397) ^ (GetDeterministicHashCode(Property2.ToString()));
            return result;
        }

// https://stackoverflow.com/questions/5154970/how-do-i-create-a-hashcode-in-net-c-for-a-string-that-is-safe-to-store-in-a
        private int GetDeterministicHashCode(string str)
        {
            unchecked
            {
                int hash1 = (5381 << 16) + 5381;
                int hash2 = hash1;

                for (int i = 0; i < str.Length; i += 2)
                {
                    hash1 = ((hash1 << 5) + hash1) ^ str[i];
                    if (i == str.Length - 1)
                        break;
                    hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
                }
                return hash1 + (hash2 * 1566083941);
            }
        }
xxramm
  • 149
  • 8