NOODB: Constraints

February 27, 2010

The constraint feature is working. It’s not done, but happy-path is good to go.

This clip demonstrates 4 things.

  1. Just save an object without any constraints. We’ve done that a million times before, so nothing enlightening there.
  2. Create a constraint, then save an object that fails the constraint. It goes boom.
  3. Create a constraint, then save an object that passes it. No boom.
  4. Extend #3 to re-prove #2. Another failure.

Good stuff. At least in my opinion. But then, I suppose I’m biased.

If anyone is reading this, I’d love to get some feedback.

        [TestMethod]
        public void DemoConstraint()
        {
            TestUtility.DeleteTestData();
            try
            {
                IClient client = new Client(new Server(TestUtility.DefaultUnityContainer));

                MovieReview2 review = new MovieReview2 { Author = "Jay", Comments = "yay!", Id=4 };
                Author author = new Author { Name = "Jay", Title = "Whatever" };

                // ----------------------------------------------------------------------
                // no constraint. works just fine.
                // ----------------------------------------------------------------------
                client.RegisterType(typeof(MovieReview2));
                client.Save(review);

                // ----------------------------------------------------------------------
                // add the constraint without the constraint data. it'll fail.
                // ----------------------------------------------------------------------
                TestUtility.DeleteTestData();
                client.RegisterType(typeof(MovieReview2));
                client.RegisterType(typeof(Author));

                // MovieReview2.Author is constrained by Author.Name. (We know .Name, because that's AUTHOR's key.)
                client.SetConstraint<MovieReview2, Author>("Author");
                try
                {
                    client.Save(review);
                    Assert.Fail("Should've failed.");
                }
                catch (Exception ex)
                {
                    // need a custom exception, and to elaborate on the message.
                    Assert.AreEqual("Property value violates the constraint.", ex.Message);
                }

                // ----------------------------------------------------------------------
                // now, create the constraint, an author, and a review that 
                // matches the constraint.
                // ----------------------------------------------------------------------
                client.RegisterType(typeof(MovieReview2));
                client.RegisterType(typeof(Author));

                // MovieReview2.Author is constrained by Author.Name. (We know .Name, because that's AUTHOR's key.)
                client.SetConstraint<MovieReview2, Author>("Author");

                // create the author, and the review will save.
                client.Save(author);
                client.Save(review);
                MovieReview2 reviewFromDb = client.GetObjects<MovieReview2>()[0];
                Assert.AreEqual("Jay", reviewFromDb.Author);

                // getting repetitive now. let's change the author and watch it fail 
                // because it fials the constraint.
                review.Author = "Santa";
                try
                {
                    client.Save(review);
                    Assert.Fail("Should've failed.");
                }
                catch (Exception ex)
                {
                    Assert.AreEqual("Property value violates the constraint.", ex.Message);
                }
            }
            finally
            {
                //TestUtility.DeleteTestData();
            }
        }

Advertisements

NOODB: The next step

February 26, 2010

I’m constantly changing the order in which I plan to do things, so this may change.

In the previous example, AUTHOR is an AUTHOR object. The key to AUTHOR is NAME.

I’m going to make it so that a simple property can be the key value of a particular object type without specifying the whole object.

            MovieReview2 x = new MovieReview2
            {
                Author = "Jay"
            };

Then, we just have to say “MovieReview2.Author refers to Author.Name”.


NOODB: The Walkthrough

February 26, 2010

