Skip to content

Instantly share code, notes, and snippets.

@gamlerhart
Created March 15, 2010 20:49
Show Gist options
  • Save gamlerhart/333317 to your computer and use it in GitHub Desktop.
Save gamlerhart/333317 to your computer and use it in GitHub Desktop.
{
// when you add the post, its author will be set
var author = new Author();
var post = new Post();
author.AddPost(post);
AssertEqual(author, post.Author);
}
{
// when you set the author of the post, the post is added to the author
var author = new Author();
var post = new Post()
{
Author = author
};
AssertEqual(post, author.Posts.Single());
// when you remove a post, it's author isn't set any more
author.RemovePost(post);
AssertEqual(null, post.Author);
}
{
// when you add a tag, the post will be added to the tag
var post = new Post();
var tag = new Tag();
post.AddTag(tag);
AssertEqual(post,tag.Posts.First());
}
{
// when you add a post to a tag, the post will have the tag
var tag = new Tag();
var post = new Post();
tag.AddPost(post);
AssertEqual(tag, post.Tags.First());
// and removing also is bidirectional
tag.RemovePost(post);
AssertEqual(false, post.Tags.Any());
}
// upgraded author-property
public Author Author
{
get { return _author; }
set { _author = value.AddToParent(this,()=>value.AddPost); }
}
// upgraded AddPost-method on the Author-class
public void AddPost(Post post)
{
_posts.AddAsChild(post, c => c.Author = this);
}
public bool RemoveChild(Child item)
{
return children.RemoveAsChild(item, c => c.Parent = null);
}
public class Author
{
private readonly ICollection<Post> _posts = new HashSet<Post>();
public void AddPost(Post post)
{
this._posts.Add(post);
}
public bool RemoveChild(Child item)
{
return children.Remove(item);
}
public IEnumerable<Post> Posts
{
get { return _posts; }
}
public string FirstName { get; set; }
public string SurName { get; set; }
}
public class Post
{
private Author _author;
public Post(Author author)
{
Author = author;
}
public Post()
{
}
public Author Author
{
get { return _author; }
set { _author = value; }
}
public string Text
{
get; set;
}
}
public Post NewPost()
{
var post = new Post(this);
AddPost(post);
return post;
}
// the upgraded post-methods
public void AddTag(Tag item)
{
_tags.AddAsChild(item,t=>t.AddPost(this));
}
public bool RemoveTag(Tag item)
{
return _tags.RemoveAsChild(item, t => t.RemovePost(this));
}
// the upgraded tag-methods
public bool RemovePost(Post item)
{
return _posts.RemoveAsChild(item,p=>p.RemoveTag(this));
}
public IEnumerable<Post> Posts
{
get { return _posts; }
}
public class Post
{
private readonly ICollection<Tag> _tags = new HashSet<Tag>();
public void AddTag(Tag item)
{
_tags.Add(item);
}
public bool RemoveTag(Tag item)
{
return _tags.Remove(item);
}
public string Text
{
get; set;
}
public IEnumerable<Tag> Tags
{
get { return _tags; }
}
}
public class Tag
{
private readonly ICollection<Post> _posts = new HashSet<Post>();
public void AddPost(Post item)
{
_posts.Add(item);
}
public bool RemovePost(Post item)
{
return _posts.Remove(item);
}
public IEnumerable<Post> Posts
{
get { return _posts; }
}
}
public static class RelationManagementExtensions
{
[ThreadStatic]
private static bool inAddToParentCall;
[ThreadStatic]
private static readonly ICollection<Tuple<object,object>> InChildAction = new HashSet<Tuple<object, object>>();
public static TParent AddToParent<TChild, TParent>(this TParent parent, TChild child, Func<Action<TChild>> addToParentAction) where TParent:class
{
try
{
if(inAddToParentCall)
{
return parent;
}
inAddToParentCall = true;
if (null != parent)
{
addToParentAction()(child);
}
} finally
{
inAddToParentCall = false;
}
return parent;
}
public static TValue AddAsChild<TValue>(this ICollection<TValue> children, TValue child, Action<TValue> setterOnChild)
{
return ChildAction(child, setterOnChild,child, ()=>
{
children.Add(child);
return child;
});
}
public static bool RemoveAsChild<TValue>(this ICollection<TValue> children,
TValue child, Action<TValue> setterOnChild)
{
return ChildAction(child, setterOnChild, true, () => children.Remove(child));
}
private static TReturn ChildAction<TValue, TReturn>(TValue child, Action<TValue> setterOnChild, TReturn noActionReturn, Func<TReturn> action)
{
var id = new Tuple<object, object>(child, noActionReturn);
try
{
if (InChildAction.Contains(id))
{
return noActionReturn;
}
InChildAction.Add(id);
setterOnChild(child);
return action();
}
finally
{
InChildAction.Remove(id);
}
}
}
[TestFixture]
public class TestRelationManagementExtensions
{
[Test]
public void TestAddToParent()
{
var parent = new object();
var child = new object();
object expected = null;
Action<object> expectCall = o => expected = o;
var returnValue = parent.AddToParent(child,()=> expectCall);
Assert.AreSame(parent,returnValue);
Assert.AreSame(child, expected);
}
[Test]
public void PreventRecursionAddParent()
{
var parent = new object();
var child = new object();
Action<object> expectCall2 = null;
Action<object> expectCall = o => parent.AddToParent(child, () => expectCall2);
expectCall2 = o => parent.AddToParent(child, () => expectCall);
expectCall(child);
}
[Test]
public void TestAddAsChild()
{
var child = new object();
var coll = new List<object>();
object expected = null;
Action<object> expectCall = o => expected = o;
var returnValue = coll.AddAsChild(child,expectCall);
Assert.AreSame(child, returnValue);
Assert.AreSame(child, expected);
Assert.Contains(child, coll);
}
[Test]
public void PreventRecursionAddAsChild()
{
var coll = new List<object>();
var child = new object();
Action<object> expectCall2 = null;
Action<object> expectCall = o => coll.AddAsChild(child, expectCall2);
expectCall2 = o => coll.AddAsChild(child, expectCall);
expectCall(child);
}
[Test]
public void TestRemoveAsChild()
{
var child = new object();
var coll = new List<object> {child};
object expected = null;
Action<object> expectCall = o => expected = o;
var returnValue = coll.RemoveAsChild(child, expectCall);
Assert.IsTrue(returnValue);
Assert.AreSame(child, expected);
Assert.IsFalse(coll.Contains(child));
}
[Test]
public void AddToParent()
{
var parent = new Parent();
var child = new Child(parent);
Assert.Contains(child,parent.Children.ToArray());
Assert.AreSame(parent, child.Parent);
}
[Test]
public void AddChild()
{
var parent = new Parent();
var newParent = new Parent();
var child = new Child(parent);
newParent.AddChild(child);
Assert.Contains(child, newParent.Children.ToArray());
Assert.AreSame(newParent, child.Parent);
}
[Test]
public void RemoveChild()
{
var parent = new Parent();
var child = new Child(parent);
parent.RemoveChild(child);
Assert.AreEqual(0,parent.Children.Count());
Assert.AreSame(null, child.Parent);
}
[Test]
public void ManyToManyAdd()
{
var parent = new Parent();
var child = new Child();
parent.AddManyToMany(child);
Assert.Contains(child, parent.ManyToMany.ToArray());
Assert.Contains(parent, child.ManyToMany.ToArray());
}
[Test]
public void ManyToManyRemove()
{
var parent = new Parent();
var child = new Child();
parent.AddManyToMany(child);
child.RemoveManyToMany(parent);
Assert.AreEqual(0,parent.ManyToMany.Count());
Assert.AreEqual(0, child.ManyToMany.Count());
}
class Parent
{
private readonly ICollection<Child> children = new List<Child>();
private readonly ICollection<Child> manyToMany = new List<Child>();
public IEnumerable<Child> Children
{
get { return children; }
}
public IEnumerable<Child> ManyToMany
{
get { return manyToMany; }
}
public void AddChild(Child item)
{
children.AddAsChild(item,c=>c.Parent=this);
}
public bool RemoveChild(Child item)
{
return children.RemoveAsChild(item, c => c.Parent = null);
}
public void AddManyToMany(Child item)
{
manyToMany.AddAsChild(item, c => c.AddManyToMany(this));
}
public bool RemoveManyToMany(Child item)
{
return manyToMany.RemoveAsChild(item, c => c.RemoveManyToMany(this));
}
}
class Child
{
private Parent parent;
private readonly ICollection<Parent> manyToMany = new List<Parent>();
public Child(Parent parent)
{
Parent = parent;
}
public Child()
{
}
public IEnumerable<Parent> ManyToMany
{
get { return manyToMany; }
}
public Parent Parent
{
get { return parent; }
set { parent = value.AddToParent(this, () => value.AddChild); }
}
public void AddManyToMany(Parent item)
{
manyToMany.AddAsChild(item, c => c.AddManyToMany(this));
}
public bool RemoveManyToMany(Parent item)
{
return manyToMany.RemoveAsChild(item, c => c.RemoveManyToMany(this));
}
}
}
/// <summary>
/// Handy for creating by a tuple by implicit typing
/// </summary>
public static class Tuple
{
public static Tuple<TFirst, TSecond> New<TFirst, TSecond>(TFirst first, TSecond second)
{
return new Tuple<TFirst, TSecond>(first, second);
}
public static IEqualityComparer<Tuple<T,T>> IgnoreArgOrder<T>()
{
return IgnoredArgOrder<T>.Instance;
}
class IgnoredArgOrder<T> : IEqualityComparer<Tuple<T,T>>
{
internal static readonly IEqualityComparer<Tuple<T, T>> Instance = new IgnoredArgOrder<T>();
public bool Equals(Tuple<T, T> t1, Tuple<T, T> t2)
{
return t1.Equals(t2) || OutOfOrderEquals(t1,t2);
}
private static bool OutOfOrderEquals(Tuple<T, T> t1, Tuple<T, T> t2)
{
return Equals(t1.First, t2.Second) && Equals(t1.Second, t2.First);
}
public int GetHashCode(Tuple<T, T> obj)
{
unchecked
{
return obj.First.GetHashCode() * obj.Second.GetHashCode();
}
}
}
}
/// <summary>
/// A handy tuple for returning two values
/// </summary>
/// <typeparam name="TFirst"></typeparam>
/// <typeparam name="TSecond"></typeparam>
public struct Tuple<TFirst,TSecond> : IEquatable<Tuple<TFirst, TSecond>>
{
public Tuple(TFirst first, TSecond second): this()
{
First = first;
Second = second;
}
public TFirst First { get; private set; }
public TSecond Second { get; private set; }
public bool Equals(Tuple<TFirst, TSecond> other)
{
return Equals(other.First, First) && Equals(other.Second, Second);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != typeof (Tuple<TFirst, TSecond>)) return false;
return Equals((Tuple<TFirst, TSecond>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (First.GetHashCode()*397) ^ Second.GetHashCode();
}
}
public static bool operator ==(Tuple<TFirst, TSecond> left, Tuple<TFirst, TSecond> right)
{
return left.Equals(right);
}
public static bool operator !=(Tuple<TFirst, TSecond> left, Tuple<TFirst, TSecond> right)
{
return !left.Equals(right);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment