Stefan Rieger · Jan 18, 2023 go to post

this always happens when you call Methods who try to "output" something to a stream other than the internal .net message stream - very much Methods do that to signal status and progress with the terminal.

Fortunately some of them can be forced to behave silent - e.g. For the Method %SYSTEM.OBJ.Load you must provide qspec with argument "/display=none"

hope that helps

Stefan Rieger · Mar 30, 2022 go to post

my guess was is it should be possible with any (os matching) iris/ cache -

your intention is to remotely access the databases on the nas? if you don't directly mount the database via filesystem (i'm not going to do that, too big fear from latency) you anyway have to have a installed db server on the NAS System to enable advanced features (don't know the name ist it ecp protocoll?)

Stefan Rieger · Mar 30, 2022 go to post

obviously did understand the question quite well ;-) - already thought about the possibility with docker. The other possibility i was wondering was not VM but install that directly.

Qnap (my Station) is based on Linux so theoretically shouldn't be impossible; My guess that this could become quite compilicated as the OS probably  is very customized

Stefan Rieger · Jan 3, 2022 go to post

my guess was that there migth be a little hack like calling stored procedures with brackets (which is not mentioned in any documentation): 

"{ call %SYSTEM.SQL_GetROWID()"

doesn't anybody have an idea on that? 

Stefan Rieger · Jan 3, 2022 go to post

this is the exception

InterSystems.Data.CacheClient.CacheException (0x80004005): [SQLCODE: <-1>:<Ungültige SQL-Anweisung >]
[Location: <Prepare>]
[%msg: < IN erwartet, <Ende des Ausdrucks> gefunden ^LOCK TABLE Appliance . Setting>]
   bei InterSystems.Data.CacheClient.CacheADOConnection.GetServerError(Int32 rc)
   bei InterSystems.Data.CacheClient.CacheADOConnection.processError(Int32 error, Int32 allowError)
   bei InterSystems.Data.CacheClient.InStream.readHeader(CacheCommand stmt, Int32 stmt_id, Int32 type, Int32 allowError, Boolean requestData)
   bei InterSystems.Data.CacheClient.InStream.readHeader(CacheCommand stmt, Int32 type, Int32 allowError)
   bei InterSystems.Data.CacheClient.CacheCommand.sendDirectUpdateRequest()
   bei InterSystems.Data.CacheClient.CacheCommand.Execute()
   bei InterSystems.Data.CacheClient.CacheCommand.ExecuteReaderObject(CommandBehavior behavior, String method)
   bei InterSystems.Data.CacheClient.CacheCommand.internalExecuteNonQuery()
   bei InterSystems.Data.CacheClient.CacheCommand.ExecuteNonQuery()

Stefan Rieger · Jan 3, 2022 go to post