This unit test demonstrates the current functionality of NOODB:

        [TestMethod]
        public void Walkthrough()
        {
            TestUtility.DeleteTestData();
            try
            {
                // register some types.
                // they should auto register, bu that's currently broken.
                IClient client = new Client(new Server(TestUtility.DefaultUnityContainer));
                client.RegisterType(typeof(Author));
                client.RegisterType(typeof(Movie));
                client.RegisterType(typeof(MovieReview));
                client.RegisterType(typeof(BookReview));
                client.SetObjectRule<Author>("SaveWhenNested", "0");
                client.SetObjectRule<Movie>("SaveWhenNested", "0");


                // create some authors
                // the key is Author.Name.
                // note the typo in the second one.
                // authors are ICloneable, so make sure we end up with 3 of those too
                client.Save(new Author { Name = "Jay", Title = "Some Random Guy" });
                client.Save(new Author { Name = "Gene", Title = "World Fzzamous" });
                client.Save(new Author { Name = "Roger", Title = "Guy from tv" });
                Assert.AreEqual(3, client.GetObjects<Author>().Count);
                Assert.AreEqual(3, client.GetObjects<ICloneable>().Count);

                // fix the typo. there will be 3 authors.
                client.Save(new Author { Name = "Gene", Title = "World Famous" });
                Assert.AreEqual(3, client.GetObjects<Author>().Count);

                // we can't query yet, so let's just get them all and validate
                Collection<Author> authors = client.GetObjects<Author>();
                Assert.AreEqual("World Famous", (from author in authors where author.Name == "Gene" select author.Title).Single());

                // create 4 movies
                // the key is Movie.Name
                client.Save(new Movie { Name = "Die Hard", Director = "John McTiernan" });
                client.Save(new Movie { Name = "Die Hard 2: Die Harder", Director = "Renny Harlin" });
                client.Save(new Movie { Name = "Die Hard With a Vengeance", Director = "John McTiernan" });
                client.Save(new Movie { Name = "Live Free or Die Hard", Director = "Len Wiseman" });
                Assert.AreEqual(4, client.GetObjects<Movie>().Count);

                // let's create a review.
                Console.WriteLine("-----------------------------");
                client.Save(
                    new MovieReview
                    {
                        // These are defined in "MovieReview".
                        // the key is name, so that's all we need to specify. (anything else will be ignored due to SaveIfNested=0)
                        Movie = new Movie { Name = "Die Hard" },

                        // these are defined "Review", which is the base class of "MovieReview".
                        // the key is Id.
                        Author = new Author { Name = "Jay" },
                        Rating = 5,
                        Comments = "80's action classic!",
                        Id = Guid.NewGuid()
                    });

                // the author and movie count haven't changed
                Assert.AreEqual(3, client.GetObjects<Author>().Count);
                Assert.AreEqual(4, client.GetObjects<Movie>().Count);

                // there is 1 MovieReview. Since a MovieReview is also a Review, there is also 1 review.
                Assert.AreEqual(1, client.GetObjects<MovieReview>().Count);
                Assert.AreEqual(1, client.GetObjects<Review>().Count);

                // now let's add a book review
                client.Save(
                    new BookReview
                    {
                        // these are defined in "BookReview". There should be a book object, but not yet.
                        BookTitle = "Wednesdays With Morrie's Paperboy",
                        ISBN = "ISBN",
                        // these are defined in "Review" which is the base class of "BookReview".
                        // the key is Id.
                        Comments = "like a movie, but with more reading",
                        // title is temporary
                        Author = new Author { Name = "Gene" },
                        Rating = 3,
                        Id = Guid.NewGuid()
                    });

                // 1 movie, 1 book
                // BookReview implements an interface called IBookReview, so there should be one of those too
                Assert.AreEqual(2, client.GetObjects<Review>().Count);
                Assert.AreEqual(1, client.GetObjects<BookReview>().Count);
                Assert.AreEqual(1, client.GetObjects<MovieReview>().Count);
                Assert.AreEqual(1, client.GetObjects<IBookReview>().Count);
            }
            finally
            {
                //TestUtility.DeleteTestData();
            }
        }


NOODB: Save When Nested

February 26, 2010

Suppose that you have a Movie Review object that contains an AUTHOR and a MOVIE.

When you write a review, you want to say “the author is Jay” and “the movie is highlander”. When you save the Movie Review, though, you just want to save the review, not the Movie or the Author. Those 2 things didn’t change; you’re just referencing them.

One way to handle that is to put some information on the relationship between the objects. “When the parent is saved, don’t save this child”. That may become necessary for future scenarios. But, for current scenarios, we can simplify it. If someone’s adding a new Author or Movie, then swell. Go ahead and add it. But, if it is just referenced by another object, then don’t save it. This rule is called “Save When Nested”. It defaults to 1; it will save. But, for the objects that’ll be nested, set it to zero.

Another factor is the properties of the child object. If the child saves when you save the parent, then the child has to be fully populated. In the case of AUTHOR, you would have to set the NAME (key) and the TITLE. Otherwise, title will get wiped out. We can’t have that.

Default Behavior – No Rule

        [TestMethod]
        public void ShouldUpdateByDefault()
        {
            TestUtility.DeleteTestData();

            IClient client = new Client(new Server(TestUtility.DefaultUnityContainer));
            client.RegisterType(typeof(Author));
            client.RegisterType(typeof(Movie));
            client.RegisterType(typeof(MovieReview));
            client.RegisterType(typeof(BookReview));

            client.Save(new Author { Name = "Jay", Title = "Some Random Guy" });
            client.Save(new Movie { Name = "Highlander", Director = "Russell Mulcahy" });
            MovieReview review = new MovieReview
            {
                // just specify the key.
                // as is, it will overwrite the author. TITLE isn't specfied, so it will be overwritten with null.
                Author = new Author { Name = "Jay" },
                Movie = new Movie { Name = "Highlander" }                
            };

            client.Save(review);

            MovieReview fromDb = client.GetObjects<MovieReview>()[0];
            
            // todo: the values should be NULL, not EMPTY

            Assert.AreEqual("Jay", fromDb.Author.Name);
            Assert.AreEqual(string.Empty, fromDb.Author.Title);

            Assert.AreEqual("Highlander", fromDb.Movie.Name);
            Assert.AreEqual(String.Empty, fromDb.Movie.Director);
        }


