Get Reference Assemblies Dashboard (Kinetic & Classic)

Ok, so I did another thing.

People have been requesting to be able to pull down the files to develop BPMs locally, to
do development in more advanced IDEs.

This is the same way the client does it.

Please see these threads for previous discussions:


Anywho,

The initial dashboard is done in the Kinetic Web Client.

What this dashboard does is utilize the GetAssemblyBytes Function of the Service ICE → EcfTools.

If you provide that Business Object with an Assembly name, it will give you the Assembly in bytes.

The list of Assemblies available can be had by querying the Ice.BO.BpMethod.GetAvailableReferences
Business Object, and ask for “Assemblies”.

This is accomplished in a UBAQ directive on the GetList in a BAQ to drive the dashboard.

The code is as follows:

// UBAQ Directive:
// Post-Processing Directive on GetList


//ref: Ice.Contracts.BO.BpMethod
//using Ice.Assemblies;

  using (Ice.Contracts.BpMethodSvcContract bpMethod = ServiceRenderer.GetService<Ice.Contracts.BpMethodSvcContract>(Db))
  {
      List<Ice.Contracts.BO.BpMethod.ReferenceInfo> referenceAssemblies = bpMethod.GetAvailableReferences("Assemblies");
      
      int x = 0;
      List<ResultsUbaqRow> resultsTemp = new List<ResultsUbaqRow>(); 
      foreach(Ice.Contracts.BO.BpMethod.ReferenceInfo refAss in referenceAssemblies)
      {
      
          ResultsUbaqRow newRow = new ResultsUbaqRow();
          
          newRow.Calculated_AssemblyName = refAss.Name;
          newRow.Calculated_FileName = refAss.FileName;
          newRow.Calculated_Version = refAss.Version;
          
          string fileNameNoExt = System.IO.Path.GetFileNameWithoutExtension(refAss.FileName);
          
          newRow.Calculated_ZipName = fileNameNoExt + ".zip";
          
          
          newRow.RowIdent = x.ToString(); x++;
          
          resultsTemp.Add(newRow);
      }
      result.Results.Clear();
      result.Results.AddRange(resultsTemp);
  };

The dashboard was created with the Classic Client Dashboard Designer, and is only the basics.
I published it as a Kinetic App, and modified from there.

Note: This can’t be server paged, find that in the grid and turn it off if you are trying
to recreate this from scratch.

I added a few textboxes and a button to download the files:

I added widget code to populate the event for download:

This code fills the CallContextBPMData with:

CallContextBPMData.ShortChar01 -> [GAB] (Trigger Phrase)
CallContextBPMData.ShortChar02 -> Assembly Name
CallContextBPMData.ShortChar03 -> File Name

Then we are going to do a little Tom-Foolery with the file-transfer-erp widget… :rofl:

You can only download files with the file-transfer-erp widget from special folders, so no dice
there, however, it’s return type is byte, and one of it’s parameters is file name…

So what I have done, is pass some of my data I need in the CallContextBPMData, and the
zip file name as a parameter for file-transfer-erp.

There is a pre-processing method directive on Ice.Lib.FileTransfer.DownloadFile
which checks for the trigger phrase.
If it finds it, we hijack the method, call EcfTools.GetAssemblyBytes, and return that data
after we zip it.

I was returning just the dll file, but the browsers weren’t real happy, so now it’s zipped.

Code for Method Directive:

// Method Directive:
// Pre-Processing Directive on Ice.Lib.FileTransfer.DownloadFile
// Ice -> Simple Service -> FileTransfer


