Iridio
Iridio

Reputation: 9271

MongoDB and derived class

I am having trouble saving a particular class with MongoDB.

My abstract class is as follow:

public abstract class Enumeration : IComparable
{
public string Name { get; }
public int Id { get; }

protected Enumeration(int id, string name)
{
  Id = id;
  Name = name;
}

public override string ToString() => Name;

public static IEnumerable<T> GetAll<T>() where T : Enumeration
{
  var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);

  return fields.Select(f => f.GetValue(null)).Cast<T>();
}

public override bool Equals(object obj)
{
  var otherValue = obj as Enumeration;

  if (otherValue == null)
    return false;

  var typeMatches = GetType().Equals(obj.GetType());
  var valueMatches = Id.Equals(otherValue.Id);

  return typeMatches && valueMatches;
}

public override int GetHashCode() => Id.GetHashCode();

public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
{
  var absoluteDifference = Math.Abs(firstValue.Id - secondValue.Id);
  return absoluteDifference;
}

public static T FromValue<T>(int value) where T : Enumeration
{
  var matchingItem = Parse<T, int>(value, "value", item => item.Id == value);
  return matchingItem;
}

public static T FromDisplayName<T>(string displayName) where T : Enumeration
{
  var matchingItem = Parse<T, string>(displayName, "display name", item => item.Name == displayName);
  return matchingItem;
}

private static T Parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration
{
  var matchingItem = GetAll<T>().FirstOrDefault(predicate);

  if (matchingItem == null)
    throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}");

  return matchingItem;
}

public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id);
}

The derived class:

public class SeatState : Enumeration
{
public static SeatState Free = new SeatState(1, nameof(Free).ToLowerInvariant());
public static SeatState Booked = new SeatState(2, nameof(Booked).ToLowerInvariant());
public static SeatState Reserved = new SeatState(3, nameof(Reserved).ToLowerInvariant());

public static IEnumerable<SeatState> List() => new[] { Free, Booked, Reserved };

public SeatState(int id, string name)
  : base(id, name)
{
}

public static SeatState FromName(string name)
{
  var state = List().SingleOrDefault(s => string.Equals(s.Name, name, StringComparison.CurrentCultureIgnoreCase));

  if (state == null)
    throw new Exception($"Possible values for SeatState: {string.Join(",", List().Select(s => s.Name))}");

  return state;
}

public static SeatState From(int id)
{
  var state = List().SingleOrDefault(s => s.Id == id);

  if (state == null)
    throw new Exception($"Possible values for SeatState: {string.Join(",", List().Select(s => s.Name))}");

  return state;
}
}

When I try to save a class that have SeatState as a property, mongo throw this error:

System.ArgumentOutOfRangeException: The memberInfo argument must be for class SeatState, but was for class Enumeration.

I tried also with BsonClassMap.RegisterClassMap<Enumeration>() (with and without IsRootClass) and BsonClassMap.RegisterClassMap<SeatState>() but without success. It seems that the problem lie in the static properties, but I need them.

Any clarification in what I am doing wrong is greatly appreciated.

Upvotes: 0

Views: 1580

Answers (1)

Iridio
Iridio

Reputation: 9271

For others who may have the same problem, the solution is as follow:

BsonClassMap.RegisterClassMap<Enumeration>(cm =>
{
  cm.SetIsRootClass(true);
  cm.MapMember(m => m.Id);
  cm.MapMember(m => m.Name);
});
BsonClassMap.RegisterClassMap<SeatState>(cm =>
{
  cm.MapCreator(c => new SeatState(c.Id, c.Name));
});

Upvotes: 3

Related Questions