At the beginning, we create an author named JAY. When we save the movie review, we say we want to refer to that object. But, there it only specfies the name. So, when it saves, it’s going to overwrit the title with NULL. (Actually, empty space currently, but should be null). That’s not good for 2 reasons:

  1. We shouldn’t be saving at all; it’s wasteful. We only care about the reference.
  2. We don’t have all the information we need (title), so when we save, it’s wiped out.

Set the Behavior to Not Save When Nested

So, how do we treat these guys like referencees? Set the  “SaveWhenNested” property to FALSE.

            client.SetObjectRule<Author>("SaveWhenNested", "0");
            client.SetObjectRule<Movie>("SaveWhenNested", "0");

Now, you can assert to see that the values of the non-key properties have not been lost.

            Assert.AreEqual("Jay", fromDb.Author.Name);
            Assert.AreEqual("Some Random Guy", fromDb.Author.Title);

            Assert.AreEqual("Highlander", fromDb.Movie.Name);
            Assert.AreEqual("Russell Mulcahy", fromDb.Movie.Director);


NOODB: Lost Update

February 24, 2010

Greetings

Last night I posted an update, but apparently it didn’t go through. For those of you that I’ve talked to about it who may have wondered “what is he talking about???”… whoops.

The gist of it was this: Yesterday was a good milestone. Save/Update/Read are all working. I built a test demonstrating it’s full range of functionality. I didn’t post that code yet, though, because the next 2 things I work on are going to be important, and I don’t want to post the same demo (with minor changes) multiple times.

Anyway, sorry for the confusion. I will post the demo in a day or two.


NOODB: Object Data Context Update

February 23, 2010

I’ve been working on the object data context for the last two nights. It’s mostly just refactoring, but I ended up doing some re-engineering in a few places. It’s really important that the the framework objects do as much as possible, and implementations of the data contexts do as little work as possible. The result is a 2 step save process:

  1. The xml is deserialized into a graph of Graph Description objects. This maintains the relationships between the objects
  2. When it comes time to save each individual object, the Graph Description is simplified to just the property names and values which are passed to the object data context. The object data context only has to insert or update the exact values it received; no other work is needed.

That is about done. The only part left is the actual SQL part within the implementations; rewriting the inserts/updates.

Also, I’ve introduced object-type level rules. These are the rules that control the behavior when you save the graph. For example; if you’re saving a movie review where the author is JAY, you don’t have to save the author object. You only have to save the movie review and possibly update the reference to the author object. The name of that rule is “UpdateWhenNested”. It defaults to 1. In the case of author, we’ll change it to 0.

The rules are also going to be user aware. Different rules may apply to different users or different types of users. But, that’s a future task.

42 objects/interface and counting.


NOODB: Update

February 17, 2010

 

I’ve been working on 3 things

Complex Graphs

Once I started to build and test against more complex graphs, I realized the type registration was only working 100% for the type being saved, not all of the related types.

I had a complex solution at first, then I rewrote it with a simpler version, and the simpler version is the one that I found the problems with.

I ended up rewriting it two times in order to get it right. Now, the type registration builder properly parses the entire graph.

Heavy Admin Data Context

The admin data context is responsible for things like registering types. It was doing too much work. I’d simply pass it a registration object and say “save this”. It would have to work out all of the details and associations, etc.

All of that work has been moved to the server. The server contains all of the logic. All the admin data context has, as it should, is simple operations. “Create This Object Type”, “Create This Property For This Object”. It doesn’t have to resolve Ids on it’s own, or figure out if it’s an insert or update, etc. It just does what it’s told.

This is much cleaner. Now, new database contexts can be introduced very easily.

Simple / Complex Types

I was originally differentiating between simple types and complex types. Simple types are things like string, primitives and guid. The problem there is that there are probably tons more. It’s a high-maintenance branch point, so I eliminated the branch as much as possible.

This is a work in progress. It no longer needs to know if it’s simple or complex when saving the type information. But, it needs to figure it out to save the value. (Simple values are stored in columns; if it’s complex, it references another object.

Passing Tests

I have a test that builds up a couple objects, then saves it a bunch of times, swapping out the ids on each iterations. There are interfaces, base classes, etc. Since it now fully parses the graph, you get everything.

                Assert.AreEqual(Iterations, client.GetObjects<BookReview>().Count);
                Assert.AreEqual(Iterations, client.GetObjects<IBookReview>().Count);
                Assert.AreEqual(Iterations, client.GetObjects<MovieReview>().Count);
                Assert.AreEqual(Iterations * 2, client.GetObjects<Review>().Count);
                Assert.AreEqual(Iterations * 3, client.GetObjects<IDisposable>().Count);
                Assert.AreEqual(Iterations, client.GetObjects<ICloneable>().Count);
                Assert.AreEqual(Iterations * 3, client.GetObjects<Rating>().Count);
                Assert.AreEqual(Iterations * 3, client.GetObjects<IRating>().Count);

It’s not working for primitives yet.