top of page

Sage 100 AppDesigner String-Ressourcen aus .net-Code lesen / Read AppDesigner String resources from .net code

(alle Versionen/all versions)

Mittwoch, 1. Februar 2023

Hintergrund/Background

Lösungsmöglichkeit/Possible solution

Beispiel/Example



Hintergrund/Background


ree





Es gibt in der Praxis immer wieder Situationen, in denen auch in .net-Klassen basierend auf Sage-"BusinessProcessBase" das Abfragen von String-Ressourcen aus Sage AppDesigner-Lösungen durchaus gewünscht sein kann.

Z.B. bei Datenquellen, die über einen Geschäftsprozess auf der Basis von "BusinessProcessBase" aufbereitet werden und dynamisch somit Daten zusammenbauen, die dafür String-Ressourcen aus einer AppDesigner-Lösung benötigen.

Leider hat Sage das Lesen von AppDesigner String-Ressourcen primär in der Klasse "Sagede.OfficeLine.Shared.RealTimeData.MacroProcess.MacroProcessBase" über die Methode "ReadMetaDataResourceStrings" vorgesehen.

Jedoch beispielsweise nicht in Klassen, die auf "Sagede.OfficeLine.Shared.RealTimeData.BusinessProcesses.BusinessProcessBase" basieren. In neueren Sage 100-Versionen gibt es alternative Möglichkeiten).

Der folgende Quellcode adressiert dieses Problem für Sage 100-Versionen, in denen von Sage aus keine Funktionen zum Lesen der AppDesigner-String-Ressourcen verfügbar scheinen.


In practice, there are always situations in which the querying of string resources from Sage AppDesigner solutions is also required in .net classes based on Sage "BusinessProcessBase".

E.g. for data sources which are prepared via a business process based on "BusinessProcessBase" and thus dynamically assemble data that requires string resources from an AppDesigner solution.

Unfortunately, Sage intended to read AppDesigner string resources primarily in the "Sagede.OfficeLine.Shared.RealTimeData.MacroProcess.MacroProcessBase" class via the "ReadMetaDataResourceStrings" method.

But not, for example, in classes based on "Sagede.OfficeLine.Shared.RealTimeData.BusinessProcesses.BusinessProcessBase".

In newer Sage 100 versions, there are alternative possibilities.

The following source code addresses this issue for Sage 100 versions in which no functions to read the AppDesigner string resources appear to be available from Sage.



Lösungsmöglichkeit/Possible solution


Grundsätzlich kann man aus Sicht der Sage ERP-Umgebung stets auf die Sage Routinen für die AppDesigner-Metadaten zugreifen, wenn eine Kontext-Instanz vom Typ "Sagede.Shared.RealTimeData.Common.ApplicationContext" zur Verfügung steht.

Zum Ermitteln des Kontext in bestimmten Situationen siehe https://www.officium-inservio.com/sage-100-1/netgetappservercontext.

Der Code, den Sage für "ReadMetaDataResourceStrings" selber verwendet, ist sehr überschaubar und lässt sich leicht in eine statische Methode transferieren.

Nachfolgend ein Beispiel für einen Zugriff auf eine String-Ressource aus Sage AppDesigner-Lösung.


Basically, from the point of view of the Sage ERP environment, you can always access the Sage routines for the AppDesigner metadata if a context instance of the "Sagede.Shared.RealTimeData.Common.ApplicationContext" type is available.

To get the context in special situations refer to https://www.officium-inservio.com/sage-100-1/netgetappservercontext.

The code that Sage uses for "ReadMetaDataResourceStrings" itself is very straightforward and can easily be transferred to a static method.

Below is an example of accessing a String resource from the Sage AppDesigner solution.


