Created
March 15, 2010 20:49
-
-
Save gamlerhart/333317 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
// 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()); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public Post NewPost() | |
{ | |
var post = new Post(this); | |
AddPost(post); | |
return post; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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