Detecting changes within serializable data classes in C# - c#

I've been experimenting with detecting changes in plain objects in C#. The aim being to have a container-type class for a bunch of data objects that can react when any one of them changes. For fun I wanted to see if all the work could be done in the container class, rather than resort to properties and dirty flags or events on the objects themselves.
What I'm curious about is whether there is a smart, fast and efficient way of doing this. My attempt is below, and it's none of those (the 'CheckStates' method would need to be called every frame for a start!) I've restricted it to only allow one instance per type, which suits my needs.
Note that an object passed in might be as follows:
[Serializable]
public class PlayerInfo
{
public string name = string.Empty;
public int score = 0;
}
Then the container:
public class AppState
{
private class StateData
{
public System.Object instance = null;
public Byte[] currentState = new Byte[0];
public Byte[] previousState = new Byte[0];
}
private Dictionary<Type, StateData> _allStates = new Dictionary<Type, StateData>();
private BinaryFormatter _formatter = new BinaryFormatter();
private MemoryStream _memoryStream = new MemoryStream();
public T GetState<T>() where T : class, new()
{
T state = default(T);
var stateType = typeof(T);
StateData stateData;
if(_allStates.TryGetValue(stateType, out stateData))
{
state = ReadData<T>(stateData);
}
else
{
var newState = CreateData<T>(out state);
_allStates[stateType] = newState;
}
return state;
}
public void CheckStates()
{
foreach(var state in _allStates)
{
if(HasChanged(state.Value))
{
Console.WriteLine(state.Key.ToString() + " has changed");
UpdateState(state.Value);
}
}
}
private StateData CreateData<T>(out T instance) where T : class, new()
{
instance = new T();
var stateData = new StateData();
stateData.instance = instance;
_formatter.Serialize(_memoryStream, instance);
var bytes = _memoryStream.ToArray();
stateData.currentState = bytes;
stateData.previousState = bytes;
return stateData;
}
private T ReadData<T>(StateData data) where T : class, new()
{
return data.currentState as T;
}
private bool HasChanged(StateData data)
{
_memoryStream.Position = 0;
_formatter.Serialize(_memoryStream, data.instance);
var current = _memoryStream.ToArray();
var previous = data.previousState;
if(current.Length != previous.Length)
{
return true;
}
for(int i = 0; i < current.Length; ++i)
{
if(current[i] != previous[i])
{
return true;
}
}
return false;
}
private void UpdateState(StateData data)
{
_memoryStream.Position = 0;
_formatter.Serialize(_memoryStream, data.instance);
data.previousState = _memoryStream.ToArray();
}
}
Alternatives I could think of were:
use structs instead of serializable classes (being forced to pass by value would mean that any change would have to go through a 'set' method on the container)
have the AppState's 'GetState' method return an IDisposable wrapper, which on Dispose could trigger a check for changes on that type (only problem is that there's nothing to stop someone from storing a reference to the object and modifying it without the container knowing)
EDIT: should add that it doesn't need to be thread-safe

I don't regard serializable classes as POCO, because you're engineering the classes so that they work with your change detection mechanism. So I wouldn't call them plain.
Your alternatives:
use structs instead of serializable classes
Don't use mutable structs Why are mutable structs “evil”?. And if your struct is immutable, then you might as well pass by reference, i.e. have a class.
have the 'get' method return an IDisposable wrapper
I'm not sure what get method you are referring to.
Proxy
One alternative is to allow a descendant proxy to react to calls to the setters:
public class PlayerInfo
{
public virtual string Name { get; set; }
public virtual int Score { get; set; }
}
public class PlayerInfoDetection : PlayerInfo
{
public int Revision { get; private set; }
public override string Name
{
set
{
base.Name = value;
Revision++;
}
}
public override int Score
{
set
{
base.Score = value;
Revision++;
}
}
}
private static void Example()
{
PlayerInfo pi = new PlayerInfoDetection();
Console.WriteLine(((PlayerInfoDetection)pi).Revision);
pi.Name = "weston";
Console.WriteLine(((PlayerInfoDetection)pi).Revision);
pi.Score = 123;
Console.WriteLine(((PlayerInfoDetection)pi).Revision);
}
This is how NHibernate "watches" objects fetched from the database, and why every object property must be virtual in NHibernate.
Aspect orientated
The same could be achieved with a product like post sharp where you could annotate your class to tell it when the revision must be changed.
public class PlayerInfo
{
public int Revision { get; private set; }
public string Name { get; [IncreaseRevision] set; }
public int Score { get; [IncreaseRevision] set; }
}
Making use of a well implemented hash function
Hash functions should not change their value while the object is in a container such as a hash set. We can make use of this to detect changes.
Drawback Note that any Hash collisions will yield incorrect results. This includes duplicates.
[TestClass]
public class ChangeDetectUnitTest
{
public class ChangeDetectList<T>
{
private readonly List<T> list = new List<T>();
private readonly ISet<T> hashes = new HashSet<T>();
public bool HasChanged(T t)
{
return !hashes.Contains(t);
}
public void Add(T t)
{
list.Add(t);
hashes.Add(t);
}
public void Reset()
{
hashes.Clear();
foreach (var t in list)
hashes.Add(t);
}
}
public class PlayerInfo
{
public string Name { get; set; }
public int Score { get; set; }
public override int GetHashCode()
{
//every field that you want to detect must feature in the hashcode
return (Name ?? "").GetHashCode() * 31 + Score;
}
public override bool Equals(object obj)
{
return Equals(obj as PlayerInfo);
}
public bool Equals(PlayerInfo other)
{
if (other == null) return false;
return Equals(other.Name, Name) && Score == Score;
}
}
private ChangeDetectList<PlayerInfo> list;
[TestInitialize]
public void Setup()
{
list = new ChangeDetectList<PlayerInfo>();
}
[TestMethod]
public void Can_add()
{
var p1 = new PlayerInfo();
list.Add(p1);
Assert.IsFalse(list.HasChanged(p1));
}
[TestMethod]
public void Can_detect_change()
{
var p1 = new PlayerInfo();
list.Add(p1);
p1.Name = "weston";
Assert.IsTrue(list.HasChanged(p1));
}
[TestMethod]
public void Can_reset_change()
{
var p1 = new PlayerInfo();
list.Add(p1);
p1.Name = "weston";
list.Reset();
Assert.IsFalse(list.HasChanged(p1));
}
}

Related

Dbset as hashset in the Entity Framework

I need to merge data from tables in the database with data based on some logic from third-party sources. I implemented this logic via hashset, for which I overloaded the GetHashCode and Equals methods for entities. Now I don't understand how I can save the result of work in the database via DbSet, with subsequent data loading and subsequent merging (the task of merging/supplementing is periodic)
The directories are quite voluminous, so working through hashset speeds up the process.
class Program
{
private class DummyDbContext { public void SaveChangesAsync() { }}
static void Main(string[] args)
{
var dbContext = new DummyDbContext(); // TODO: Get from DI
// TODO: I don't know how to do it yet with HashSets
var currentFactories = LoadCurrentFactoriesFromDb(dbContext);
var currentProducts = LoadCurrentProductsFromDb(dbContext);
var thirdPartyData = GetThirdPartyData();
foreach (var data in thirdPartyData)
{
/*
In reality, the logic is more complicated, because some data transformation is required.
Some data may be missing. That is why comparing two objects is not quite easy (see the method Product.Equals)
*/
var factory = new Factory(data.otherFactory.Name);
var product = new Product(data.otherProduct.Property1, data.otherProduct.Property2, factory);
if (currentFactories.TryGetValue(factory, out var existedFactory))
factory = existedFactory;
else
currentFactories.Add(factory);
if (currentProducts.TryGetValue(product, out var existedProduct))
{
if (!existedProduct.Factory.Equals(factory))
throw new InvalidOperationException(); // TODO:
product = existedProduct;
factory.Products.Add(product); // TODO:
}
else
currentProducts.Add(product);
}
// **how to implement the saving of combined directories, in hashsets, in the database ?**
dbContext.SaveChangesAsync();
}
private static IEnumerable<(ThirdPartyFactory otherFactory, ThirdPartyProduct otherProduct)> GetThirdPartyData()
{
return new (ThirdPartyFactory otherFactory, ThirdPartyProduct otherProduct)[]
{
( new ThirdPartyFactory () {Name = "SomeFactory"}, new ThirdPartyProduct() {Property1 = "ProductName1"}),
( new ThirdPartyFactory () {Name = "SomeFactory"}, new ThirdPartyProduct() {Property1 = "ProductName2"}),
( new ThirdPartyFactory () {Name = "SomeFactory"}, new ThirdPartyProduct() {Property2 = "Property1"})
};
}
private static HashSet<Factory> LoadCurrentFactoriesFromDb(DummyDbContext context)
{
// DbContext.DbSet<Factory>.GetAll()
return new HashSet<Factory>();
}
private static HashSet<Product> LoadCurrentProductsFromDb(DummyDbContext context)
{
// DbContext.DbSet<Product>.GetAll()
return new HashSet<Product>();
}
}
public class Product
{
public Product(string property1, string property2, Factory factory)
{
Property1 = property1;
Property2 = property2;
Factory = factory;
}
public long Id { get; set; }
public string Property1 { get; }
public string Property2 { get; }
public Factory Factory { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
var product = (Product) obj;
return (string.IsNullOrWhiteSpace(Property1) && string.IsNullOrWhiteSpace(product.Property1)
|| string.CompareOrdinal(this.Property1, product.Property1) == 0)
&& (string.IsNullOrWhiteSpace(Property2) && string.IsNullOrWhiteSpace(product.Property2)
|| string.CompareOrdinal(this.Property2, product.Property2) == 0);
}
public override int GetHashCode()
{
return HashCode.Combine(Property1, Property2).GetHashCode();
}
}
public class Factory
{
public Factory(string name)
{
Name = name;
}
public long Id { get; set; }
public string Name { get; }
public HashSet<Product> Products { get; set; }
}
public class ThirdPartyProduct
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class ThirdPartyFactory
{
public string Name { get; set; }
}
Is it possible to implement this ? Or do I need to convert data from DbSet to HashSet and then back ? But won't I lose information about entities inside the context during such transformations ?

