Define namespace prefix in config or programmatically

Oct 1, 2014 at 2:10 PM
Hi there,

Is there a way to define namespaces in the config or programmatically (rather than the assembly with NamespaceDeclaration)?

The reason would be that we need different values of [PropertyType] within different environments.

Thanks!
Coordinator
Oct 2, 2014 at 9:28 AM
Hi,

You should be able to do this by using the methods on the BrightstarDB.EntityFramework.EntityMappingStore class - this is a singleton that manages the mappings for .NET types and properties to URIs (amongst other things). There is a SetIdentityInfo(Type, IdentityInfo) class that you can use to change the default info read from the attributes. The Type should be the Type for the interface (not the implementation class) and although the IdentityInfo constructor takes 5 parameters, if you have the "normal" situation of an identity prefix and an Id property then you only need to provide the first two - the remaning three arguments can be null.

Hope this helps! Let me know if you need more help around this or anything else.

Cheers

Kal
Oct 2, 2014 at 12:55 PM
Edited Oct 2, 2014 at 1:55 PM
Hi Kal,

Thanks for that :-). I am trying to change the url for all the properties (not just the identifier), and for the type as well.

I have add the following line of code :
 EntityMappingStore.Instance.SetIdentityInfo(typeof(IPerson), new IdentityInfo("http://xxx.co.uk/",typeof(IPerson).GetProperty("Id"),null,null,null ));

 EntityMappingStore.Instance.SetIdentityInfo(typeof(IPerson), new IdentityInfo("http://xxx.co.uk/",typeof(IPerson).GetProperty("Name"),null,null,null ));
But the properties 'Id' (the identifier) and 'Name' are still saved with the default BS namespace.

Also, how can I change the URL of the entity type ([Entity])?

Thanks!
Coordinator
Oct 3, 2014 at 8:46 AM
Hi,

Sorry I should have added that for properties and for entity types the registration calls on the EntityMappingStore are slightly different.

To set the URI for an entity use the method EntityMappingStore.Instance.SetTypeMapping(Type, string) where Type is the type of the implementation class (so typeof(Person) rather than typeof(IPerson)).

To set the URI for a property use the methodEntityMappingStore.Instance.SetPropertyHint(PropertyInfo, PropertyHint) where PropertyInfo is the property on the implementation type (so typeof(Person).GetProperty("Name")) and PropertyHint is either new PropertyHint(PropertyMappingType.Property, uri) for a property where the value is a primitive type like a string or integer; new PropertyHint(PropertyMappingType.Arc, uri) for a property where the value is another entity or a collection of entities; or new PropertyHint(PropertyMappingType.Id, uri) for the Id property.

Also rereading the code it looks like actually the entity framework consistently uses the implementation type for its registration, so the call to SetIdentityInfo for the Id property should probably use typeof(Person).GetProperty("Id") - that would explain why your registration wasn't overriding the default registration because the implementation type is checked before the interface type.

If you want to take a look at how BrightstarDB uses this stuff internally the class that does the registration is BrightstarDB.EntityFramework.ReflectionMappingProvider. You should probably also take a look at the code generated by the .tt file as well, but most of the registration work happens in the ReflectionMappingProvider class.

Hope this helps!

Cheers

Kal
Oct 3, 2014 at 2:06 PM
Edited Oct 3, 2014 at 2:07 PM
Hi,

I have tried the following code, but still no luck:
 EntityMappingStore.Instance.SetTypeMapping(typeof(Person), "http://typeurl.co.uk/");

 EntityMappingStore.Instance.SetPropertyHint(typeof(Person).GetProperty("Name"), new PropertyHint(PropertyMappingType.Property, "http://nameurl.co.uk/"));

EntityMappingStore.Instance.SetPropertyHint(typeof(Person).GetProperty("Id"), new PropertyHint(PropertyMappingType.Id, "http://idurl.co.uk/"));

 EntityMappingStore.Instance.SetPropertyHint(typeof(Person).GetProperty("Movies"), new PropertyHint(PropertyMappingType.Arc, "http://moviesurl.co.uk/"));
Have I done it correctly?

Also I am bit confused regarding the identity - should I be able to either call
EntityMappingStore.Instance.SetPropertyHint(typeof(Person).GetProperty("Id"), new PropertyHint(PropertyMappingType.Id, "http://idurl.co.uk/"));

or

