cancel
Showing results for 
Search instead for 
Did you mean: 

Anyone using Extension Methods on top of the Unity API?

Alex_French
Elite Collaborator
Elite Collaborator

In the last few months I've written several significant chunks of Unity Script code- two Libraries encompassing separate sets of business logic, each of which has ~4 different "entry points" from individual scripts that are triggered directly in Workflow.  Because these are both for a completely new project, I've had the chance to try and revamp some general approaches to structuring code based on my experience with the Unity API, the work of some consultants that we used early in Dartmouth's OnBase time, and talking with Hyland employees and folks at TechQuest as much as possible.

One of the approaches I've started using very heavily is to write C# Extension methods, extending Unity API objects, whenever possible. 

I'm curious to hear if anyone else is doing the same, and what advantages and disadvantages you've found (or if you've found improvements over what I've been doing).

A good candidate for an Extension Method would be something that doesn't really have business logic in it, but does something straightforward but tedious.  One example might be if I want to remove all instances of a certain Keyword Type from a KeywordModifier object before triggering .ApplyChanges().

It is pretty simple code to find the KeywordRecord that contains standalone Keywords, to find all Keywords that match a specific KeywordType, and to use KeywordModifier.RemoveKeyword().  But that may quickly get tedious, so I would very often wrap it up in a method wiht a signature like this (in slightly simplified psuedo-code):

    RemoveKeywords(myKeywordModifierObject, keywordTypeName)

But an Extension Method is actually much cleaner!  It lets me turn my code into something like (still in simplified psuedo-code):

    myKeywordModifier.RemoveAllKeyword(keywordTypeName
    
The bit I'm working on right now uses about 20 Extension Methods spread out over objects including KeywordModifier and EditableKeywordRecord, KeywordRecordList, Document, PropertyBag, DisplayColumnList, DocumentQuery, and Queue.
    
Here's what I like about this approach:


    -A lot of simple, common operations now look like they're just part of the API
    
    -I can return the object I'm working with (something the API often doesn't do, probably an intentional design choice)- this let's me chain a long series of method calls, making either for short-and-sweet one-liners or for longer series of operations that are technically one-liners even if I choose to format that over many lines for readability.  I'm used to this style both as a heavy user of LINQ and as a Javascript developer.
    
    -Trying to think in terms of supplementing the API seems to lead to better business-logic free little units of code, that still save a lot of work and improve readability, rather than trying to think in terms of small methods

    
Here's what isn't perfect:


    -It will still take a long time to develop a standard set of code that is re-usable in many projects, and the same tricky decisions come up a lot: Should this method throw an exception if something isn't found?  Or should it accept a default value?  Or should it do either?  Do I need to write N overloads of this for different data types?  Or can I make it generic with a little more work inside the method? If there was an easy across-the-board answer to most of these, then Hyland would probably have already implemented everything I want anyway.
    
    -Many of the objects I'm extending don't necessarily give you a reference back to an Application or Document object, so almost all of them end up needing to pass one of those as an argument.  This is just a little messy.  It would be awesome if I could get back to an Application or Document from many more API objects, but I'm sure there are many cases where that would be confusing or failure prone if Hyland made it possible
    
    -Working with any of the "List" types, like KeywordRecordList, DocumentList, etc.- can be a little awkward.  I would really love to have an exposed constructor of the form KeywordRecordList(List<KeywordRecord>).  In general I used to avoid those specialized List types, instead getting to a standard List<whatever>, but when working in Extension Methods it makes a lot more sense to keep working in the custom List types so that basic API operations can be used after the Extension Methods.
    
    -I would love to be able to reference a Library script from a Library script.  I know I could put these building-blocks in a DLL and then reference that from multiple Libraries, but until I'm really happy with a stable set of Extension Methods I would much rather keep the code visible and easy to work with- but that means if I have two separate Libraries for different sets of business-logic, even within the same general project, I end up having a ton of duplicated Extension Methods in both Libraries.


Have other developers tried using Extension Methods?  What tricks am I missing?  What problems are there that I haven't noticed yet and will regeret later?

Thanks for any ideas people share, and I hope this discussion helps someone else who hasn't tried this approach.

1 ACCEPTED ANSWER

Brian_Koning
Star Contributor
Star Contributor

Hello Alex! Thank you for the great discussion topic. Extension methods are a feature I find I am also using a lot more, especially given several C# 6 features made the overall experience of inline invocation and object creation more fluid. I really like how they make code intent much more declarative by easily abstracting away common functionality.

Our team has discussed, and will continue to discuss, how extension methods fit into the Developer experience in the Unity API going forward. We decided to expose the Unity pop additions for 16 as extension methods and we are looking for opportunities to expose more context-sensitive or shortcut operations in the future.

Towards your specific comments about what is not perfect with extending the API:

- I recognize that we have an advantage internally that our API consumers do not have when writing extension methods: internal access to symbols. Most Unity API objects have a reference to the Application they came from. There are also a few internal attempts at cross-cutting interfaces for commonly exposed functionality (eg. IKeywordContainer).

The many internal symbols are a reoccurring problem with the API, and not just with extensibility. They also make the testability of the API very, very awkward for API consumers. This is a known problem and we have found the solution is not trivial. Examples of several projects in various stages of development to move us towards a more transparently scoped future are: a hard look at serialization, a more robust separation of business and services logic, and review of our caching procedures.

The Unity API backlog already has SCR #213093, a non-customer enhancement request, to expose more functional interfaces which promote code reuse. Completion of the SCR should cut down on the number of extension method wrappers bound tightly to concrete types.

- The majority of the strongly-typed list structures the API exposes are meant to be read only to API consumers. In general, we have not exposed class constructors because of complex initialization and contextually sensitive use cases. This is absolutely the case around keyword-related classes. Can you provide examples where creating your own instance of KeywordRecordList would be useful?

- Lastly, the library script reference problem is, again, a well-known issue that has yet to be addressed. The Unity Scripts backlog already has SCR #228070 to track the customer enhancement. This is not so much a technical problem as a priority problem; we believe there are more beneficial enhancements to be made elsewhere. Furthermore, the functionality is not quite coherent with the direction we are taking Unity Scripts and their configuration.

From the product lead prospective, I am also very curious to what our API consumer's experience is with extending the API. It is one of the areas we are actively looking to address and any input and guidance from our users is much appreciated.

View answer in original post

8 REPLIES 8

Brian_Koning
Star Contributor
Star Contributor

Hello Alex! Thank you for the great discussion topic. Extension methods are a feature I find I am also using a lot more, especially given several C# 6 features made the overall experience of inline invocation and object creation more fluid. I really like how they make code intent much more declarative by easily abstracting away common functionality.

Our team has discussed, and will continue to discuss, how extension methods fit into the Developer experience in the Unity API going forward. We decided to expose the Unity pop additions for 16 as extension methods and we are looking for opportunities to expose more context-sensitive or shortcut operations in the future.

Towards your specific comments about what is not perfect with extending the API:

- I recognize that we have an advantage internally that our API consumers do not have when writing extension methods: internal access to symbols. Most Unity API objects have a reference to the Application they came from. There are also a few internal attempts at cross-cutting interfaces for commonly exposed functionality (eg. IKeywordContainer).

The many internal symbols are a reoccurring problem with the API, and not just with extensibility. They also make the testability of the API very, very awkward for API consumers. This is a known problem and we have found the solution is not trivial. Examples of several projects in various stages of development to move us towards a more transparently scoped future are: a hard look at serialization, a more robust separation of business and services logic, and review of our caching procedures.

The Unity API backlog already has SCR #213093, a non-customer enhancement request, to expose more functional interfaces which promote code reuse. Completion of the SCR should cut down on the number of extension method wrappers bound tightly to concrete types.

