This project has moved. For the latest updates, please go here.

Save a single property in two ways (two triples for each direction)

Oct 1, 2014 at 11:18 AM
Hi there,

Is there a way to create a two way property, meaning that one property will be save as two triples.

For example if we have a Movie object that contains a collection of Person objects named Cast.
[Entity("arts:Movie")]
public interface IMovie 
{
   ...
    [PropertyType("arts:performer")]
    ICollection<IPerson> Cast { get; set; }
}

[Entity("http://xmlns.com/foaf/0.1/Person")]
 public interface IPerson 
 {
  ...
    [InverseProperty("Cast")]
    ICollection<IMovie> Movies { get; set; }
}
I would like to save each person within the cast as two triples:

Movie -> Cast -> Person
Person -> performs in -> Movie

Thanks!
Coordinator
Oct 2, 2014 at 9:20 AM
There isn't a direct way to do this, but you can add this functionality. I think the easiest way to do this would be to add a handler to the SavingChanges event on the DataObjectContext (note that this means you will need to separately construct the DataObjectContext and then pass that in to the entity context constructor). That event will get invoked just before the update transaction is sent to the server and you can modify the collections of triples to be added and triples to be deleted to ensure that if one of the triples is added/deleted, the other is also added/deleted. Thinking about it, this would make a nice sample for that particular bit of behaviour in the DataObjectContext.
Oct 3, 2014 at 2:55 PM
Edited Oct 3, 2014 at 3:41 PM
Hi, Thank you.

Do you mean the SavingChanges eventhandler on BrightstarEntityContext or on IDataObjectStore.

if it is the handler on BrightstarEntityContext - how do I get access to the triples?

if it is IDataObjectStore, do I need create an instance of RestDataObjectContext and then call CreateStore/OpenStore, passing the mapping dictionary, is that correct?

something like:
 Assembly assembly = typeof(IPerson).Assembly;

            var attributes = assembly.GetCustomAttributes(typeof(NamespaceDeclarationAttribute), false)
                                    .Cast<NamespaceDeclarationAttribute>();

            var mappingDict = attributes.ToDictionary(x => x.Prefix, x => x.Reference);
            
            var storeName = ConfigurationManager.AppSettings["BrightstarDB.ConnectionString"].Split(';').First(x => x.ToLower().StartsWith("storename")).Split('=').Last();

            RestDataObjectContext restContext = new RestDataObjectContext(new BrightstarDB.ConnectionString(ConfigurationManager.AppSettings["BrightstarDB.ConnectionString"]));

            IDataObjectStore store;
            if(restContext.DoesStoreExist(storeName))
            {
                store = restContext.OpenStore(storeName, mappingDict,null);
            }
            else
            {
                store = restContext.CreateStore(storeName, mappingDict);
            }
          
            store.SavingChanges = SavingChangesStoreEvent;
       
            using (var context = new MyEntityContext(store))
            {
                   ....
            }
And in the event should I use the 'AddTriples' Collection?
((BrightstarDB.Client.RestDataObjectStore)sender).AddTriples ...
How would the update and delete work in this case?

Thank you!
Coordinator
Oct 7, 2014 at 2:14 PM
NZ_Dig wrote:
Hi, Thank you.

Do you mean the SavingChanges eventhandler on BrightstarEntityContext or on IDataObjectStore.

if it is the handler on BrightstarEntityContext - how do I get access to the triples?
It's the IDataObjectStore SavingChanges event that you need to handle.
if it is IDataObjectStore, do I need create an instance of RestDataObjectContext and then call CreateStore/OpenStore, passing the mapping dictionary, is that correct?
Don't worry about the mapping dictionary unless you really want to use it from your code. The EntityFramework uses full URIs (even if you define prefixes in assembly attributes - these get resolved by the EF) so it doesn't need the mapping dictionary.
And in the event should I use the 'AddTriples' Collection?
((BrightstarDB.Client.RestDataObjectStore)sender).AddTriples ...
How would the update and delete work in this case?
You also need to process the DeletePatterns collection as this will list triples being removed (an update is just a delete followed by an add). Delete patterns can include the wildcard URI (BrightstarDB.Constants.WildcardUri) which is like having a * wildcard for subject, predicate or object. Typically from the EF if you set a update a property you will get a delete pattern of <s> <p> * and an AddTriple of <s> <p> v (where <s> and <p> are subject and predicate URIs, * is the wildcard URI and v is the new value). If you delete an object you will get a delete pattern of <s> * *.

If you see a delete pattern with no wildcards that matches one of your predicates (e.g. <s> <p1> <v>) then it is a simple matter to just add the inverse also to the deletion (<v> <p2> <s>). It shouldn't be too hard to handle the wildcards. A delete pattern of <s> <p1> * would mean you need to add another delete pattern of * <p2> <s>. A delete pattern of <s> * * would probably mean that you need to add * <p1> <s> and * <p2><s> to the delete patterns as the deleted object could be the target of either of those predicates (depends on if it is a movie or a person).

AddTriples never have wildcards so it is much easier - if you see <s> <p1> <v> in AddTriples then you also need to add <v> <p2> <s> (and of course vice-versa). Don't worry about duplicating stuff, RDF doesn't care if you add the same triple multiple times, it still only exists once.

And as update is just delete followed by add, once you have these two basic cases covered, update comes for free! :)

I think that covers it - let me know how it goes!

Cheers

Kal
Oct 9, 2014 at 9:48 AM
Edited Oct 9, 2014 at 9:59 AM
Hi,

Couple more questions :-) :
  1. The 'AddTriples' and 'DeletePatterns' property seems to be of an internal classes. Is there an easy way to access them (without reflection)?
  2. If I use the SavingChanges event of the store, then I would need to:
    a. get the ids of all the added/deleted triples
    b. find the objects within the context that correspond to those ids
    c. find for the objects from (b) the properties that are being changed
    d. for each property that is changed, check if the object defines a 'two way property' (as these will be defined by and attribute similar to the [PropertyType])
However in the SavingChanges event we do not have access to the context any more, is that right? If so I am not really sure what to do as we do need access to the context, don't we?

Thanks!
Coordinator
Oct 9, 2014 at 7:26 PM
NZ_Dig wrote:
Hi,

Couple more questions :-) :
  1. The 'AddTriples' and 'DeletePatterns' property seems to be of an internal classes. Is there an easy way to access them (without reflection)?
Oh, that's a pain. I'll need to fix it up to expose the BrightstarDB.Model.Triple class.
  1. If I use the SavingChanges event of the store, then I would need to:
    a. get the ids of all the added/deleted triples
    b. find the objects within the context that correspond to those ids
    c. find for the objects from (b) the properties that are being changed
    d. for each property that is changed, check if the object defines a 'two way property' (as these will be defined by and attribute similar to the [PropertyType])
However in the SavingChanges event we do not have access to the context any more, is that right? If so I am not really sure what to do as we do need access to the context, don't we?
Hmm - I had assumed that in the SavingChanges event you would have prior knowledge of which properties are two way, but if you don't you should still just be able to reflect on the types, right? If two-way is defined as an attribute on the interface using a different namespace from the one BrightstarDB uses, it shouldn't care (if the attribute can be copied onto the implementation class BrightstarDB should do that, but if not it should just ignore the attribute).

To avoid overhead you may want to do that reflection once in your startup code and then squirrel it away somewhere that you can get hold of in the event handler.

Does that help?
Oct 10, 2014 at 8:34 AM
Hi, yes, that helps, I will probably have to have static mapping object similar to the EntityMappingStore (once the internal classes are exposed!)

Thanks :-)