Create game inventory system, without casting to derived

I am trying to implement a high-performance game inventory system. I have This abstract base class to store different type of items in Inventory, for example, Coin, Flashlight, Knife etc..
public abstract class ObtainableItem
{
public string Name { get; private set; }
public ObtainableItem(string name)
{
Name = name;
}
}
For example, I have a DoorKey which opens a door. DoorKey has a property KeyCode which will be used for opening a door.
public class DoorKey : ObtainableItem
{
public int KeyCode { get; private set; }
public DoorKey() : base("key")
{
KeyCode = 1234;
}
}
All ObtainableItem are stored in Inventory
public class Inventory
{
const int slotCount = 2;
ObtainableItem[] slots = new ObtainableItem[slotCount];
public Inventory()
{
slots[0] = new DoorKey();
}
}
Now imagine user drags DoorKey from his Inventory on a Door and triggers Open method
public class Door
{
public void Open(ObtainableItem key)
{
if (key is DoorKey)
{
DoorKey doorKey = (DoorKey)key;
if (doorKey.KeyCode == 1234)
{
// Open door
}
}
else
{
// "can't use this item on a door"
}
}
}
How to avoid cast from ObtainableItem to a DoorKey? I have read that using casting is bad practice and it points at a bad code oop design. Ideally, a Door class should look like this. Is there any pattern I should for my inventory system?
public class Door
{
public void Open(DoorKey key)
{
if (key.KeyCode == 1234)
{
// Open door
}
}
}
There are always exceptions that can be made for ease of implementation and readability. What you describe is common, if not typical.
An alternative would be to have the "control" logic in the class that calls Door.Open. This could be easily achieved with a touch of reflection:
public abstract class ObtainableItem
{
public string Name { get; private set; }
public ObtainableItem(string name)
{
Name = name;
}
}
public abstract class WorldItem
{
}
public interface IActsOn<in TWorldItem>
where TWorldItem : WorldItem
{
void ApplyTo(TWorldItem worldItem);
}
public class World
{
// If profiling shows that this is a performance issue, a cache keyed by tWorldItem, tInvItem
// should fix it. No expiry or invalidation should be needed.
private Action<ObtainableItem, WorldItem> GetApplyTo(Type tWorldItem, Type tInvItem)
{
var tActOn = typeof(IActsOn<>).MakeGenericType(tWorldItem);
if (!tActOn.IsAssignableFrom(tInvItem))
{
return null;
}
var methodInfo = tActOn.GetMethod(nameof(IActsOn<WorldItem>.ApplyTo));
return new Action<ObtainableItem, WorldItem>((invItem, worldItem) =>
{
methodInfo.Invoke(invItem, new object[] { worldItem });
});
}
public bool IsDropTarget(WorldItem worldItem, ObtainableItem item)
=> GetApplyTo(worldItem.GetType(), item.GetType()) != null;
public void ActOn(WorldItem worldItem, ObtainableItem item)
{
var actOn = GetApplyTo(worldItem.GetType(), item.GetType());
if (actOn == null)
{
throw new InvalidOperationException();
}
actOn(item, worldItem);
}
}
While this slightly complicates the implementation of World, it simplifies the implementation of various objects:
class Door : WorldItem
{
public void Unlock(string bitting)
{
if (bitting == "1234")
{
Console.WriteLine("Door Opened");
}
else
{
Console.WriteLine("Door could not unlock");
}
}
}
class DoorKey : ObtainableItem, IActsOn<Door>
{
private readonly string Bitting;
public DoorKey(string bitting)
: base("Key")
{
this.Bitting = bitting;
}
public void ApplyTo(Door worldItem)
{
worldItem.Unlock(this.Bitting);
}
}
class RubberChicken : ObtainableItem
{
public RubberChicken()
: base("Rubber chicken")
{
}
}
Example usage:
class Program
{
static void Main(string[] args)
{
var key1 = new DoorKey("1234");
var key2 = new DoorKey("4321");
var rubberChicken = new RubberChicken();
var door = new Door();
var world = new World();
Debug.Assert(!world.IsDropTarget(door, rubberChicken));
Debug.Assert(world.IsDropTarget(door, key1));
world.ActOn(door, key2);
world.ActOn(door, key1);
Console.ReadLine();
}
}