EntityMappingStore.Instance.SetIdentityInfo(typeof(Person), new IdentityInfo("http://idurl.co.uk/", typeof(IPerson).GetProperty("Id"), null, null, null));
?

Thanks!
Coordinator
Oct 7, 2014 at 8:45 AM
Hi,

We are a bit into uncharted waters here as the API wasn't really designed with this use case in mind. It looks like your calls are correct and that should be overriding the defaults set up by the reflection provider, but it is possible that either (a) your registration is itself getting overwritten or (b) I've missed something in my description of the code. I'll have to have a bit more of a detailed look, though it might be that the best thing to do would be to implement a way to build a context without the reflection provider getting involved.

Cheers

Kal
Oct 7, 2014 at 2:12 PM
Thanks Kal, Do you have by any chance a working sample code that I can use / compare?
Coordinator
Oct 7, 2014 at 2:37 PM
I'm afraid the only code that really uses this stuff is in the reflection provider (BrightstarDB.EntityFramework.ReflectionMappingProvider). That's why its a bit of a murky thing even for me!

I think that the problem is that the approach up to now has been to use a static provider (basically as a runtime cache to avoid the reflection overhead every time the context is created), but this does make the sort of extension you are trying to do a bit tricky. It may be that you have to create a context once to ensure the reflection provider runs and does its stuff against your assembly, then update EntityMappingStore singleton instance and then create a context that you are actually going to use - you wouldn't need to do this dance every time you create a context, just the first time, so perhaps it is something that could go in application startup (if its a web app then it would need to happen each time the app pool is restarted too).

Side note: Ideally we would be using a DI container so that you could inject the behaviour at runtime - moving to using dependency injection is something I would like to do for 2.0 (when I will allow myself to break all the APIs :-), but hopefully we can find a working solution for what you need to do right now without having to go that far.

Cheers

Kal
Oct 9, 2014 at 10:23 AM
In my actual app, I use UnitOfWork and create one context that is used across. So extending it to inject a behaviour would be actually quite easy (if I know what to inject!)

Regarding your solution, I think I may be doing something wrong. I created a context, set the entity mapping and then created another context, but still not luck. Here is my code:
         using (var context = new MyEntityContext())
         { }
             
         EntityMappingStore.Instance.SetIdentityInfo(typeof(Person), new IdentityInfo("http://xxx.co.uk/", typeof(IPerson).GetProperty("Id"), null, null, null));
         EntityMappingStore.Instance.SetIdentityInfo(typeof(Person), new IdentityInfo("http://xxx.co.uk/", typeof(IPerson).GetProperty("Name"), null, null, null));
         EntityMappingStore.Instance.SetTypeMapping(typeof(Person), "http://typeurl.co.uk/");
         EntityMappingStore.Instance.SetPropertyHint(typeof(Person).GetProperty("Name"), new PropertyHint(PropertyMappingType.Property, "http://nameurl.co.uk/"));
         EntityMappingStore.Instance.SetPropertyHint(typeof(Person).GetProperty("Id"), new PropertyHint(PropertyMappingType.Id, "http://idurl.co.uk/"));
         EntityMappingStore.Instance.SetPropertyHint(typeof(Person).GetProperty("Movies"), new PropertyHint(PropertyMappingType.Arc, "http://moviesurl.co.uk/"));

          using (var context = new MyEntityContext())
          {
                var p = context.Persons.Create();
                p.Name = "Jeff";

                var m = context.Movies.Create();
                m.Title = "the movie";

                p.Movies.Add(m);

               context.SaveChanges();
            }

And the entities:
   [Entity]
    public interface IPerson
    {
        /// <summary>
        /// Get the persistent identifier for this entity
        /// </summary>
        [Identifier]
        string Id { get; }

        [PropertyType("name")]
        string Name { get; set; }
        
        [PropertyType("movie")]
        [InverseProperty("Cast")]
        ICollection<IMovie> Movies { get; set; }

      
    }


   [Entity]
    public interface IMovie
    {
        /// <summary>
        /// Get the persistent identifier for this entity
        /// </summary>
        [Identifier]
        string Id { get; }

        [PropertyType("title")]
        string Title { get; set; }

        ICollection<IPerson> Cast { get; set; }
    }
Can you see what I have done wrong?

Thanks!
Coordinator
Oct 9, 2014 at 7:16 PM
Hi,

I'll take a look into it - that looks like code that I can easily repurpose to make a minimal test case. Thanks!

Cheers

Kal