Object Type Manager (.NET)

You can use the Object Type Manager API to programmatically create custom object types for use in your applications. It includes the following features:

  • Support for create, read, update, and delete operations on object types.
  • Helper methods for retrieving parent object types and dependent objects.

Additionally, the following APIs provide functionality for working with event handlers, object rules, and mass operations for object types:

  • Event Handler Manager API - includes methods for attaching event handlers to and removing them from object types. It contains helper methods used for retrieving event handlers available in a workspace, and for retrieving event handlers currently attached to an object type.
  • Object Rule Manager API - includes methods for creating, updating, reading, or deleting object rules on an object type. It also provides helper methods for retrieving associative objects, layouts, choices, and choice fields used when creating an object rule.
  • Mass Operation Manager API - includes methods for creating, updating, reading, or deleting mass operations on an object type. It also provides helper methods for retrieving available object types, event handlers, and layouts for use with mass operations.

As a sample use case, you could use the Object Type Manager service to add new object types to support a custom application that you developed. You might want to implement an application that tracks vendor or customer information and decide to add different object types for each of these items. These object types could be further customized with object rules, event handlers, and mass operations.

You can also use the Object Type Manager and other related services through the REST API. For more information, see Object Type Manager (REST).

This page contains the following information:

Fundamentals for managing object types

Click the following drop-down links to learn about the methods and classes used by the Object Type Manager and related APIs.

CRUD operations for object types

The Object Type Manager API supports create, read, update, and delete operations on object types. It also includes helper methods used to retrieve information about parent object types, and dependent objects.

Review the following guidelines for working with this service:

  • Verify that you have the appropriate permissions to access an object type before attempting to modify or delete it.
  • Verify that the Relativity application is unlocked before attempting to add an object type to it, or to modify or delete an existing object type. You also need permissions to the application and the object type to perform these tasks.
  • Use -1 to indicate the admin-level context when necessary.

See the following subsections for more information:

Create an object type

Use the CreateAsync() method to add a new object type to a workspace or the admin-level context. When you call this method, pass the Artifact ID of the workspace or -1 for admin-level context, and an ObjectTypeRequest object.

To set the Artifact ID for the ParentObjectType on the ObjectTypeRequest object, use the GetAvailableParentObjectTypesAsync() method. For more information, see Retrieve a parent object types.

Read an object type

You can retrieve basic information about an object type or extended information, which also includes operations that you have permissions to perform on the object type. If you want to return extended information, use the overloaded method by passing Boolean values set to true for additional metadata and permissions as follows:

ObjectTypeResponse response = await objectTypeManager.ReadAsync(workspaceId, objectTypeArtifactId, true, true);

The following code sample illustrates how to call the ReadAsync() method by passing the Artifact ID of a workspace and an object type. It returns only basic information. For a list of basic and extended properties, see Read an object type in Object Type Manager (REST).

