Ef Core 3.1 Generate primary key by properties - c#

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 ...; // ???
}
}

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#

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)]

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

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

Related

Class equal with if on one property

I have a class that I use to describe a XYZ coordinate along with 3 properties.
The class looks like this:
class dwePoint
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as dwePoint);
}
protected bool Equals(dwePoint other)
{ //This doesnt seem to work
if(Prop1== "Keep")
{
return false;
}
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = X.GetHashCode();
hashCode = (hashCode * 397) ^ Y.GetHashCode();
hashCode = (hashCode * 397) ^ Prop1.GetHashCode();
hashCode = (hashCode * 397) ^ Z.GetHashCode();
return hashCode;
}
}
}
Checking the XYZ on the Equals, I can filter out duplicates only based on the actual coordinates, ignoring the properties.
In my code, I use a list, so I call the List.Distinct()
Now there is one thing I cant figure out yet:
It is possible there are 2 points with the same XYZ, but with different properties.
In that case I always want to keep the one with a specific string (for example "Keep") and always remove the one that has some other value.
I was already trying some if statements, without any luck...
How should I handle this ?
What you want is not really possible with Distinct since it uses your Equals as it's only input for equality (as it should), so it has no way to even be aware that there could be a difference between the objects.
I think it would be a better design for you to compose your class using a new class, e.g. Point3D containing your coordinates and your 3 properties. Then you can group by the point, and for everything that has more than one equal point, apply your own logic as to which to keep.
In code:
class Point3D
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
// Equals and get hash code here
}
class dwePoint
{
Point3D Coordinate {get;}
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
// Filter list by applying grouping and your custom logic
points = points.GroupBy(p => p.Coordinate)
.Select(x =>
x.OrderByDescending(p => p.Prop1 == "Keep") // Sort so the element you want to keep is first
.First() // If there is only one element, the ordering will not matter
).ToList();
If you really want, the GroupBy also works with your current class design, since only the coordinates takes part in the Equals.
Your GetHashCode runs into a nullref if Prop1 == null, you should fix that.
And another solution: use Aggregate and Lamda to distinct your list. The Equal() on your class only compares the X,Y and Z - the Aggregate lambda makes sure you keep what you want. Probably put the Aggregate in a extension method or Function.
static void Main()
{
List<dwePoint> points = new List<dwePoint>();
// Testdata
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
for (int z = 0; z < 3; z++)
{
points.Add(new dwePoint { X = x, Y = y, Z = z });
if (x == y && x == z) // and some duplicates to Keep
points.Add(new dwePoint { X = x, Y = y, Z = z, Prop1 = "Keep" });
}
// prefer the ones with "Keep" in Prop1
var distincts = points.Aggregate(new HashSet<dwePoint>(), (acc, p) =>
{
if (acc.Contains(p))
{
var oldP = acc.First(point => point.X == p.X && point.Y == p.Y && point.Z == p.Z);
if (oldP.Prop1 == "Keep")
{
// do nothing - error, second point with "keep"
}
else
{
acc.Remove(oldP);
acc.Add(p); // to use this ones other props later on ....
}
}
else
acc.Add(p);
return acc;
}).ToList();
Console.WriteLine(string.Join(" - ", points));
Console.WriteLine(string.Join(" - ", distincts));
Console.ReadLine();
}
private class dwePoint
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as dwePoint);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = X.GetHashCode();
hashCode = (hashCode * 397) ^ Y.GetHashCode();
hashCode = (hashCode * 397) ^ Z.GetHashCode();
return hashCode;
}
}
public override string ToString() => $"{X}-{Y}-{Z}-{Prop1}-{Prop2}-{Prop3}";
protected bool Equals(dwePoint other)
{
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
}
I had the same answer in mind as driis provided..
But, I would like to provide you with another option.
You can write a Distinct on your own as an extension method.
Here is a work around. I wrote a sample code, so that you can understand better.
static class Program
{
static void Main(string[] args)
{
List<Abc> list = new List<Abc>()
{
new Abc()
{
a = 5,
b = 6,
s = "Phew"
},
new Abc()
{
a = 9,
b = 10,
s = "Phew"
},
new Abc()
{
a = 5,
b = 6,
s = "Keep"
},
new Abc()
{
a = 9,
b = 10,
s = "Keep"
},
new Abc()
{
a = 5,
b = 6,
s = "Phew"
},
new Abc()
{
a = 9,
b = 10,
s = "Phew"
},
};
list = list.MyDistinct();
}
// Extension Method
public static List<Abc> MyDistinct(this List<Abc> list)
{
List<Abc> newList = new List<Abc>();
foreach (Abc item in list)
{
Abc found = newList.FirstOrDefault(x => x.Equals(item));
if (found == null)
{
newList.Add(item);
}
else
{
if (found.s != "Keep" && item.s == "Keep")
{
newList.Remove(found);
newList.Add(item);
}
}
}
return newList;
}
}
class Abc
{
public int a, b;
public string s;
public override bool Equals(object obj)
{
Abc other = obj as Abc;
return a == other.a && b == other.b;
}
public override int GetHashCode()
{
return a.GetHashCode() ^ b.GetHashCode();
}
}
Hope it will help you..