Auto-properties with mutable objects

I'm trying to make properties for mutable objects. Is this a problem with Auto-properties? For example, the following code would allow for unwanted manipulation of the mutable object. How would I avoid this?
public class Mutable{
public int Value { get; set; }
}
public class ClassWithMutable{
public Mutable Object { get; }
public ClassWithMutable(){
this.mutable = new Mutable();
this.mutable.Value = 0;
}
}
public class Demo{
public static void Main(String[] args){
ClassWithMutable test = new ClassWithMutable();
Mutable o = test.Object;
o.Value = 1;
}
}
You could use an interface that only exposes the get of the properties, and a private class that implements it.
public interface IImmutable {
int Value { get; }
}
public class ClassWithImmutable{
private Mutable _object;
public IImmutable Object { get { return _object; } }
public ClassWithImmutable(){
this._object = new Mutable();
this._object.Value = 0;
}
private class Mutable : IImmutable {
public int Value { get; set; }
}
}
public class Demo{
public static void Main(String[] args){
ClassWithImmutable test = new ClassWithImmutable();
IImmutable o = test.Object;
o.Value = 1; // fails
}
}
I'm trying to understand the intent of your question rather than your question, and I'm coming up a little short. However, I think I came up with something.
You can "mask" your mutable object under a read-only interface.
public class ClassWithMutable
{
public IImumutable Mutable { get { return _mutable; } }
private Mutable _mutable;
public ClassWithMutable()
{
_mutable = new Mutable()
{
Value = 1
};
}
}
public interface IImumutable
{
int Value { get; }
}
public class Mutable : IImumutable
{
public int Value { get; set; }
}
As long as your ClassWithMutable instance exposes the Mutable instance as an Immutable then the consumer can't easily change it. (I emphasize easily, because there's pretty much always a way that you can change it. It just depends on how hard you want to work.)

What data structure is appropriate for this?

Within code I want to do something like this:
item.Stage = Stage.Values.ONE;
Where Stage.Values.ONE represents some predefined Stage:
public class Stage
{
[Key]
public virtual int StageId { get; set; }
public string Name { get; set; }
public TimeSpan Span { get; set; }
}
I'm dealing with EF CodeFirst... and I have a lot of stages to define. I'm not sure if I should store the data in the database, or in the dbContext, or what, but I'm looking for the simplest implementation.
I've tried this:
I've tried the following (defining two constants):
public class Stage
{
[Key]
public virtual int StageId { get; set; }
public string Name { get; set; }
public TimeSpan Span { get; set; }
public static class Values
{
public static readonly Stage ONE = new Stage()
{
StageId = 0,
Name = "ONE",
Span = new TimeSpan(0, 0, 0)
};
public static readonly Stage TWO = new Stage()
{
StageId = 1,
Name = "TWO",
Span = new TimeSpan(0, 0, 10)
};
}
But whenever I create a new instance of an entity that has a Stage, a new Stage is added to the db. I just need a few constant stages.
Use of Stage:
public class Side
{
public Side()
{
Stage = Stage.Values.ONE; // Adds new Stage to DB, when it should be a reference to the one I defined above
}
public virtual Stage Stage { get; set; }
}
It looks a bit like an enum, and I've used a kind of 'extended enum' patter several times before with some success. Because you're refencing these values in code, it may not make sense to store them in the database as well, but it's possible if needed.
The technique is described in detail here: http://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/
Basically, you create a base class which provides a number of services similar to an enum, and then to create your "enumerated class" you inherit from it and provide a bunch of static instances which call the constructor with however many properties you need to have.
To avoid link rot, here is the base class to use (just put the whole class into your project somewhere), and scroll down for your own code.
public abstract class Enumeration : IComparable
{
private readonly int _value;
private readonly string _displayName;
protected Enumeration()
{
}
protected Enumeration(int value, string displayName)
{
_value = value;
_displayName = displayName;
}
public int Value
{
get { return _value; }
}
public string DisplayName
{
get { return _displayName; }
}
public override string ToString()
{
return DisplayName;
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var info in fields)
{
var instance = new T();
var locatedValue = info.GetValue(instance) as T;
if (locatedValue != null)
{
yield return locatedValue;
}
}
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromValue<T>(int value) where T : Enumeration, new()
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new ApplicationException(message);
}
return matchingItem;
}
public int CompareTo(object other)
{
return Value.CompareTo(((Enumeration)other).Value);
}
}
And now your code will look something like this:
public class Stage : Enumeration
{
public TimeSpan TimeSpan { get; private set; }
public static readonly Stage One
= new Stage (1, "Stage one", new TimeSpan(5));
public static readonly Stage Two
= new Stage (2, "Stage two", new TimeSpan(10));
public static readonly Stage Three
= new Stage (3, "Stage three", new TimeSpan(15));
private EmployeeType() { }
private EmployeeType(int value, string displayName, TimeSpan span) : base(value, displayName)
{
TimeSpan = span;
}
}
Once you have that set up, you can just store the .Value in the database. I'm afraid I haven't done it in EF, but in nHibernate it's reasonably straight-forward to tell a property to just store the ".Value" of the property, and you can wire it back up when you load the value by having it call:
Stage.FromValue<Stage>(intValue);
Hold the Stage as a property of your entity, use it the way you're doing and add
Ignore(x => x.Stage)
to your mapping. This will ignore this property when mapping to your database.
Edit: I misinterpreted the question.
If you want just the different stages in your database, you should put the stages in their own table with an ID, and refer to that ID trough a relationship. Every entity will hold an additional reference and you'll have to define relationships for them.
Is this what you were looking for?