public static async Task Read_Async()
{
    int workspaceId = 1018486;
    int objectTypeArtifactId = 1035231;

    using (Services.Interfaces.ObjectType.IObjectTypeManager objectTypeManager = serviceFactory.CreateProxy<Services.Interfaces.ObjectType.IObjectTypeManager>())
    {
        try
        {
            ObjectTypeResponse response = await objectTypeManager.ReadAsync(workspaceId, objectTypeArtifactId);
            string info = string.Format("Read object type {0} with Artifact ID {1}", response.Name, response.ArtifactId);
            Console.Write(info);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }        
}

Update an object type

Use the UpdateAsync() method to modify the properties of an object type. The following code sample illustrates how to call this method by passing the Artifact ID of a workspace and an object type, and an ObjectTypeRequest object to it.

Additionally, you can also restrict the update of an object type to the date that it was last modified by passing the value of LastModifiedOn property as an argument to the overloaded UpdateAsync() method. You can get the value of this property from an ObjectTypeResponse object, which is returned by the ReadAsync() method.

Delete an object type

Before you delete an object type, you may want to call the GetDependencyList() method to retrieve a list of dependent objects. For more information, see the following pages:

The following code sample illustrates how to call the DeleteAsync() method by passing Artifact IDs of a workspace and an object type.

public static async Task Delete_Async()
{
    int workspaceId = 1018486;
    int objectTypeArtifactId = 1035231;

    using (Services.Interfaces.ObjectType.IObjectTypeManager objectTypeManager = serviceFactory.CreateProxy<Services.Interfaces.ObjectType.IObjectTypeManager>())
    {
        try
        {
            await objectTypeManager.DeleteAsync(workspaceId, objectTypeArtifactId);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Retrieve a parent object types

Use the GetAvailableParentObjectTypesAsync() method to retrieve a list of parent object types. You may want to call this method before creating a new object type, because you must specify its parent object type.

public static async Task GetAvailableParentObjectTypes_Async()
{
    int workspaceId = 1018486;

    using (Services.Interfaces.ObjectType.IObjectTypeManager objectTypeManager = serviceFactory.CreateProxy<Services.Interfaces.ObjectType.IObjectTypeManager>())
    {
        try
        {
            List<ObjectTypeIdentifier> response = await objectTypeManager.GetAvailableParentObjectTypesAsync(workspaceId);
            foreach (ObjectTypeIdentifier objectType in response)
            {
                string info = string.Format("Read objectType {0} with Artifact ID {1}", objectType.Name, objectType.ArtifactID);
                Console.Write(info);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Retrieve a list of dependent objects

You can retrieve a list of objects in a workspace that are dependent on a specific object type. The GetDependencyList() method returns a list of Dependency instances, which include information about the relationship between object type that you specified, and the dependent object. For general information about dependent objects, see Deleting object dependencies on the RelativityOne Documentation site.

The following sample code illustrates how to call the GetDependencyList() method by passing the Artifact IDs of a workspace and an object type.

public static async Task GetDependencyList(int objectTypeID)
{
    int workspaceId = 1018486;

    using (Services.Interfaces.ObjectType.IObjectTypeManager objectTypeManager = serviceFactory.CreateProxy<Services.Interfaces.ObjectType.IObjectTypeManager>())
    {
        try
        {
            List<Dependency> response = await objectTypeManager.GetDependencyList(workspaceID, objectTypeID);
            foreach (Dependency dependency in response)
            {
                string info = string.Format("Object type {0} has a connection type {1} with {2} object(s).", dependency.ObjectType, dependency.Connection, dependency.Count);
                Console.Write(info);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Event Handler Manager API for object types

You can add custom behavior to an object type by attaching event handlers to it. For example, you might attach an event handler that performs a specific action when a user makes an update to an object and then attempts to save it. For more information, see Develop object type event handlers.

The Event Handler Manager service contains methods for programmatically attaching event handlers to an object type, and for detaching them. It also provides helper methods that you can use for the following purposes:

  • To retrieve a list of event handlers in a workspace, which could be attached to a specific object type.
  • To retrieve a list of event handlers currently attached to an object type.

See the following subsections for more information:

Attach an event handler to an object type

To attach an event handler to an object type, call the AttachAsync() method by passing the Artifact IDs of a workspace and an object type, and the ID of the event handler.

Note: In the following code sample, the eventHandlerId parameter is an identifier assigned by Relativity to the event handler. This identifier isn't the artifact ID for the event handler. The GetAttachedAsync() and GetAvailableEventHandlersAsync() methods return an EventHandlerResponse object, which has this ID property. For more information, see EventHandlerResponse class in the Services API.

public static async Task Attach_Async()
{
    int workspaceId = 1018486;
    int objectTypeArtifactId = 1035231;
    int eventHandlerId = 1982384;            

    using (Services.Interfaces.EventHandler.IEventHandlerManager eventHandlerManager = serviceFactory.CreateProxy<Services.Interfaces.EventHandler.IEventHandlerManager>())
    {
        try
        {
            await eventHandlerManager.AttachAsync(workspaceId, objectTypeArtifactId, eventHandlerId);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }            
}

Detach an event handler to an object type

To detach an event handler from an object type, call the DetachAsync() method by passing the Artifact IDs of a workspace and an object type, and the ID of the event handler.

Note: In the following code sample, the eventHandlerId parameter is an identifier assigned by Relativity to the event handler. This identifier isn't the Artifact ID for the event handler. The GetAttachedAsync() and GetAvailableEventHandlersAsync() methods return an EventHandlerResponse object, which has this ID property. For more information, see EventHandlerResponse class in the Services API.

public static async Task Detach_Async()
{
    int workspaceId = 1018486;
    int objectTypeArtifactId = 1035231;
    int eventHandlerId = 1982384;
    
    using (Services.Interfaces.EventHandler.IEventHandlerManager eventHandlerManager = serviceFactory.CreateProxy<Services.Interfaces.EventHandler.IEventHandlerManager>())
    {
        try
        {
            await eventHandlerManager.DetachAsync(workspaceId, objectTypeArtifactId, eventHandlerId);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }            
}

Retrieve event handlers attached to an object type

To retrieve the event handlers attached to an object type, call the GetAttachedAsync() method by passing the Artifact IDs of a workspace and an object type. If no event handlers are available for the object type, this method returns an empty list.

public static async Task GetAttached_Async()
{
    int workspaceId = 1018486;
    int objectTypeArtifactId = 1035231;

    using (Services.Interfaces.EventHandler.IEventHandlerManager eventHandlerManager = serviceFactory.CreateProxy<Services.Interfaces.EventHandler.IEventHandlerManager>())
    {
         try
        {
            List<EventHandlerResponse> response = await eventHandlerManager.GetAttachedAsync(workspaceId, objectTypeArtifactId);
            foreach (EventHandlerResponse eventHandler in response)
            {
                string info = string.Format("Read Event Handler {0} with Artifact ID {1}", eventHandler.ClassName, eventHandler.ArtifactID);
                Console.Write(info);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Retrieve available event handlers for an object type

You can retrieve a list of event handlers in a workspace that are compatible with a specific object type.

The following code sample illustrates how to call the GetAvailableEventHandlersAsync() method by passing the Artifact IDs of a workspace and an object type. If no event handlers are available for the object type, this method returns an empty list.

public static async Task GetAvailableEventHandlers_Async()
{
    int workspaceId = 1018486;
    int objectTypeArtifactId = 1035231;

    using (Services.Interfaces.EventHandler.IEventHandlerManager eventHandlerManager = serviceFactory.CreateProxy<Services.Interfaces.EventHandler.IEventHandlerManager>())
    {
        try
        {
            List<EventHandlerResponse> response = await eventHandlerManager.GetAvailableEventHandlersAsync(workspaceId, objectTypeArtifactId);
            foreach (EventHandlerResponse eventHandler in response)
            {
                string info = string.Format("Read Event Handler {0} with Artifact ID {1}", eventHandler.ClassName, eventHandler.ArtifactID);
                Console.Write(info);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Object Rule Manager API

You can use object rules to further customize the behavior of the object types that you create. The Object Rule Manager service simplifies this process by supporting CRUD operations on object rules. It also provides helper methods for retrieving information about associative objects, layouts, choices and choice fields used when creating or updating an object rule. For a complete list of object rules, see Guidelines for the Object Rule Manager API.

See the following subsections for more information:

Guidelines for the Object Rule Manager API

Review the following guidelines for the Object Rule Manager service:

  • Use the Object Rule Manager service to work with the same object rule types available through the Relativity UI. For general information about object rules, see Adding an object rule
  • Use the helper methods to retrieve information that you need when creating or updating an object rule. See the following table for suggested uses.
    Create or update this object rule Use these helper methodsCompares to this UI field

    Choice Behavior

    Choice field methodsField

    Default Layout

    Layout methodsAction
    Choice field methodsField
    Choice methodValue

    Default Layout on New

    Layout methodsAction

    Sub-List Button Visibility

    Choice field methodsField
    Choice methodValue
    Associated object methodsAssociative/Child Object
  • If a call to a helper method returns an empty list, you can't create that object rule on that object type.
  • To attach an object rule, the object type must be an RDO and non-system object, or a document object type.
  • Use -1 for the workspace ID when you want to indicate the admin-level context.
  • To retrieve the Artifact ID of an object rule, use the Object Manager Service. For more information, see Object Manager (.NET).

Create an object rule

You can create object rules by using the methods available on the IObjectRuleManager interface. For general information about object rules, see Adding an object rule.

Click the following drop-down links to view sample code for sub-list visibility, choices, and layouts rules. For more information about create methods, see the IObjectRuleManager interface in the Relativity.Services.Interfaces.ObjectRules namespace of the Services API.

Read an object rule

You can retrieve basic information about an object rule or extended information, which also includes operations that you have permissions to perform on the object rule. If you want to return extended information, use the overloaded method by passing Boolean values set to true for additional metadata and permissions as follows:

ObjectRuleResponse response = await objectRuleManager.ReadAsync(workspaceId, objectRuleArtifactId, true, true);

The following code sample illustrates how to call the ReadAsync() method by passing only the Artifact IDs of the workspace and the object rule. Consequently, it returns only basic information. For a list of basic and extended properties, see Read an object rule in Object Type Manager (REST).

public static async Task Read_Async()
{
    int workspaceId = 1018486;
    int objectRuleArtifactId = 1039509;

    using (Services.Interfaces.ObjectRules.IObjectRuleManager objectRuleManager = serviceFactory.CreateProxy<Services.Interfaces.ObjectRules.IObjectRuleManager>())
    {
        try
        {
            ObjectRuleResponse response = await objectRuleManager.ReadAsync(workspaceId, objectRuleArtifactId);
            string info = string.Format("Read object rule {0} with Artifact ID {1}", response.Name, response.ArtifactId);
            Console.Write(info);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }        
}

Update an object rule

You can update object rules by using the methods available on the IObjectRuleManager interface. All object rules have an overloaded update method called by passing the Artifact IDs of a workspace and the object rule, the appropriate request object for the rule type, and an optional DateTime object.

Note: To get the Artifact ID of an object rule, use the ReadAsync() method on the Object Manager (.NET).

When you want to restrict the update of an object rule to the date that it was last modified, pass the value of LastModifiedOn property as an argument to one of the overloaded update methods. You can get the value of this property from an ObjectRuleResponse object, which is returned by the ReadAsync() method.

Click the following drop-down links to view sample code for sub-list visibility, choices, and layouts rules. For more information about update methods, see the IObjectRuleManager interface in the Relativity.Services.Interfaces.ObjectRules namespace of the Services API.

Delete an object rule

You can remove an object rule from an object type by calling the DeleteAsync()method, and passing Artifact IDs of a workspace and an object rule to it. See the following code sample:

public static async Task Delete_Async()
{
    int workspaceId = 1018486;
    int objectRuleId = 1039509;

    using (Services.Interfaces.ObjectRules.IObjectRuleManager objectRuleManager = serviceFactory.CreateProxy<Services.Interfaces.ObjectRules.IObjectRuleManager>())
    {
        try
        {
            await objectRuleManager.DeleteAsync(workspaceId, objectRuleId);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Delete multiple object rules

You can remove multiple object rules across different object types by making a single call to the MassDeleteAsync() method. Pass the Artifact ID of the workspace and a list containing the Artifact ID for each rule that you want to delete to this method. See the following code sample:

public static async Task Delete_Async()
{
    int workspaceId = 1018486;
    int objectRuleId1 = 1039509;
    int objectRuleId2 = 1039525;
    int objectRuleId3 = 1039630;

    List<ObjectIdentifiers> objectRulesToDelete = new List<ObjectIdentifiers> = [ objectRuleId1, objectRuleId2, objectRuleId3];
 
    using (Services.Interfaces.ObjectRules.IObjectRuleManager objectRuleManager = serviceFactory.CreateProxy<Services.Interfaces.ObjectRules.IObjectRuleManager>())
    {
        try
        {
            await objectRuleManager.MassDeleteAsync(workspaceId, objectRulesToDelete);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Retrieve choices, choice fields, layouts, or associated objects

When you create or update an object rule, you must provide the Artifact ID of any associative objects, layouts, choices and choice fields that it references. The Object Rule Manager service provides several helper methods that you can use to retrieve the Artifact ID, name, and other information about these objects. For most objects, it includes overloaded methods with following signatures:

  • Method signature with workspace ID and Artifact ID parameters - use this method if you have the Artifact ID of the object type.
  • Method signature with workspace ID and ObjectTypeIdentifier object parameters - use this method if you have the user-friendly name of the object type, but not its Artifact ID. See ObjectTypeIdentifier class in the Relativity.Services.Interfaces.Shared.Models namespace of the Services API.

For more information, see Guidelines for the Object Rule Manager API.

Mass Operation Manager API

You can add mass operations to object types to further customize their behavior. When a user interacts with a mass operation, you can display a custom page or execute an event handler with specialized functionality. For general information about mass operations, see Adding a custom mass operation.

The Mass Operations Manager API includes methods for creating, reading, updating, and deleting mass operations. It also includes helper methods for retrieving information about object types that be associated with a mass operation, and available event handlers and layouts for use with a mass operation.

Review the following guidelines for working with this service:

  • Make sure that you set the appropriate field values for the type of mass operation that you want to create. See Create an object type and Update an object type.
  • Use the helper methods to retrieve object types, event handlers, and layouts available for associating with mass operation.
  • Mass operations aren't available in the admin-level context, so you must specify a workspace ID.

See the following subsections for more information:

Create a mass operation

You can a create a mass operation that displays a custom page or that executes a custom event handler when a user interacts with it. For general information about mass operations, see Adding a custom mass operation.

Click the following drop-down links to view sample code for custom page and event handler mass operations.

Read a mass operation

You can retrieve basic information about a mass operation or extended information, which also includes operations that you have permissions to perform on the mass operation. If you want to return extended information, use the overloaded method by passing Boolean values set to true for additional metadata and permissions as follows:

MassOperationResponse response = await massOperationManager.ReadAsync(workspaceId, massOperationId, true, true);

The following code sample illustrates how to call the ReadAsync() method by passing the Artifact IDs of the workspace and the mass operation. Consequently, it returns only basic information. For a list of basic and extended properties, see Read a mass operation.

public static async Task Read_Async()
{
    int workspaceId = 1018486;
    int massOperationId = 1830283;

    using (Services.Interfaces.MassOperation.IMassOperationManager massOperationManager = serviceFactory.CreateProxy<Services.Interfaces.MassOperation.IMassOperationManager>())
    {
        try
        {
            MassOperationResponse response = await massOperationManager.ReadAsync(workspaceId, massOperationId);
            string info = string.Format("Read object type {0} with Artifact ID {1}", response.Name, response.ArtifactId);
            Console.Write(info);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }        
}

Update a mass operation

You can update the properties of custom page and event handler mass operations by using the overloaded UpdateAsync() method.

You can also restrict the update of a mass operation to the date that it was last modified by passing the value of LastModifiedOn property as an argument to the overloaded UpdateAsync() method. You can get the value of this property from an MassOperationResponse object, which is returned by the ReadMassOperationAsync() method.

Delete a mass operation

You can remove a mass operation from object types by calling the DeleteAsync() method, and passing Artifact IDs of a workspace and a mass operation to it. See the following code sample:

public static async Task Delete_Async()
{
    int workspaceId = 1018486;
    int massOperationId = 1830283;

     using (Services.Interfaces.MassOperation.IMassOperationManager massOperationManager = serviceFactory.CreateProxy<Services.Interfaces.MassOperation.IMassOperationManager>())
     {
        try
        {
            await massOperationManager.DeleteAsync(workspaceId, massOperationId);
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Retrieve available object types for a mass operation

You can customize object types with additional functionality by creating mass operations for them. To retrieve a list of available object types in a specific workspace, call the GetAvailableObjectTypeAsync() method.

The following code sample illustrates how to call the GetAvailableObjectTypeAsync() method by passing the Artifact ID of the workspace containing the object types to retrieve.

public static async Task GetAvailableObjectTypes_Async()
{
    int workspaceId = 1018486;

    using (Services.Interfaces.MassOperation.IMassOperationManager massOperationManager = serviceFactory.CreateProxy<Services.Interfaces.MassOperation.IMassOperationManager>())
    {
        try
        {
            List<ObjectTypeIdentifier> response = await massOperationManager.GetAvailableObjectTypesAsync(workspaceId);
            foreach (ObjectTypeIdentifier objectType in response)
            {
                string info = string.Format("Read object type {0} with Artifact ID {1}", objectType.Name, objectType.ArtifactID);
                Console.Write(info);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Retrieve available event handlers for a mass operation

You can add an event handler for a mass operation to an object type. This event handler executes when the user executes the mass operation through the Relativity UI. You can retrieve a list of available event handlers by calling the GetAvailableEventHandlersAsync() method. For general information about event handlers, see Create an object type.

The following code sample illustrates how to call the GetAvailableEventHandlersAsync() method by passing the passing the Artifact ID of the workspace containing the event handlers to retrieve.

public static async Task GetAvailableEventHandlersAsync()
{
    int workspaceId = 1018486;

    using (Services.Interfaces.MassOperation.IMassOperationManager massOperationManager = serviceFactory.CreateProxy<Services.Interfaces.MassOperation.IMassOperationManager>())
    {
        try
        {
            List<MassOperationLayoutResponse > response = await massOperationManager.GetAvailableEventHandlersAsync(workspaceId);
            foreach (MassOperationEventHandlerResponse eventHandler in response)
            {
                string info = string.Format("Read event handler {0} with Artifact ID {1}", eventHandler.Name, eventHandler.ArtifactID);
                Console.Write(info);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Retrieve available layouts for a mass operation

When you add a mass operation that uses an event handler to an object type, you must select a layout that displays after the user initiates the operation in the Relativity UI. You can retrieve a list of layouts available for the object type by calling the GetAvailableLayoutsAsync() method. For general information about layouts, see Create an object type.

The following code sample illustrates how to call the GetAvailableLayoutsAsync() by passing the Artifact ID of the workspace containing the layouts to retrieve, and the Artifact ID of the object type associated with the layouts.

public static async Task GetAvailableLayoutsAsync()
{
    int workspaceId = 1018486;
    ObjectTypeIdentifier objectType = new ObjectTypeIdentifier{ ArtifactID = 1246375 }

    using (Services.Interfaces.MassOperation.IMassOperationManager massOperationManager = serviceFactory.CreateProxy<Services.Interfaces.MassOperation.IMassOperationManager>())
    {
        try
        {
            List<MassOperationLayoutResponse > response = await massOperationManager.GetAvailableLayoutsAsync(workspaceId, objectType);
            foreach (MassOperationLayoutResponse layout in response)
            {
                string info = string.Format("Read layout {0} with Artifact ID {1}", eventHandler.Name, eventHandler.ArtifactID);
                Console.Write(info);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        }
    }
}

Community Updates

Aero Developer FAQ Evolving the Platform Most recent release notes
Learn more Learn more Learn more

Additional Resources

   
Access Third-Party Tools with GitHub     Create .NET Apps Faster with NuGet
Visit github     visit nuget