How to use Collection in loop

Small doubt, In Update method I had int count, let say count == 4, then i am using for loop to get id, version and set.
In this case Id,version and set values getting only the last value, but how to get the all the values
I tried but i feel its wrong and not working,
Created a seperate list for id, version and set,
eg: _details.imageList.Add(logoHeader.LogoID);
public void Updates(AUnit _aUnit, int Id)
{
ImageDetails _details = new ImageDetails(_aUnit, Id);
int count = (int) _aUnit.ReadBits(8);
for (int i = 0; i < (int) count; i++)
{
_details.ID = (int) _aUnit.ReadBits(8);
_details.Version = (int) _aUnit.ReadBits(8);
_details.set = (int) _aUnit.ReadBits(24);
}
_details.Rset = _aUnit.Buffer.Skip(10).Take(_details.set).ToArray();
//MemoryStream ms = new MemoryStream(_details.PortrateImages.First());
//Image image = Image.FromStream(ms);
//Bitmap bmp = new Bitmap(image);
_details.UpdateTime = DateTime.Now.ToString("h:mm:ss tt");
newData.Add(_details);
}
public class ImageDetails
{
public ImageDetails(AUnit _au, int carouselId)
{
carId = carouselId;
_AUnit = _au;
_updateTime = "";
}
private string _updateTime;
public int ID { get; set; }
public int Version { get; set; }
public int set { get; set; }
public int carId { get; set; }
public byte[] Rset { get; set; }
public AUnit _AUnit { get; set; }
public byte[] bytes { get; set; }
public List<byte[]> dataArray = new List<byte[]>();
public string UpdateTime
{
get { return _updateTime; }
set { _updateTime = value; }
}
public List<byte[]> PImages
{
get
{
List<byte[]> Plogos = new List<byte[]>();
if (carId == 2)
{
Plogos.Add(Rset);
}
return Plogos;
}
}
public List<byte[]> LImages
{
get
{
List<byte[]> Llogos = new List<byte[]>();
if (carId == 1)
{
Llogos.Add(Rset);
}
return Llogos;
}
}
}
You could make detail an object(class) and make a list of details.
like,
public class Detail
{
public int Id{get;set;}
public int Version{get;set;}
public int set{get;set;}
}
and make a list of detail like,
var Details = new List<Detail>();
and you could access it using the foreach loop. like,
foreach(var detail in Details){
Console.log(detail.Id)
.....
}
hope this helps.

Serialize POCO to Simple Byte Array

