go to post Michael Cohen · Nov 14, 2019 I think I now understand your question and why you asked. You want to control the list of Actors stored for each Movie by Populate(). The problem is that, for each new Movie, your ActorFilter method is called by Populate a number of times to create the list of Actors for the Movie. ActorFilter needs to return a random Actor ID that is for a Man, and that was not already generated for this Movie. Checking that the random Actor ID exists and is a Man is straightforward. The trick is to check the interim list of Actors for this Movie to ensure that a duplicate ID is not returned. Since Populate and ActorFilter are instance methods in Movie, the ..Actor syntax accesses the current/partial Actor %Collection.ListOfObj in the Movie currently being Populated. Here are my example Movie and Actor classes to accomplish this. Class Packet.Actor Extends (%Persistent, %Populate){Property Name As %String [ Required ]; Index NameIndex On Name [ Unique ]; Property Age As %Integer(MAXVAL = 100, MINVAL = 10) [ Required ]; Property Sex As %String(DISPLAYLIST = ",Woman,Man", VALUELIST = ",F,M") [ Required ];} Class Packet.Movie Extends (%Persistent, %Populate){Property Title As %String(POPORDER = 0) [ Required ]; Index TitleIndex On Title [ Unique ]; Property Date As %Date(POPORDER = 2) [ Required ]; Property Actor As list Of Packet.Actor(POPSPEC = ".ActorFilter()") [ Required ]; Method ActorFilter(){ // Returns a Packet.Actor ID, where Actor is a Man not already in this Movie's current Actors list // Assumes Actors was just populated, so <IdLocation>^Packet.ActorD</IdLocation> identifies location of last ID s maxID=^Packet.ActorD // loop until we find an ID or tried 100 times s tried=0 s ID="" while ID="" { s tried=tried+1 if tried>100 quit s ID=$random(maxID)+1 // ..Actor is this Movie's list of Actor IDs so far s found=0 if ..Actor.Size>0 { //check whether this ID was already used s key="" s used=..Actor.GetObjectIdNext(.key) while key'="" { if ID=used {s found=1 quit } //this ID was already used s used=..Actor.GetObjectIdNext(.key) } } if found s ID="" CONTINUE //already used s actor=##class(Packet.Actor).%OpenId(ID) if actor="" CONTINUE //not found if actor.Sex'="M" s ID="" CONTINUE //wrong Sex } quit ID} }
go to post Michael Cohen · Nov 13, 2019 OR, is the goal to be able to Populate Actor and Movie data that satisfies the conditions?
go to post Michael Cohen · Nov 13, 2019 Sorry, but the problem sill is not clear to me. First, I don't think that you want a SQL query that returns all movies with unique Man Actors. I think you want to prevent save of a Movie if all Actors are not Man or list of Actors is not unique for this Movie. Assuming that, are Movies being saved via SQL Insert/Update, via object Save, or both? Once you confirm, we should have some suggestions to confirm that the Movie's list of Actors is acceptable.
go to post Michael Cohen · Sep 6, 2019 Sorry I was hasty in my judgement of "num+1". "1+num" is not faster.I am not a performance/benchmark expert, but, as noted earlier, timings will vary because the OS is doing other things.Increasing the loop from 1E6 to 1E7, and repeating/alternating the tests in the program, my laptop was fairly consistent:+1: 0.2048121+: 0.201091+1: 0.2015261+: 0.207091+1: 0.203081+: 0.201488+1: 0.2026131+: 0.202009
go to post Michael Cohen · Sep 6, 2019 Timings are always variable, but the general trends are clear ("count=1+count" still wins).I added a $seq test: SET $SEQ(^myseq)=1 for i=1:1:lim { if $SEQ(^myseq) } w count_" if $SEQ(^myseq)): "_((+$p($now(),",",2))-start)_" seconds",!Results:1000000 count=count+1: .010362 seconds1000000 count=1+count: .007998 seconds1000000 if $i(count): .025006 seconds1000000 if $SEQ(^myseq)): .099028 seconds
go to post Michael Cohen · Sep 5, 2019 I noticed that a couple of folks changed the original:set tCount=tCount+1to:if $increment(sum) I wondered if that was in fact a performance improvement, so wrote: s lim=1000000 s start=+$p($now(),",",2) s count=0 for i=1:1:lim { s count=count+1 } w count_" count=count+1: "_((+$p($now(),",",2))-start)_" seconds",! s start=+$p($now(),",",2) s count=0 for i=1:1:lim { s count=1+count } w count_" count=1+count: "_((+$p($now(),",",2))-start)_" seconds",! s start=+$p($now(),",",2) s count=0 for i=1:1:lim { if $i(count) } w count_" if $i(count): "_((+$p($now(),",",2))-start)_" seconds",! The result is:1000000 count=count+1: .010256 seconds1000000 count=1+count: .008554 seconds1000000 if $i(count): .024483 seconds So, "s count=1+count" is a little faster than "s count=count+1", but 3 time faster than "if $i(count)".
go to post Michael Cohen · Oct 24, 2018 I haven't tried this, but Microsoft's DbContext doc here:https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...seems to match our example down this page in our docs here:https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...I don't find ConfigureServices in our docs, but Microsoft indicates that it is optional:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=a...Sorry for lack of detail.
go to post Michael Cohen · Sep 26, 2018 Roberto, Am I correct that you are having the common problem of SQL queries can return multiple columns that are too wide to display/print? A solution is to truncate the output widths without changing the class/table definition or the stored data. For example, if this is too wide: SELECT ID, Name, AltName, Address... FROM table WHERE BusinessType = ? there are solutions, depending on how/where the SQL is entered. If you are entering such queries manually, you can simply truncate, as with: SELECT ID, SUBSTR(Name,30), SUBSTR(AltName,25), Address... FROM table WHERE BusinessType = ? If this is a class query, you can change it to take these MAXLEN parameters. For example, in our SAMPLES namespace, class Sample.Person includes: Query ByName(name As %String = "") As %SQLQuery(CONTAINID = 1, SELECTMODE = "RUNTIME") [ SqlName = SP_Sample_By_Name, SqlProc ] { SELECT ID, Name, DOB, SSN FROM Sample.Person WHERE (Name %STARTSWITH :name) ORDER BY Name } You could create a similar Query that accepts truncate values, like: Query ByNameT(name As %String = "", nameLen As %Integer = 50) As %SQLQuery(CONTAINID = 1, SELECTMODE = "RUNTIME") [ SqlName = SP_Sample_By_NameT, SqlProc ] { SELECT ID, SUBSTR(Name,1,:nameLen), DOB, SSN FROM Sample.Person WHERE (Name %STARTSWITH :name) ORDER BY Name } Then users can run such SQL queries like: SELECT * FROM Sample.SP_Sample_By_NameT('A',10) and see truncated column data. Dynamic SQL (%SQL.Statement) provides similar functionality.
go to post Michael Cohen · Mar 7, 2018 Hello Vivek,Microsoft LINQ adds query capability to .net. You are asking whether similar things can be done with COS.First, please confirm my understanding of your example.Looking at our Sample.Person database, I see that property FavoriteColors is a list of strings. I can find or count all Persons with 'Red' as one of their FavoriteColors with SQL:SELECT ID, FavoriteColors FROM Sample.Person WHERE FOR SOME %ELEMENT(FavoriteColors) (%VALUE='Red')SELECT COUNT(*) FROM Sample.Person WHERE FOR SOME %ELEMENT(FavoriteColors) (%VALUE='Red')You can capture the count in COS code with: &sql(SELECT COUNT(*) INTO :myCount FROM Sample.Person WHERE FOR SOME %ELEMENT(FavoriteColors) (%VALUE='Red'))So COS provides the desired functionality for objects stored in the database. You seem to want to examine a list of objects in memory to count those that have Property2='Red'If your objects are in memory but not on disk, then I don't believe that COS offers a similar facility to LINQ WHERE. Hopefully, iterating is not a major inconvenience.
go to post Michael Cohen · Feb 2, 2018 Hello Edmund,Which Caché version are you using? I believe that some earlier versions were incomplete. In particular, when you unzip/extract CacheEF.zip from folder <installdir>\dev\dotnet\bin\v4.0.30319do you get 16 items as I do for Caché 2017.1.1.111?Are Caché and Visual Studio on the same PC?The instructions call for running setup with a one-time arg -installflags SetupVS. ***Have other users of EF been able to run these?What version of Visual Studio? I have 2015 (14.0), and I found VsDevCmd.bat in C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools. But instead opened an admin cmd prompt to the indicated directory, and did: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE>.\devenv.com /setupThis seemed to take a minute or so. I don't know if this is what finished the setup, but Visual Studio does now offer the Caché Server option.On my Win10 64-bit...Local Caché installations are registered here:HKEY_CURRENT_USER\SOFTWARE\InterSystems\Cache\ConfigurationsHKEY_USERS\S-1-5-21-112145844-1872675854-1690816760-2965\SOFTWARE\InterSystems\Cache\Configurationsand servers here:HKEY_CURRENT_USER\SOFTWARE\InterSystems\Cache\ServersHKEY_USERS\S-1-5-21-112145844-1872675854-1690816760-2965\SOFTWARE\InterSystems\Cache\ServersODBC Drivers here:HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INIODBC DSNs here:HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INIHKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\ODBC\ODBC.INIVS2013 data providers here:HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\12.0\DataProvidersHKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\12.0_Config\DataProvidersHKEY_USERS\S-1-5-21-112145844-1872675854-1690816760-2965\SOFTWARE\Microsoft\VisualStudio\12.0_Config\DataProvidersVS2015 data providers here:HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\14.0_Config\DataProvidersHKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\DataProvidersHKEY_USERS\S-1-5-21-112145844-1872675854-1690816760-2965\SOFTWARE\Microsoft\VisualStudio\14.0_Config\DataProvidersDo you have similar registry entries?And, since these are registry changes, you likely need to restart Visual Studio, and perhaps Caché or Windows.