- The majority of the strongly-typed list structures the API exposes are meant to be read only to API consumers. In general, we have not exposed class constructors because of complex initialization and contextually sensitive use cases. This is absolutely the case around keyword-related classes. Can you provide examples where creating your own instance of KeywordRecordList would be useful?

- Lastly, the library script reference problem is, again, a well-known issue that has yet to be addressed. The Unity Scripts backlog already has SCR #228070 to track the customer enhancement. This is not so much a technical problem as a priority problem; we believe there are more beneficial enhancements to be made elsewhere. Furthermore, the functionality is not quite coherent with the direction we are taking Unity Scripts and their configuration.

From the product lead prospective, I am also very curious to what our API consumer's experience is with extending the API. It is one of the areas we are actively looking to address and any input and guidance from our users is much appreciated.

Here's a pretty trivial example of a minor-convenience Extension method. I've made a conscious choice that I don't mind getting a pretty generic, opaque Exception if my keywordTypeName isn't valid:



public static KeywordRecordList FindAllByKeywordValue(this KeywordRecordList thisKeywordRecordList,
Hyland.Unity.Application app, string keywordTypeName, long keywordValue)
{
return thisKeywordRecordList.FindAll(
app.Core.KeywordTypes
.Find(keywordTypeName)
.CreateKeyword(keywordValue));
}


Right now I just wrote two overloads for "string keywordValue" and "long keywordValue", though just staring at it for a moment now, it would be easy enough to let it accept "object keywordValue" and then attempt to cast based on the KeywordType that's found.

But what if I wanted to write FindAllByKeywordValueGreaterThan() or FindAllByKeywordValueContains()?

It's easy enough to loop and return a List, but then I've lost the ability to chain this with itself or with other KeywordRecordList methods.

I can see at least two ways that Hyland could make that easily doable:



1) Expose a "KeywordRecordList(List<KeywordRecord>)" constructor, or some other constructor

2) Add a method to Keyword Record List like:
KeywordRecordList KeywordRecordList.FindAll( predicate)



I guess (1) would also let me create a "map" Extension Method (or just plain old method) while staying in the KeywordRecordList (again instead of moving to List)... which would be both awesomely powerful and probably dangerous.

I think the same basic idea would apply to all the custom List types. This current project doesn't involve a lot of working with DocumentQueries, but I can see Extension Methods (or just .FindAll() with predicates) for a lot of querying needs when the querying logic can't be done in the DocumentQuery itself.

Hi Alex

Although in your example, the custom creation of a strongly typed list would allow the use of chained extension methods. As Brian mentioned above, the API shows potential for limited business cases were we would feel comfortable with this type of modification. The only valid case where the List object would be beneficial to modify would be when returning a PageDataList; SCR 175953 is currently in the Development backlog to accommodate for this enhancement. Allowing the ability to modify these List structures, especially in the case of Keywords, can introduce catastrophic issues.

For your second point, looking into the SDK, there is a KeywordRecordList.FindAll(PredicateT) overload currently within the Unity API. I would suggest looking into the reference section of the SDK for further information on this overload. Overall, this would return an IEnumerable of that object type. This seems to be the common return type within the Unity API when using a Predicate in this fashion.

Hi Tyler,

I'm aware of the existing FindAll with the signature:
IEnumerable IndexedList.FindAll(Predicate)

That option (inherited from IndexedList) is fine for applying a single bit of logic, but it is much less helpful than:
KeywordRecordList KeywordRecordList.FindAll(Predicate)

would be for building up any sort of reusable or chainable abstraction, like an Extension Method.

A FindAll that returns an IEnumerable<> means that you lose the option to follow it up with one of the 3 other overloads of Find() or 4 overloads of FindAll() or any other extension methods added to KeywordRecordList.

Did that clarify why I think it would be helpful?

Getting started

Find what you came for

We want to make your experience in Hyland Connect as valuable as possible, so we put together some helpful links.