I have a need to serialize the following type
public class MspMessage {
[DataMember(Order = 1), ProtoMember(1)]
public byte[] Prefix { get; private set; }
[DataMember(Order = 2), ProtoMember(2)]
public MspMessageType MessageType { get; private set; }
[DataMember(Order = 3), ProtoMember(3)]
public int DataLength { get; private set; }
[DataMember(Order = 4), ProtoMember(4)]
public MspCodes MspCode { get; private set; }
[DataMember(Order = 5), ProtoMember(5)]
public byte[] Data { get; private set; }
[DataMember(Order = 6), ProtoMember(6)]
public byte Checksum
{
get; private set;
}
public MspMessage(MspMessageType messageType, MspCodes mspCode, byte[] data) {
Prefix = new byte[] { 36, 77 };
MessageType = messageType;
MspCode = mspCode;
Data = data;
DataLength = data?.Length ?? 0;
if (data == null || data.Length == 0) {
Checksum = (byte)(0 ^ (int)MspCode);
} else {
var checksum = data.Length ^ (int)MspCode;
checksum = data.Aggregate(checksum, (current, t) => current ^ t);
Checksum = (byte)checksum;
}
}
}
Into a simple binary array like this: {36, 77, 60, 0, 1, 1}. I hacked around with BinaryFormatter and protobuf-net, but both produce added output besides the pure contents of the type. What is the most efficient way to go about this? It's my intention to create derived types that will have additional fields. Thanks!
I wound up writing my own IFormatter which essentially is the same thing as a ToByteArray() and FromByteArray(). Thanks!

How to fix a StackOverflow exception in Get/Set?

