Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made BsonMapper inheritance friendly. Derived BsonMapper provides more control over Entity Mapping #1212

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions LiteDB.Tests/CustomMapper/Custom_Mapper_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using LiteDB.Tests.CustomMapper.Types;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace LiteDB.Tests.CustomMapper
{
[TestClass]
public class Custom_Mapper_Tests
{
private BsonMapper _mapper;

[TestInitialize]
public void Initialize()
{
_mapper = new CollectionMapperClass();
}

private ItemCollection CreateCollection()
{
var items = new ItemCollection()
{
MyItemCollectionName = "MyCollection"
};
items.Add(new Item()
{
MyItemName = "MyItem"
});
return items;
}
[TestMethod]
public BsonDocument ShouldSerializeCollectionClass()
{
var items = CreateCollection();

var document = _mapper.ToDocument(items);
Assert.AreEqual("MyCollection", (string)document["MyItemCollectionName"]);

var array = (BsonArray)document["_items"];
Assert.IsNotNull(array);
var recoveritem = (BsonDocument)array[0];
Assert.AreEqual("MyItem", (string)recoveritem["MyItemName"]);
return document;
}

[TestMethod]
public void ShouldDeserializeCollectionClass()
{

var items = CreateCollection();
var document = _mapper.ToDocument(items);


var lst = (ItemCollection)_mapper.Deserialize(typeof(ItemCollection), document);

Assert.AreEqual("MyCollection", lst.MyItemCollectionName);
Assert.AreEqual(lst.Count, 1);
Assert.IsInstanceOfType(lst[0], typeof(Item));
Assert.AreEqual(lst[0].MyItemName,"MyItem");
}

[TestMethod]
public void ShouldInsertIntoDatabaseAndRecover()
{
var items = CreateCollection();
using (var repository = new LiteRepository(new MemoryStream(), _mapper))
{
var result= repository.Upsert<ItemCollection>(items);
Assert.IsTrue(result);
Assert.AreNotEqual(Guid.Empty,items.Id);
var lst = repository.SingleById<ItemCollection>(items.Id);
Assert.AreEqual("MyCollection", lst.MyItemCollectionName);
Assert.AreEqual(lst.Count, 1);
Assert.IsInstanceOfType(lst[0], typeof(Item));
Assert.AreEqual(lst[0].MyItemName, "MyItem");
}
}
}
}
42 changes: 42 additions & 0 deletions LiteDB.Tests/CustomMapper/Types/CollectionMapperClass.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LiteDB.Tests.CustomMapper.Types;

namespace LiteDB.Tests.CustomMapper
{
public class CollectionMapperClass:BsonMapper
{
public override object Deserialize(Type type, BsonValue value)
{
if (type.GetInterfaces().Any(s => s == typeof(ICollectionClass)))
return DeserializeCollectionObject(type, (BsonDocument)value);
return base.Deserialize(type, value);
}

private object DeserializeCollectionObject(Type type, BsonDocument value)
{
var array = base.Deserialize(type,(BsonArray) value["_items"]);
DeserializeObject(type,array,value);
return array;
}

public override BsonValue Serialize(Type type, object obj, int depth)
{
if (obj is ICollectionClass)
{
return SerializeCollectionClass(type,obj,depth);
}
return base.Serialize(type, obj, depth);
}

private BsonDocument SerializeCollectionClass(Type type, object obj, int depth)
{
var doc = SerializeObject(type, obj, depth);
doc["_items"] = SerializeArray(GetListItemType(type),(IEnumerable) obj, depth);
return doc;
}
}
}
11 changes: 11 additions & 0 deletions LiteDB.Tests/CustomMapper/Types/ICollectionClass.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace LiteDB.Tests.CustomMapper.Types
{
public interface ICollectionClass
{

}
}
18 changes: 18 additions & 0 deletions LiteDB.Tests/CustomMapper/Types/Item.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace LiteDB.Tests.CustomMapper.Types
{
public class Item
{
public Guid Id { get; set; }
public string MyItemName { get; set; }
}

public class ItemCollection : List<Item>, ICollectionClass
{
public Guid Id { get; set; }
public string MyItemCollectionName { get; set; }
}
}
48 changes: 37 additions & 11 deletions LiteDB/Mapper/BsonMapper.Deserialize.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
Expand Down Expand Up @@ -71,8 +71,13 @@ internal T Deserialize<T>(BsonValue value)
};

#endregion

internal object Deserialize(Type type, BsonValue value)
/// <summary>
/// Deserilize an object and it's members. It's the entry point of deserialization procedure
/// </summary>
/// <param name="type"></param>
/// <param name="value"></param>
/// <returns></returns>
public virtual object Deserialize(Type type, BsonValue value)
{
Func<BsonValue, object> custom;

Expand Down Expand Up @@ -187,8 +192,13 @@ internal object Deserialize(Type type, BsonValue value)
// it's used for "public object MyInt { get; set; }"
return value.RawValue;
}

private object DeserializeArray(Type type, BsonArray array)
/// <summary>
/// Deserializes an array member of a class.
/// </summary>
/// <param name="type">Type of array </param>
/// <param name="array">BsonArray to be deserialized</param>
/// <returns>Deserialized Object</returns>
protected virtual object DeserializeArray(Type type, BsonArray array)
{
var arr = Array.CreateInstance(type, array.Count);
var idx = 0;
Expand All @@ -200,8 +210,13 @@ private object DeserializeArray(Type type, BsonArray array)

return arr;
}