//ref: Ice.Contracts.Lib.EcfTools
//using Ice.Assemblies;
//using System.IO;
//using System.IO.Compression;  
  
  
  Func<string, byte[], byte[]> ZipByteArray = (fileName, fileBytes) =>
  {
      byte[] retBytes = null;
      
      using (MemoryStream zipMS = new MemoryStream())
      {
          using (ZipArchive zipArchive = new ZipArchive(zipMS, ZipArchiveMode.Create, true))
          {
              var zipArchiveEntry = zipArchive.CreateEntry(fileName, CompressionLevel.Fastest);
              
              using (var zipStream = zipArchiveEntry.Open())
              {
                  zipStream.Write(fileBytes, 0, fileBytes.Length);
              }
          }
          
          zipMS.Flush();
          retBytes = zipMS.ToArray();
      };
      
      return retBytes;    
  };
  
  
  //Processing begins here: -->
  
  if(callContextBpmData.ShortChar01.ToLower() == "[gab]")
  {
      string assemblyToGet = callContextBpmData.ShortChar02;
      string fileNameToGet = callContextBpmData.ShortChar03;
      
      using (Ice.Contracts.EcfToolsSvcContract ecfTools = ServiceRenderer.GetService<Ice.Contracts.EcfToolsSvcContract>(Db))
      {
          byte[] resultBytes = ecfTools.GetAssemblyBytes(assemblyToGet);
          
          result = ZipByteArray(fileNameToGet, resultBytes);
      }; 
      
      MarkCallCompleted();
  }

Finally we clear the CallContextBPMData.

You choose your file, and click Download:

bam yes GIF

2 Likes

This is all 2023.1.
Earlier the grid was unusable, and this grid is still bad.

GetReferenceAssembliesDBMetaUIs.zip (3.8 KB)
KEV_Get_Assemblies.baq (20.8 KB)
GRADB_MethodPreFileTransferSvcCustomCode.cs (1.5 KB)
KEV_Get_AssembliesBaq_GetListCustomCode.cs (1.2 KB)

Classic Dashboard coming soon.

2 Likes

Reserved for Classic Dashboard

I arrived at your post as soon as I received the notification. Thank you for sharing; you’re quick and saved me a lot of time! :star_struck:

I would like to mention that after downloading the DLLs, I realized that I needed to set up a new class in my IDE to begin coding. This class includes a namespace, a main method, and other elements that prevent me from simply copying and pasting. Without these components, my class would display numerous errors due to the missing methods.

Here is what I did to set up a class in my IDE (Rider) and start coding, mimicking the Epicor BMP code window (almost). This process should work for most IDEs.

  1. Create a new C# project.
  2. Import the necessary DLLs downloaded from your amazing tool as assembly references.
  3. Open the .csproj file generated by the project.
  4. Replace the existing PropertyGroup block with the following:
 <PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net5.0</TargetFramework>
  <LangVersion>preview</LangVersion>
 </PropertyGroup>
  1. Create a new class in your project and delete all of its content.

  2. Navigate to “Create programming interface” in your BPM.
    image

  3. Copy the blue sections from the generated code.

  4. Paste it into your IDE.

  5. Initialize the commonly used variables.

var Db = new ErpContext();
var BaqConstants = new BAQConstants(null);
Ice.Tablesets.ClientRow callContextClient = new ClientRow();
ISmtpMailer GetMailer(bool async)
{
    return new SmtpMailer("");
}

10 Start coding.

My class looks like this:

Pros:

  • IntelliSense
  • IDE code validation
  • Ability to view expected parameters for methods

Cons:

  • Cannot run it directly from the IDE.

This approach has worked well for me, but there might be more efficient methods that you could explore. If you come across any faster or more effective approaches, feel free to share them.

you better use .net 6 not 5, if you want to mimic server.

3 Likes

@hmwillett , If you would kindly look in my post above, I have some dataview-clear widgets to
clear the CallContextBPMData DataView.

The way I am doing it looks horribly inefficient. All I want to do is chain that clear event to be after the
file transfer, no matter what. Is there a better way?

Hi Kevin,
You can just do another row-update to reset fields used to their original “”, 0, false, etc. value.
image

If you want to use a dataview-clear, check the debugger, it will tell you its obsolete and you should use data-clear widget instead.
image
image

and thanks for the post above! very interesting.
Regards,

1 Like

@klincecum don’t put branch conditions. Just wire it up right after and it will fire regardless.

I swear I tried that and it didn’t work. That’s why I was asking!

That of course doesn’t mean I didn’t do it wrong.

I’ll try it again, thanks. :tada:

I mean… I’m preeetty sure that works? Hahaha
I guess, to be fair, I never explicitly tried to make it fail to see if it nukes the whole event if it errors. :thinking:

Just throw down some :popcorn: and I’ll let you know.