Bear in mind that this is my first day of learning C#; this is also my first compiled language, so I am absolutely not used to the concept of 'types'. I am coming from Python.
It seems strongly typed languages are an order of magnitude more difficult than Python... v_v'
I get a StackOverflow exception, but I don't know why:
static void Main(string[] args)
{
Dictionary<string, int> init = new Dictionary<string,int>();
init["stepId"] = 250;
init["offset"] = 13;
init["inc"] = 113;
init["danger"] = 0;
StepGenerator gen = new StepGenerator(init);
Console.Write("Next: {0}", gen.Step["stepId"]);
Console.Read();
The error is seemingly here:
public Dictionary<string, int> Step
{
get { return Step; }
set
{
Dictionary<string, int> step = value;
// It complains about this line, and suggests an infinite recursion.
step["rnd"] = genRnd(rnlut[step["stepId"]], step["offset"]);
step["limit"] = genDangerLimit(step["rnd"]);
step["enc"] = genEnc(step["danger"], step["limit"]);
Step = step;
}
Full:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace rePlanner
{
public class StepGenerator
{
// snipped array size:
private static readonly int[] rnlut = new int[] {0xB1}
enum Input
{
B,
G,
};
enum Encounter
{
No,
Yes,
}
enum Type
{
Run,
Walk,
Stutter,
Unit,
}
public Dictionary<string, int> Step
{
get { return Step; }
set
{
Dictionary<string, int> step = value;
step["rnd"] = genRnd(rnlut[step["stepId"]], step["offset"]);
step["limit"] = genDangerLimit(step["rnd"]);
step["enc"] = genEnc(step["danger"], step["limit"]);
Step = step;
}
}
internal int stepId {get; set;}
internal int offset { get; set;}
internal int danger { get; set; }
internal int rnd { get; set; }
internal int dangerLimit { get; set; }
internal int enc { get; set; }
internal int type { get; set; }
internal int units { get; set; }
public int input { get; set; }
// Constructor.
public StepGenerator(Dictionary<string, int> step)
{
this.Step = step;
}
private int genStepId(int oldStepId)
{
return (oldStepId + 2) & 0xFF;
}
private int genOffset(int oldOffset, int stepId)
{
if (stepId == 0)
{
int offset = (oldOffset + 13) & 0xFF;
}
else
{
offset = oldOffset;
}
return offset;
}
private int genDanger(int oldDanger, int oldInc)
{
danger = oldDanger;
danger += oldInc;
if (danger > 65535)
{
danger = oldInc;
}
return danger;
}
private int genRnd(int rnlut, int offset)
{
return (rnlut - offset) & 0xFF;
}
private int genDangerLimit(int rnd)
{
return ((rnd + 1) * 256);
}
private int genEnc(int danger, int dangerLimit)
{
if(danger > dangerLimit)
{
return (int)Encounter.Yes;
}
return (int)Encounter.No;
}
public Dictionary<string, int> next()
{
Dictionary<string, int> step = this.Step;
step["stepId"] = genStepId(this.Step["stepId"]);
step["offset"] = genOffset(this.Step["offset"], this.Step["stepId"]);
step["danger"] = genDanger(this.Step["danger"], this.Step["inc"]);
step["rnd"] = genRnd(rnlut[step["stepId"]], step["offset"]);
step["limit"] = genDangerLimit(step["rnd"]);
step["enc"] = genEnc(step["danger"], step["limit"]);
this.Step = step;
return step;
}
}
}
You're calling the setter on Step repeatedly with with line
Step = step;
This causes infinite recursion.
I think that you need to make your Step property the getter and setter for a private step member variable. First of all, remove
Dictionary<string, int> step = value;
from the Step setter. Make step a private member variable of StepGenerator:
private Dictionary<string, int> step;
Change your Step property to be:
public Dictionary<string, int> Step
{
get { return this.step; }
set
{
this.step = value;
this.step["rnd"] = genRnd(rnlut[step["stepId"]], step["offset"]);
this.step["limit"] = genDangerLimit(step["rnd"]);
this.step["enc"] = genEnc(step["danger"], step["limit"]);
}
}

How to create Hash of on instance?

I have a Product class,
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ModelNumber { get; set; }
public string Sku { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public double NewPrice { get; set; }
}
I am saving this class in my database table. But I also need to save the hash of the each object in my database table for change tracking. What I am looking is that,
var p1 = new Product{
Id =2,
Name = "1",
ModelNumber = "4"
};
var p2 = new Product
{
Id = 2,
Name = "1",
ModelNumber = "4"
};
var hashOfp1 = Hash(p1);
var hashOfp2 = Hash(p2);
// This should return true because both objects have same values
if(hashOfp1 == hashOfp2){
}
A good for this kind of things is :
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ModelNumber { get; set; }
public string Sku { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public double NewPrice { get; set; }
public override int GetHashCode()
{
return Id ^ (Name ?? "").GetHashCode() ^ (ModelNumber ?? "").GetHashCode() ^ (Sku ?? "").GetHashCode()^ (Description ?? "").GetHashCode() ^ Price.GetHashCode() ^ NewPrice.GetHashCode();
}
}
The idea is to combine hashes of all sub properties... you could choose any combination you wish, but "xor" operator is quite a good choice because it prevents your hash to tend to something as you add new properties to your hash (like a "&" or "+" operator would)
edit
Quick and dirty explanation for the "tend to something" part, as requested:
Let's assume you chose A & B & C & D ... to hash values A, B, C etc.
The most properties you add to your hash, the more chance you will have to have a huge hash that tends to int.MaxValue (which corresponds to 11111111111111111111111111111111, binary)
Same thing with "+"... your hashes will get bigger and bigger, not using the full spectrum of int values.
... A good hash algorithm is just about maximizing the chances to have a different hash for different values. To do that, you can either study how values are used in real life (can be painful and overkill), or maximize the spectrum of int values that your hash algorithm covers for random unknown values.
You should override the GetHashCode() method of the base class Object.
In this overriden method you can create a hash code based on the Id or other properties.
You can use it then like:
var hashOfp1 = p1.GetHashCode();
If you override GetHashCode() you should also override Equals(object). Code courtesy of ReSharper
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ModelNumber { get; set; }
public string Sku { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public double NewPrice { get; set; }
protected bool Equals(Product other)
{
return Id == other.Id && string.Equals(Name, other.Name) &&
string.Equals(ModelNumber, other.ModelNumber) &&
string.Equals(Sku, other.Sku) && string.Equals(Description, other.Description) &&
Price.Equals(other.Price) && NewPrice.Equals(other.NewPrice);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != this.GetType())
{
return false;
}
return Equals((Product) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Id;
hashCode = (hashCode*397) ^ (Name != null ? Name.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (ModelNumber != null ? ModelNumber.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (Sku != null ? Sku.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (Description != null ? Description.GetHashCode() : 0);
hashCode = (hashCode*397) ^ Price.GetHashCode();
hashCode = (hashCode*397) ^ NewPrice.GetHashCode();
return hashCode;
}
}
}

Categories

Resources