Reputation: 749
Still just a starter in ravendb. I have a doubt whether raven db supports some kind of referential integrity.
Here is my doubt and work..
I have document type screens as below
public class Screens
{
public String Id { get; set; }
public String ScreenName { get; set; }
}
And Another document named RightsDeclaration as below
public class RightsDeclaration
{
public RightsDeclaration()
{
_screenrights = new List<ScreenRight>();
}
public String Id { get; set; }// Role Name
List<ScreenRight> _screenrights;
public List<ScreenRight> ScreenRights { get { return _screenrights; } set { _screenrights = value; } }
}
Now the screen rights class looks like below with the screen name field as below
public class ScreenRight :
{
public String ScreenName { get; set; }
public Boolean Create { get; set; }
public Boolean Read { get; set; }
public Boolean Update { get; set; }
public Boolean Delete { get; set; }
public Boolean Approve { get; set; }
public Boolean Access { get; set; }
public Boolean Print { get; set; }
public Boolean Email { get; set; }
}
Now first i will create some list of screens and then i assign rights for each role with mentioning what is the screen name and list of rights. up to this point everything goes fine. Now in a scenario where if i would delete the screen name in screens class but then the screen rights for that screen still exists in the rights declaration. is it possible to delete the corresponding screen rights from userrights document too? if so please mention the query or idea to do the above.. Thanks in advance..
Upvotes: 3
Views: 330
Reputation: 241573
Raven doesn't support true referential integrity, that's not something document databases are typically interested in. You can, however, achieve the same effect.
First, I wouldn't reference via name, the screen Id is a better key because you can load by it. If you want to also capture the screen name in your ScreenRight
class, that's fine but it's a secondary concern.
Also, a minor thing, but try to keep your entity/document names singular. Screen
instead of Screens
. It will save you some trouble down the road.
One way to handle the delete would be to specifically update the referencing document when you delete the primary. If you do this in the same session, you have transactional guarantees. You do have to consider paging of query results.
using (var session = documentStore.OpenSession())
{
var screen = session.Load<Screen>(screenId);
if (screen != null)
{
// Set up some variables to help with pagination.
var now = DateTime.UtcNow;
const int pageSize = 1024;
var page = 0;
while (true)
{
// Get all rights that use this screen.
// Waiting for nonstale results is appropriate, but we want the same "now" with each page.
var rights = session.Query<RightsDeclaration>()
.Customize(x => x.WaitForNonStaleResultsAsOf(now))
.Where(x => x.ScreenRights.Any(y => y.ScreenId == screenId))
.Skip(page * pageSize)
.Take(pageSize)
.ToList();
foreach (var right in rights)
{
// Remove the screen from any rights that used it
right.ScreenRights.RemoveAll(x => x.ScreenId == screenId);
// You might want to delete any empty rights declarations
if (right.ScreenRights.Count == 0)
session.Delete(right);
}
// Exit when we get back less than a full page, indicating there are no more pages.
if (rights.Count < pageSize)
break;
// Next page please.
page++;
}
// Delete the screen.
session.Delete(screen);
// Save all your changes together.
session.SaveChanges();
}
}
Another approach would be to use Raven's Patching API to perform the update. It's faster, but you don't have transactional guarantees.
You can also look at Raven's Cascading Deletes Bundle, but I don't think it would work here because you're not necessarily deleting the RightsDeclaration
every time.
Update
I pushed out some of the harder paging code into an easy-to-consume extension method. This is now part of the ravendb/contrib project. Using those extensions, your code is much simpler:
using (var session = documentStore.OpenSession())
{
var screen = session.Load<Screen>(screenId);
if (screen != null)
{
// Get all rights that use this screen.
session.Query<RightsDeclaration>()
.Customize(x => x.WaitForNonStaleResultsAsOfNow())
.Where(x => x.ScreenRights.Any(y => y.ScreenId == screenId))
.ForEachWithPaging(right =>
{
// Remove the screen from any rights that used it
right.ScreenRights.RemoveAll(x => x.ScreenId == screenId);
// You might want to delete any empty rights declarations
if (right.ScreenRights.Count == 0)
session.Delete(right);
});
// Delete the screen.
session.Delete(screen);
// Save all your changes together.
session.SaveChanges();
}
}
Upvotes: 3