0

I'm trying to implement cascading deletes in this application which embeds RavenDB, via the CascadeDelete RavenDB bundle, but the bundle doesn't seem to be activated. How should I go about this?

In the below code snippet is a test console application that tries to enable cascading deletes in an embedded/in-memory RavenDB database, I have installed NuGet packages RavenDB.Embedded 2.5.2505-Unstable and RavenDB.Bundles.CascadeDelete 2.5.2505-Unstable for it:

using System;
using System.Linq;
using Raven.Client;
using Raven.Client.Embedded;
using Raven.Json.Linq;

namespace ConsoleApplication1
{
    public class Parent
    {
        public string Id { get; set; }
    }

    public class Child
    {
        public string Id { get; set; }
        public string ParentId { get; set; }
    }

    public static class RavenExtensions
    {
        public static void AddCascadeDeleteReference(this IAdvancedDocumentSessionOperations session,
            object entity, params string[] documentKeys)
        {
            var metadata = session.GetMetadataFor(entity);
            if (metadata == null)
                throw new InvalidOperationException(
                  "The entity must be tracked in the session before calling this method.");

            if (documentKeys.Length == 0)
                throw new ArgumentException(
                  "At least one document key must be specified.");

            const string metadataKey = "Raven-Cascade-Delete-Documents";

            RavenJToken token;
            if (!metadata.TryGetValue(metadataKey, out token))
                token = new RavenJArray();

            var list = (RavenJArray)token;
            foreach (var documentKey in documentKeys.Where(key => !list.Contains(key)))
                list.Add(documentKey);

            metadata[metadataKey] = list;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var store = new EmbeddableDocumentStore { RunInMemory = true };
            store.Initialize();
            Parent parent;
            using (var session = store.OpenSession())
            {
                parent = new Parent();
                session.Store(parent);
                session.SaveChanges();

                var child = new Child();
                child.ParentId = parent.Id;
                session.Store(child);
                session.Advanced.AddCascadeDeleteReference(parent, child.Id);
                session.SaveChanges();
            }

            using (var session = store.OpenSession())
            {
                Console.WriteLine("Before deleting parent:");
                foreach (var child in session.Query<Child>())
                {
                    Console.WriteLine("  Child: {0} of parent {1}", child.Id, child.ParentId);
                }

                parent = session.Load<Parent>(parent.Id);
                session.Delete(parent);
                session.SaveChanges();

                Console.WriteLine("After deleting parent:");
                foreach (var child in session.Query<Child>())
                {
                    Console.WriteLine("  Child: {0} of parent {1}", child.Id, child.ParentId);
                }

                Console.WriteLine("Press any key...");
                Console.ReadKey();
            }
        }
    }
}

When you run this app, you should see that the child is not deleted with the parent, even though it's configured to through cascading delete. Please point out how I can make cascading deletes work in this program :)

Update:

I've tried adding the following after store.Initialize();, but it makes no difference:

store.DocumentDatabase.Configuration.Catalog.Catalogs.Add(
            new AssemblyCatalog(typeof(CascadeDeleteTrigger).Assembly));
aknuds1
  • 65,625
  • 67
  • 195
  • 317

2 Answers2

2

Since you are in embedded mode, you have two options:

  1. You can copy the bundle dll into the plugins directory.

  2. You can reference the bundle dll and then add it to the catalog manually:

    documentStore.DocumentDatabase.Configuration.Catalog.Catalogs
        .Add(new AssemblyCatalog(typeof(CascadeDeleteTrigger).Assembly));
    

Don't do BOTH though. If you go with option #2, the bundle dll should not be in the plugins folder, or it will get picked up twice.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • Your option #2 looks excellent, but I can't make it work. I've even tried to combine it with option #1 to see if it at least causes a clash, but then it just works as if I'd only implemented option #1. Could you please modify my test program to use option #2, verify that it works, and post the code? – aknuds1 Feb 21 '13 at 17:07
  • @aknuds1 - I use this approach all the time. It should just work. Make sure you are using the cascade delete bundle dll from the same build version of RavenDB that you are running. Note that for named bundles, you would also have to modify the `Raven/ActiveBundles` setting, but the cascade delete bundle is unnamed. (It doesn't export a `Bundle` name in its metadata). So it is activated as soon as it is wired up. – Matt Johnson-Pint Feb 22 '13 at 15:56
  • Well, I can only urge you again to please post a working example as I can't figure out what I'm doing wrong. Like I said, option #1 works perfectly, whereas adding the bundle to the catalog does nothing (it's also referenced). – aknuds1 Feb 22 '13 at 18:25
0

It's somewhat counterintuitive, but I found that I need to copy packages\RavenDB.Bundles.CascadeDelete.2.5.2505-Unstable\lib\net40\Raven.Bundles.CascadeDelete.dll to ConsoleApplication1\Plugins\Raven.Bundles.CascadeDelete.dll and configure Visual Studio to copy the file to the output directory, for it to be picked up by RavenDB.

Now the CascadeDelete bundle works and the child is removed with the parent.

enter image description here

aknuds1
  • 65,625
  • 67
  • 195
  • 317