private object DeserializeList(Type type, BsonArray value)
/// <summary>
/// Deserializes a list member of a class.
/// </summary>
/// <param name="type">Type of list </param>
/// <param name="array">BsonArray to be deserialized</param>
/// <returns>Deserialized Object</returns>
protected virtual object DeserializeList(Type type, BsonArray value)
{
var itemType = Reflection.GetListItemType(type);
var enumerable = (IEnumerable)Reflection.CreateInstance(type);
Expand All @@ -226,8 +241,14 @@ private object DeserializeList(Type type, BsonArray value)

return enumerable;
}

private void DeserializeDictionary(Type K, Type T, IDictionary dict, BsonDocument value)
/// <summary>
/// Deserializes a Dictionary
/// </summary>
/// <param name="K">Keys</param>
/// <param name="T">Dictionary Value Type</param>
/// <param name="dict">Dictionary Object</param>
/// <param name="value">BsonValue to be deserialize</param>
protected virtual void DeserializeDictionary(Type K, Type T, IDictionary dict, BsonDocument value)
{
foreach (var key in value.Keys)
{
Expand All @@ -237,8 +258,13 @@ private void DeserializeDictionary(Type K, Type T, IDictionary dict, BsonDocumen
dict.Add(k, v);
}
}

private void DeserializeObject(Type type, object obj, BsonDocument value)
/// <summary>
/// Deserialize an Object
/// </summary>
/// <param name="type">Type of object</param>
/// <param name="obj">instance of deserialized object </param>
/// <param name="value">BsonValue for copying to instance of object</param>
protected virtual void DeserializeObject(Type type, object obj, BsonDocument value)
{
var entity = this.GetEntityMapper(type);

Expand Down
53 changes: 43 additions & 10 deletions LiteDB/Mapper/BsonMapper.Serialize.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -28,8 +28,14 @@ public virtual BsonDocument ToDocument<T>(T entity)
{
return this.ToDocument(typeof(T), entity).AsDocument;
}

internal BsonValue Serialize(Type type, object obj, int depth)
/// <summary>
/// Entry point on serialization procedure
/// </summary>
/// <param name="type">Type of object to be serialized</param>
/// <param name="obj">Instance of the object to be serialized</param>
/// <param name="depth">Depth of the recursive call</param>
/// <returns>Serialized BsonValue</returns>
public virtual BsonValue Serialize(Type type, object obj, int depth)
{
if (++depth > MAX_DEPTH) throw LiteException.DocumentMaxDepth(MAX_DEPTH, type);

Expand Down Expand Up @@ -109,16 +115,31 @@ internal BsonValue Serialize(Type type, object obj, int depth)
// check if is a list or array
else if (obj is IEnumerable)
{
return this.SerializeArray(Reflection.GetListItemType(obj.GetType()), obj as IEnumerable, depth);
return this.SerializeArray(GetListItemType(obj), obj as IEnumerable, depth);
}
// otherwise serialize as a plain object
else
{
return this.SerializeObject(type, obj, depth);
}
}

private BsonArray SerializeArray(Type type, IEnumerable array, int depth)
/// <summary>
/// Get the Type of ListItem
/// </summary>
/// <param name="obj">Instance of List</param>
/// <returns>Type of ListItem</returns>
protected virtual Type GetListItemType(object obj)
{
return Reflection.GetListItemType(obj.GetType());
}
/// <summary>
/// Serializes an Array
/// </summary>
/// <param name="type">Type of <b>ListItem</b></param>
/// <param name="array">Instance of the array to be serialized</param>
/// <param name="depth">Depth of the recursive call</param>
/// <returns></returns>
protected virtual BsonArray SerializeArray(Type type, IEnumerable array, int depth)
{
var arr = new BsonArray();

Expand All @@ -129,8 +150,14 @@ private BsonArray SerializeArray(Type type, IEnumerable array, int depth)

return arr;
}

private BsonDocument SerializeDictionary(Type type, IDictionary dict, int depth)
/// <summary>
/// Serialize a Dictionary
/// </summary>
/// <param name="type">Type of object to be serialized</param>
/// <param name="dict">Instance of the dictionary to be serialized</param>
/// <param name="depth">Depth of the recursive call</param>
/// <returns></returns>
protected virtual BsonDocument SerializeDictionary(Type type, IDictionary dict, int depth)
{
var o = new BsonDocument();

Expand All @@ -143,8 +170,14 @@ private BsonDocument SerializeDictionary(Type type, IDictionary dict, int depth)

return o;
}

private BsonDocument SerializeObject(Type type, object obj, int depth)
/// <summary>
/// Serializes a class object
/// </summary>
/// <param name="type">Type of object to be serialized</param>
/// <param name="obj">Instance of the object to be serialized</param>
/// <param name="depth">Depth of the recursive call</param>
/// <returns></returns>
protected virtual BsonDocument SerializeObject(Type type, object obj, int depth)
{
var o = new BsonDocument();
var t = obj.GetType();
Expand Down