Many to Many relationship in object design

I have a similar problem like this :
Many to many object to object relation in C#
However, imagine that the Championship would have a "Last Date Played" property (just as an example) that would map to any Participant. In this case, where would that property end up? Is it a must to create an intermediate class? (which i wouldn't want to do) what option do i have? thanks!
One way would be to have an array on each object containing pointers to the other objects either via an dictionary that stores the object as key and date as value (or a custom property class for any number of properties) or using a wrapper class around the object and a plain list, this wrapper should then implement the decorator pattern to allow direct access to the object together with any unique properties.
The wrapper object could use an internal object for the properties that is shared between the oposing wrapper objects for the 2 different objects so that any property is in sync.
Another way would be a separate list of pairs where one is wrapped like the above.
The later makes it easy to loop over all objects.
Here is a code example, it might not be exactly what you need but it might give you the basics of my idea.
void Main()
{
var p = new Player("David");
var c = new Championship("Chess");
p.LinkChampionship(c, DateTime.Now);
p.Dump();
}
// Define other methods and classes here
class Player : Properties {
public virtual String Name {get; set;}
public List<ChampionshipWrapper> champs = new List<ChampionshipWrapper>();
public Player() {
}
public Player(string name) {
Name = name;
}
public void LinkChampionship(Championship champ, DateTime when) {
var p = new Properties(when);
champs.Add(new ChampionshipWrapper(champ, p));
champ.players.Add(new PlayerWrapper(this, p));
}
}
class Championship : Properties {
public virtual String Name { get; set; }
public List<PlayerWrapper> players = new List<PlayerWrapper>();
public Championship(){}
public Championship(string name) {
Name = name;
}
public void LinkPlayer(Player play, DateTime when) {
var p = new Properties(when);
players.Add(new PlayerWrapper(play, p));
play.champs.Add(new ChampionshipWrapper(this, p));
}
}
class Properties {
public virtual DateTime LastPlayed { get; set; }
public Properties() {
}
public Properties(DateTime when) {
LastPlayed = when;
}
}
class PlayerWrapper : Player {
private Player player;
private Properties props;
public PlayerWrapper(Player play, Properties prop) {
this.player = play;
this.props = prop;
}
public override String Name {
get { return this.player.Name; }
set { this.player.Name = value; }
}
public override DateTime LastPlayed {
get { return this.props.LastPlayed; }
set { this.props.LastPlayed = value; }
}
}
class ChampionshipWrapper : Championship {
private Championship champ;
private Properties props;
public ChampionshipWrapper(Championship c, Properties prop) {
this.champ = c;
this.props = prop;
}
public override String Name {
get { return this.champ.Name; }
set { this.champ.Name = value; }
}
public override DateTime LastPlayed {
get { return this.props.LastPlayed; }
set { this.props.LastPlayed = value; }
}
}

Categories