Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Get / Set generic method extensible storage

6 REPLIES 6
SOLVED
Reply
Message 1 of 7
jens.slofstra!
1261 Views, 6 Replies

Get / Set generic method extensible storage

Hi everyone,

I have made three generic methods for extensible storage, a get and two set methods (one for arrays and simple field and one for maps).

Problem however is that I get an error when getting the storage:

Autodesk.Revit.Exceptions.ArgumentException: 'The Field belongs to a different Schema from this Entity, or this Entity is invalid.
Parameter name: field'

 

Can somebody help me with making these methods work. Because in the long run it will save a lot of time if methodes can be used for different types.

This is the code I have so far:

public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
using(var tx = new Transaction(_doc, "Test"))
            {
                tx.Start();

                    var e = _doc.GetElement(id);
                    var elementInfo = new Dictionary<string, string>
                    {
                        { "Test1", "Hello World!" },
                        { "Test2", "Hello World!" },
                        { "Test3", "Hello World!" }
                    };

                    var extensibleElementInfo = GetStorageData<Dictionary<string,string>>(e, _elementInfo, _guidElementInfo);
  
                    SetStorageData<string, string>(e, elementInfo, _elementInfo, _guidElementInfo);


                tx.Commit();
            }

            return Result.Succeeded;
}



private static T GetStorageData<T>(Element e, string fieldName, Guid guid)
        {
            var schema = GetSchema(guid);
            if(schema != null)
            {
                //try
                //{
                    var entity = e.GetEntity(schema);
                    return entity.Get<T>(schema.GetField(fieldName));
                //}
                //catch { }
            }
            return default(T);
        }

        private void SetStorageData<T>(Element e, T data, string schemaName, Guid guid)
        {
            var schema = GetSchema(guid);
            Entity entity = null;

            if(schema == null)
            {
                var schemaBuilder = new SchemaBuilder(guid);
                schemaBuilder.SetReadAccessLevel(AccessLevel.Public);
                schemaBuilder.SetWriteAccessLevel(AccessLevel.Vendor);
                schemaBuilder.SetVendorId("BimBuildings");
                schemaBuilder.SetSchemaName(schemaName);

                try
                {
                    if (typeof(T) != typeof(string) && typeof(IEnumerable).IsAssignableFrom(typeof(T)))
                        schemaBuilder.AddArrayField(schemaName, typeof(T));
                    else
                        schemaBuilder.AddSimpleField(schemaName, typeof(T));
                }
                catch (Autodesk.Revit.Exceptions.ArgumentException)
                {
                    Message.Display("The type provided is not storable.", WindowType.Error);
                }

                schema = schemaBuilder.Finish();
                entity = new Entity(schema);
            }
            else
            {
                entity = e.GetEntity(schema);
            }

            var fieldSet = schema.GetField(schemaName);
            entity.Set(fieldSet, data);
        }

        private void SetStorageData<TKey, TValue>(Element e, IDictionary<TKey, TValue> data, string schemaName, Guid guid)
        {
            var schema = GetSchema(guid);
            Entity entity = null;

            if (schema == null)
            {
                var schemaBuilder = new SchemaBuilder(guid);
                schemaBuilder.SetReadAccessLevel(AccessLevel.Public);
                schemaBuilder.SetWriteAccessLevel(AccessLevel.Vendor);
                schemaBuilder.SetVendorId("BimBuildings");
                schemaBuilder.SetSchemaName(schemaName);
                schemaBuilder.AddMapField(schemaName, typeof(TKey), typeof(TValue));

                schema = schemaBuilder.Finish();
                entity = new Entity(schema);
            }
            else
            {
                entity = e.GetEntity(schema);
            }

            var fieldSet = schema.GetField(schemaName);
            entity.Set(fieldSet, data);
        }

 

6 REPLIES 6
Message 2 of 7

Hi @jens.slofstra! ,

I haven't gone through the code in depth, but I would suggest using 

SetWriteAccessLevel

as public while you are debugging.

 

Which line caused the exception?


Cheers,

-Matt
_______________________________________________________________________________
Marking a post as a 'solution' helps the community. Giving a post 'Kudos' is as good as saying thanks. Why not do both?
Message 3 of 7

Hi @matthew_taylor,

I will set the SetWriteAccessLevel to Public.

This line in the Get method:

return entity.Get<T>(schema.GetField(fieldName));

 

Message 4 of 7

If you have schema defined with a field structure that field structure can't change later including the types used in such (is this what you are aiming for)? From my initial view the code looks more or less right but perhaps how it is being used is wrong?

 

Once the schema is set i.e. has an id, has a name, has documentation, has field structure then that is it. It is like a class structure that you can't change so if you have a number of fields that can't be changed later (including their types). I found early on that even if you change the documentation it causes problems.

 

I actually went through a similar thought process to you and created a method that could take a class with values and convert it to a schema (seemed to work quite well and could even incorporate nesting). I had custom attributes defining the schema GUID, name, at class level and units for field of double at field level. I even auto generated all the permutations of type combinations that could be used as a key value pair in a dictionary. In the end I never used any of it through this thought that one day I may add an extra member to a class and inadvertently create a schema with the same id but different definition. Then ForgeTypeIds came about and I had to convert it all to those.

 