/// <summary>
/// Gets an Sage ERP AppDesigner resource <see cref="string"/> from the AppDesigner section "<c>Grundlagen.Strings</c>".<br/>
/// </summary>
/// <param name="context">Required: The Sage ERP application context.</param>
/// <param name="resourceIndex">Required: The resource´s string <b>index</b>.</param>
/// <param name="resourceName">Required: The resource´s string <b>name</b>.</param>
/// <param name="partnerId">Recommended: The AppDesigner´s solution <b>partner identifier</b>. Example: If the fully qualified AppDesigner´s solution name would be "<c>100004711.MySolution</c>", the partner id would be "<c>100004711</c>".<br/>
/// If this value is provided with <c>null</c> or <see cref="string.Empty"/>, the default text "<c>Sage</c>" will be used automatically.</param>
/// <param name="packageId">Recommended: The AppDesigner´s solution <b>package identifier</b>. Example: If the fully qualified AppDesigner´s solution name would be "<c>100004711.MySolution</c>", the package id would be "<c>MySolution</c>".<br/>
/// If this value is provided with <c>null</c> or <see cref="string.Empty"/>, the default text "<c>System</c>" will be used automatically.</param>
/// <param name="returnErr">The return exception; only relevant if an exception occurred; otherwise <c>null</c>.</param>
/// <returns>The Sage ERP AppDesigner´s resource <see cref="string"/> from the AppDesigner section "<c>Grundlagen.Strings</c>" or <see cref="string.Empty"/> in case of problems.</returns>
/// <remarks>
/// Code logic based on Sage method "<c>Sagede.OfficeLine.Shared.RealTimeData.MacroProcess.MacroProcessBase.ReadMetaDataResourceStrings</c>".
/// </remarks>
public static string GetSageAppDesignerMetaDataResourceString( Sagede.Shared.RealTimeData.Common.ApplicationContext context ,
                                                               int resourceIndex ,
                                                               string resourceName ,
                                                               string partnerId ,
                                                               string packageId ,
                                                               out Exception returnErr )
{
  returnErr = null;
 
  if ( context == null )
  {
    returnErr = new SageErpArgumentNullException( "context" );
    return string.Empty;
  }
 
  if ( string.IsNullOrWhiteSpace( resourceName ) )
  {
    returnErr = new SageErpArgumentNullException( "Name" );
    return string.Empty;
  }
 
  if ( string.IsNullOrWhiteSpace( partnerId ) )
  {
    partnerId = "Sage";
  }
 
  if ( string.IsNullOrWhiteSpace( packageId ) )
  {
    packageId = "System";
  }
 
  try
  {
    var metadataResource = new Sagede.Shared.RealTimeData.Core.Provider.MetadataResources();
 
    using ( var metadata = Sagede.Shared.RealTimeData.Core.Provider.Providers.CreateMetadataProvider( context ) )
    {
      metadataResource.Strings = metadata.GetList<Sagede.Shared.RealTimeData.Common.StringResources>( Sagede.Shared.RealTimeData.Common.Enumerations.ResourceType.StringResources , context );
 
      // Note, "metadataResource.Constants" is also set in Sage ERP code in the original method "Sagede.OfficeLine.Shared.RealTimeData.MacroProcess.MacroProcessBase.ReadMetaDataResourceStrings"!
      // We better keep it the same - who knows what happens in the "Sage universe" otherwise ;-)
      metadataResource.Constants = metadata.GetList<Sagede.Shared.RealTimeData.Common.ApplicationConstants>( Sagede.Shared.RealTimeData.Common.Enumerations.ResourceType.ApplicationConstants , context );
 
      IEnumerable<Sagede.Shared.RealTimeData.Common.StringResource> stringResources = metadataResource.Strings.Where( s =>
                                                                                                                      {
if ( s.Id != resourceIndex || s.Name.IsNotIdenticalWith( resourceName ) || s.PartnerId.IsNotIdenticalWith( partnerId ) )
{
return false;
}
                                                                                                                         return s.PackageId.IsIdenticalWith( packageId );
} );
 
      Func<Sagede.Shared.RealTimeData.Common.StringResource , Sagede.Shared.RealTimeData.Common.StringResource> func = s => s;
 
      var textData = stringResources.Select( func ).FirstOrDefault();
 
      if ( textData != null ) return textData.Value.ToStringEnhanced();
    }
  }
  catch ( Exception ex )
  {
    returnErr = ex;
  }
 
  return string.Empty;
}

Anmerkungen zum Quellcode:

  • Es sind Referenzen auf nachfolgende Sage Assemblies notwendig: Sagede.Shared.RealTimeData.Core, Sagede.Shared.RealTimeData.Common, Sagede.OfficeLine.ControlCenter.MetaDataProvider.

  • Um Copy&Paste der Methode zu vereinfachen, wurden keine Namespace "using"-Anweisungen vorausgesetzt, sondern die Namespaces sind bei allen Sage-Typen angegeben.

  • In o.g. Methode wurden zwei eigene String-Erweiterungsmethoden "IsNotIdenticalWith(") sowie "ToStringEnhanced()" verwendet. Ähnliche Methoden werden Partner in der S10-Gruppe sicherlich ebenfalls haben. Der Zweck der Methoden dürfte auf der Hand liegen. Vor allem soll vermieden werden, dass string-"null"-Situationen zu ungewollten Effekten im Code-Ablauf führen.

  • Die Klasse "SageErpArgumentNullException" ist eine eigene Exception, die für Sage Code-Abläufe teils bei uns im Einsatz ist, um sofort zu erkennen, dass die Exception von unserem Code generiert wurde.

  • Das Abgreifen von String-Ressourcen ist ein gutes Beispiel für eine Situation, in der das Abfangen der Exceptions sinnvoller ist als diese "unkontrolliert" von der .net-Runtime "werfen" zu lassen. In der Praxis macht es keinen Sinn, einen kompletten Anwendungscode "abstürzen" zu lassen, nur weil ggf. ein Info-Text dann leer wäre. Es macht Sinn, im .net-Code stattdessen einen "Fallback"-Code einzubauen, der bei Nicht-Finden der String-Ressource einen Standardtext direkt aus dem .net-Code liefert. Natürlich hängt dies vom gewünschten Einsatz-Szenario ab, welche Vorgehensweise letzten Endes sinnvoll(er) ist.


Source Code Notes:

  • References to the following Sage assemblies are required: Sagede.Shared.RealTimeData.Core, Sagede.Shared.RealTimeData.Common, Sagede.OfficeLine.ControlCenter.MetaDataProvider.

  • In order to simplify the copy&paste of the method, no namespace "using" statements were added, but instead the namespaces are specified for all Sage types.

  • Two separate string extension methods, "IsNotIdenticalWith()" and "ToStringEnhanced()", were used in the above method. Tech Partners will certainly have similar methods as well. The purpose of the methods should be obvious. Above all, it should be avoided that string "null" situations lead to unwanted effects in the code flow.

  • The "SageErpArgumentNullException" class is a separate exception that we sometimes use for Sage code processes in order to recognize immediately that the exception was generated by our code.

  • Reading string resources is a good example of a situation where catching exceptions makes more sense than letting the .net runtime "throw" them "uncontrolled". In practice, it makes no sense to let a complete application code "crash" just because an info text might then be empty. It makes sense to include a "fallback" code in the .net code instead, which returns a standard text directly from the .net code if the string resource is not found. Of course, this depends on the desired application scenario, which procedure ultimately makes (more) sense.


Beispiel/Example

var textHelloWorld = GetSageAppDesignerMetaDataResourceString( this.Context , 1 , "Test" , "100000000" , "MyAppDesignerSolutionName" , out _ );
ree

bottom of page