did not set any dialect - unfortunately that did not help ;-(

i'm afraid i didn't get your point "not part of IRIS SQL"? - reading the Documentation i thought it is (pls. keep in mind that this command can be invoked via embedded sql - and ahead i also can invoke that succesfully from my little sql-tool.

Stefan Rieger · Dec 28, 2021 go to post

Hi Nigel, thanks - but my question was especially HOW a working LOCK TABLE Command with a .Net Provider CacheCommand would look like - i couldn't get that to work..

Locking a table via my Sql Tool (Database.Net) just works fine so obviously there is a way?

Stefan Rieger · Jul 19, 2021 go to post

hi matjaz,

no - maybew that was misleading;  i just store and retrieve byte arrays directly to fields defined as  %Stream.GlobalBinary and do the encoding and serialization stuff with .net

Stefan Rieger · Jul 18, 2021 go to post

just for curiosity: i do the same thing by cutting off the iris stuff completely; Is there any reason taht you let iris store the convert, store and convert back instead of just sending binary data to iris directly?

it's so much easier if you let c# pack the json string to byte[] and deserialize that....

Stefan Rieger · Jul 15, 2021 go to post

is it possible that you're missing the "Ens* Routine" mapping from Database Enslib to the namespace you're working in? (have a look to System/Configuration/Namespaces within Administration Portal)

Stefan Rieger · Jul 14, 2021 go to post

the first two might be a bit off topic - this points more or less to .net architectural decisions. if you want email  me  directly (s-rieger@gmx.net) and i try to give you some hints.

3.: Did not look into that deep - do you have an example on which method you really need that?

Stefan Rieger · Jul 13, 2021 go to post
public class TestClass : INotifyPropertyChanged
{
    
    public event PropertyChangedEventHandler PropertyChanged;

    public string Test
    {
        set
        {
            if (_test != value)
            {
                _test = value;
                PropertyChanged?.Invoke(this, nameof(Test));
            }
                
        }
        get
        {
            return _test;
        }
    }
    string _test;

}
Stefan Rieger · Jul 13, 2021 go to post

i'm not sure if understood correctly, but for .net this normally is done like that:

Stefan Rieger · Jul 13, 2021 go to post

yes, that sounds for me like tracing to the database writer - i'm not aware of this functionality in any database at all. 

keep in mind that changing a database field depends on several factors - if you're in a transaction  it might look for the client who's running the update like the field is changed, for others not; If transaction fails the field content will be rolled back.

the "normal" way is just to poll the property regularly to see if it's been changed

Stefan Rieger · Jul 13, 2021 go to post

i'm not sure if i understood rigth - your'e talking about a event listener on a database field change? (keep in mind, that the properties content is a database field which potentially could be changed by cos, sql or even direct global access)?

Stefan Rieger · Jul 12, 2021 go to post
[Test]
public void pcg()
{

    using (var conn = ConnectionFactory.CreateConnection() as IRISADOConnection)
    {
        
        conn.ChangeNs("\"USER\"");
        var tc = new TestClass(conn);
        tc.MyProperty = "FuzziGagga";
        tc.Save();
        Assert.NotNull(tc.Id);
        var id = tc.Id.Value;

        tc = new TestClass(conn, id);
        var myProp = tc.MyProperty;
        Assert.True(myProp == "FuzziGagga");

    }
    
}
Stefan Rieger · Jul 12, 2021 go to post
public class TestClass : IDisposable
{

    public TestClass(IRISADOConnection conn, int idValue)
    {
        iris = IRIS.CreateIRIS(conn);
        proxy = (IRISObject)iris.ClassMethodObject("User.MyClass", "%OpenId", idValue);
        Id = idValue;
    }

    public TestClass(IRISADOConnection conn)
    {
        iris = IRIS.CreateIRIS(conn);
        proxy = (IRISObject)iris.ClassMethodObject("User.MyClass", "%New");
    }

    public void Save()
    {
        int related = 1;
        proxy.InvokeIRISStatusCode("%Save", related);
        if (!Id.HasValue)
        {
            Id = (int)proxy.InvokeLong("%Id");
        }
    }
    
    public string MyProperty
    {
        set
        {
            proxy.Set(nameof(MyProperty), value);
        }
        get
        {
            return proxy.GetString(nameof(MyProperty));
        }
    }

    internal int? Id;
    
    IRISObject proxy;
    IRIS iris;

    public void Dispose()
    {
        iris.Close();
    }
    
}
Stefan Rieger · Jul 12, 2021 go to post

so mainly you have to work with Iris Class Documentation to find details of the Method Signatures.

If it's a classMethod you can invoke the matching IRIS Method directly, if it's a InstanceMethod you have to invoke %OpenId or %New() to have a Object Handle and invoke Methods on them. 

Invoking the Methods on Iris is straight forward and not complicated - (used to be a pain with Cache); 

Be aware that RefrenceParameters and Results with .Net Interface MAY NOT correspond to IRIS Documentation - actually i can't give you a reliable advice on that.

N.B. Some method just DO NOT WORK with .Net Interface - mainly this are all Methods which produce multiple lines of output if invoked by Iris Terminal (e.g. %ValidateIndices). If i have to invoke a method like that i just wrap them into a userdefined IRIS Helper-Class and invoke this helper class. (you even might declare your user method as SQL Method beeing able to invoke this method by sql)

Stefan Rieger · Jul 12, 2021 go to post
this is taken from testlibrary - where "Appliance.User" is a custom defined Object. 
Using IRIS and invoking ClassMethod %Library.Pesistent.%OpenId to receive a ObjectReference to the stored object with ID 1. 
Using that ObjectReference i'm able to invoke %ClassName on that object to get the ClassName of that object. 
With tryBlock i'm using same extensions to invoke same helper methods wich encapsulate more classMethods from %Persistent - but that follows exactly same pattern...


using (var conn = ConnectionFactory.CreateConnection() as IRISADOConnection)
{

    var iris = IRIS.CreateIRIS(conn);
    var obj = iris.ClassMethodObject("Appliance.User", "%OpenId", 1 );
    Assert.NotNull(obj);
    
    var iObj = obj as IRISObject;
    Assert.NotNull(iObj);
    Assert.True(iObj.Oref() == "3@Appliance.User");
    
    var clsName = iObj.InvokeString("%ClassName", true );
    try
    {
        var id = iObj.InvokeString("%Id");
        iris.LockId(clsName, id);
        iris.UnLockId(clsName, id);
        
        //iris.ValidateIndex(clsName);
        iris.PurgeIndex(clsName);
        iris.BuildIndex(clsName);
        
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
        throw;
    }
Stefan Rieger · Jul 7, 2021 go to post

1. like it this way; if you want "net" property access you have to generate proxy classes which wrap the mentioned methods...

2. InterSystems.Data.IRISClient.AD.IRISObject  is in Assembly InterSystems.Data.IRISClient, maybe dll not matching framework version? (umpf, sorry didn't read that you're on 2019... don't know about that)

Stefan Rieger · Mar 6, 2020 go to post

found out about the second; That only affects raw Globals without ClassDefinition...

Obviously a problem of how "Types" are constructed; partly they're embedded in the data; partly it's part of the ClassDefinition?

So just guessing; The problem with storing decimal values is because at the level inserting data via IRISList you don't have info about the Scale-Factor - does this make it impossible to store data internally?

Stefan Rieger · Mar 6, 2020 go to post

in addition retrieving values from IRISlist somehow ist strange as well. there are multiple values i could add to a list but will be retrieved as different types: long -> int/ or long, depending on the value short -> int bool -> int float -> double string -> byte[]

will that stay like that or is there room for improvements?

Stefan Rieger · Feb 28, 2020 go to post

good idea; i can see the point now: As %SYSTEM.OBJ.LoadStream() Method checks if Parameter <stream.IsCharacter> it fails as the parameter is passed as "No-Object". With 2019 libs i had the opportunity to pass in native CacheObjects - that's not possible with this architecture and IRISObjects?

Stefan Rieger · Feb 28, 2020 go to post

unfortunately results with same error: <INVALID OREF>zLoadStream+1^%SYSTEM.OBJ.1.... as far as i can see it's thrown at InterSystems.Data.IRISClient.InStream.readHeaderSYSIO(Int64 msgid, List`1 allowedErrors)

Any Ideas?

Stefan Rieger · Feb 24, 2020 go to post

thanks; that did the job...

... and i guess you're right with what you say - for my perspective this behaviour is somehow strange; Shouldn't then InterSystems add a "dummy device" on calling methods to avoid these kind of exceptions? 

Never before i had a problem like that calling e.g. LoadMethod and for my sight it's a bit irritating that InterSystems expect me to know if a method i'll call will write to a "not allowed device" and to investigate how to trick that away 

Stefan Rieger · Feb 23, 2020 go to post

I would expect %JSONAdapter.%JSONExportToString() should be the function to serialize arrays to string; 

extend or wrap your array into a object that inherits from %JSON.Adaptor and serialize it...

btw. the Array Objects do have "Next or GetAt" function to iterate as well (look at the matching classdocumentation)

Stefan Rieger · Feb 10, 2020 go to post
Hi Raj, on writing test code for you i noticed that this might be a "interpretation" problem.

I assumed that - as son as the transaction is started - the property IsTransactionActive will be true. In Fact this property is True only after the first Transaction is executed. 

It would be very practical if i could detect from a connection if a Transaction was started - Additionally i'd like to have  access to the IrisTransaktion on the Connection itself - would make programming very much easiere....