My other concern was always the temptation to create deeply nested structures and wondering exactly what the limits of extensible storage was in that respect.

Message 5 of 7

@RPTHOMAS108@matthew_taylor 

I am not trying to change the type of the extensible storage, just making a method I can call in multiple external commands so I don't need to type the same get and set method but with different outputs (I am just a bit lazy 😉).

 

After my question here I have been researching some more and deconstructed my code.

But even after I split the Get and Set of external storage into two external commands I get the same error.

Autodesk.Revit.Exceptions.ArgumentException: 'The Field belongs to a different Schema from this Entity, or this Entity is invalid.
Parameter name: field'

This error occurs after 

entity.Set<string>(fieldSet, data);

 

I made a new project and simplified my code but still the same error.

Below the two external commands:

namespace Testing
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class SetStorage : IExternalCommand
    {
        private Document _doc;
        private UIDocument _uidoc;

        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            #region//Utils
            // Application context.
            _uidoc = commandData.Application.ActiveUIDocument;
            _doc = _uidoc.Document;
            #endregion

            using(var tx = new Transaction(_doc, "Test"))
            {
                tx.Start();

                var reference = _uidoc.Selection.PickObject(ObjectType.Element, new SelectionFilterByCategory("Walls"), "Select an element");
                var element = _doc.GetElement(reference);

                var guid = new Guid("01b7cd09-2e3b-4ecc-aa8c-d3b5b4e407c0");
                SetStorageData(element, "Hello World!", "Test", guid);

                tx.Commit();
            }

            return Result.Succeeded;

        }

        private static void SetStorageData(Element e, string data, string schemaName, Guid guid)
        {
            var schema = GetSchema(guid);
            Entity entity = null;

            if (schema == null)
            {
                var schemaBuilder = new SchemaBuilder(guid);
                schemaBuilder.SetReadAccessLevel(AccessLevel.Public);
                schemaBuilder.SetWriteAccessLevel(AccessLevel.Public);
                schemaBuilder.SetVendorId("Company");
                schemaBuilder.SetSchemaName(schemaName);
                schemaBuilder.AddSimpleField(schemaName, typeof(string));

                schema = schemaBuilder.Finish();
                entity = new Entity(schema);
            }
            else
            {
                entity = e.GetEntity(schema);
            }

            var fieldSet = schema.GetField(schemaName);
            entity.Set<string>(fieldSet, data);
        }

        private static Schema GetSchema(Guid guid)
        {
            var schema = Schema.Lookup(guid);
            if (schema != null)
                return schema;
            return null;
        }
    }
}
namespace Testing
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class GetStorage : IExternalCommand
    {
        private Document _doc;
        private UIDocument _uidoc;

        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            #region//Utils
            // Application context.
            _uidoc = commandData.Application.ActiveUIDocument;
            _doc = _uidoc.Document;
            #endregion

            using (var tx = new Transaction(_doc, "Test"))
            {
                tx.Start();

                var reference = _uidoc.Selection.PickObject(ObjectType.Element, new SelectionFilterByCategory("Walls"), "Select an element");
                var element = _doc.GetElement(reference);

                var guid = new Guid("01b7cd09-2e3b-4ecc-aa8c-d3b5b4e407c0");
                TaskDialog.Show("Info", GetStorageData(element, "Test", guid));

                tx.Commit();
            }


            return Result.Succeeded;
        }

        private static string GetStorageData(Element e, string fieldName, Guid guid)
        {
            var schema = GetSchema(guid);
            if (schema != null)
            {
                var entity = e.GetEntity(schema);
                return entity.Get<string>(schema.GetField(fieldName));
            }
            return null;
        }

        private static Schema GetSchema(Guid guid)
        {
            var schema = Schema.Lookup(guid);
            if (schema != null)
                return schema;
            return null;
        }
    }
}

 

Message 6 of 7

Ok I don't see a call to Element.SetEntity so nothing is being stored on the element to get or set in terms of fields. In itself I'm not sure it would lead to the issue stated because it would just create a new one each time. If you've tested multiple times in same session it may be the issue (in terms of the lifecycle of it). The logic would be as follows:

if schema exists in memory from previous run then:

var schema = GetSchema(guid);

will return it however since it has never been attached to the element

entity = e.GetEntity(schema)

will return an invalid entity.

Then you try 'entity.Set<string>(fieldSet, data)' on that invalid entity.

 

So the very first session run should do nothing but further runs would throw the exception I guess.

 

Would also avoid using the schema name for the field also.

 

Failing all that I would go back to the example under SchemaBuilder in the ReviAPI.chm. Remembering also that schemas persist in memory outside the document. 

Message 7 of 7

Hi @RPTHOMAS108 ,

The setting of the entity was the problem.

Thanks for your help.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Customer Advisory Groups


Rail Community