Fehler beim Ausführen einer gespeicherten Prozedur, die keine Ergebnisse zurückgibt




 Ich benutze derzeit Dapper auf einem unserer Projekte und wir führen gespeicherte Prozeduren die ganze Zeit. In den ersten paar Methoden funktionierte alles gut, wenn die von uns ausgeführte gespeicherte Prozedur Zeilen zurückgibt. 

 Im Moment stehe ich vor einem Problem, wenn ich versuche, Daten von einer gespeicherten Prozedur abzurufen, die Informationen zurückgibt, wenn sie gefunden werden. Dies ist ein sehr häufiger Anwendungsfall (z. B. Anmelden von Benutzern in der Anwendung). Wenn die `Query` Methode aufgerufen wird und der Sproc keine Zeile zurückgibt, löst der Dapper eine `ArgumentException` mit der folgenden Nachricht aus: 

 *"Wenn Sie die Multi-Mapping-APIs verwenden, stellen Sie sicher, dass Sie den splitOn-Parameter festlegen, wenn Sie andere Schlüssel als die ID haben. Parametername: splitOn"* 

 Der Code, den ich verwende, ist: 


```
using (var conn = new SqlConnection(connString))
{
    conn.Open();

    return conn.Query<Customer>(
            sql: "prc_GetCustomer",
            param: new { Parameter = p },
            commandType: CommandType.StoredProcedure).FirstOrDefault();
}

```
 Ich bin mir bewusst, dass es eine `Execute` Methode gibt, die verwendet werden sollte, wenn erwartet wird, dass die Prozedur keine Zeilen zurückgibt, aber es ist wirklich nicht meine Situation. Auch dapper Ausnahme ist irreführend, da ich nicht Multi-Mapping verwenden. 

 Irgendwelche Ideen? Vielen Dank!
Wenn die Abfrage keine Ergebnisraster zurückgibt, sollten Sie Execute und nicht Query verwenden.
<h1><code>sudo su postgres</code></h1><p>nachdem ich diese <a href="https://stackoverflow.com/q/7765269/642706">geschlossene frage zum stapelüberlauf</a> und <a href="https://stackoverflow.com/q/23982282/642706">sein duplikat</a> gefunden habe, werde ich hier die gefundenen lösungen veröffentlichen <a href="https://stackoverflow.com/a/7765367/642706">dort</a> und <a href="https://stackoverflow.com/a/23982810/642706">dort</a> .</p><p>➥ sie müssen den befehl <code>sudo</code> <em> kombiniert</em> mit dem befehl <code>su</code> verwenden.</p><pre class="lang-bash prettyprint-override"><code>sudo su postgres </code></pre><p>wenn sie dazu aufgefordert werden, geben sie ihr <strong> macos-administratorbenutzerkennwort </strong> anstelle des kennworts <code>postgres</code> ein. zu diesem zeitpunkt ist ihr mac-administrator derjenige, der den befehl <code>sudo</code> ausführt, nicht der benutzer <code>postgres</code>.</p><p>ich verstehe zwar nicht, warum wir das <code>sudo</code> mit dem <code>su</code> kombinieren müssen, aber es funktioniert tatsächlich für mich in <a href="https://en.wikipedia.org/wiki/macos\_mojave">macos mojave</a> . ich habe diesen tipp bei meinen verschiedenen suchanstrengungen im zusammenhang mit bsd und linux nicht gefunden. vielleicht handelt es sich also nur um einen mac apple, möglicherweise um eine zusätzliche sicherheitsmaßnahme. oder unter betriebssystemen im unix-stil muss der befehl <code>su</code> vom benutzer <code>root</code> ausgeführt werden, und unter macos ist das konto <code>root</code> aus sicherheitsgründen standardmäßig deaktiviert - unser das mac-administrator-benutzerkonto ist weniger leistungsfähig als <code>root</code> und möglicherweise nicht standardmäßig zum ausführen von <code>su</code> autorisiert. keine ahnung, egal, die problemumgehung funktioniert.</p><h2>moderne alternative: <code>sudo -u</code></h2><p>nach <a href="https://stackoverflow.com/a/23988605/642706">zu craig ringer</a> und <a href="https://stackoverflow.com/a/23982810/642706">zu erwin brandstetter</a> ist die modernere form:</p><pre class="lang-bash prettyprint-override"><code>sudo -u postgres -i </code></pre><p>laut <a href="https://www.sudo.ws/man/1.8.3/sudo.man.html">diese seite</a> :</p><ul> <li><code>-u</code> bedeutet "einen angegebenen befehl als diesen angegebenen benutzer ausführen".</li> <li><code>postgres</code> gibt den benutzernamen an, unter dem der befehl ausgeführt werden soll.</li> <li><code>-i</code> simuliert eine anfängliche anmeldung, dh die shell wird mit den startskripten des benutzers initialisiert.</li> <li>da kein befehl bereitgestellt wird, wird eine interaktive shell ausgeführt.</li> </ul><p>weitere informationen zu älteren und neueren ansätzen finden sie in dieser frage auf der schwesterseite <a href="https://serverfault.com/q/601140/142214"><em> was ist der unterschied zwischen sudo su - postgres und sudo -u postgres ?</em></a> . die antwort von craig ringer lautet: (a) vergessen sie <code>sudo su</code> und (b) verwenden sie stattdessen <code>sudo -u</code>.</p><h3>führen sie apps als <code>postgres</code> benutzer aus</h3><p>so führen sie eine <a href="https://en.wikipedia.org/wiki/postgresql#psql"><code>psql</code></a> sitzung als <code>postgres</code> benutzer aus:</p><pre class="lang-bash prettyprint-override"><code>sudo -u postgres psql </code></pre><p>so führen sie die <a href="https://en.wikipedia.org/wiki/gnu\_nano"><code>nano</code></a> <a href="https://en.wikipedia.org/wiki/text\_editor">texteditor</a> app als <code>postgres</code> benutzer aus:</p><pre class="lang-bash prettyprint-override"><code>sudo -u postgres nano </code></pre> <p>in jasmine-versionen 3.0 und höher können sie <code>withargs</code> verwenden.</p><pre><code>describe('my fn', function() { it('gets user name and id', function() { spyon(externalapi, 'get') .withargs('abc').and.returnvalue('jane') .withargs('123').and.returnvalue(98765); }); }); </code></pre><p>für jasmine-versionen vor 3.0 ist <code>callfake</code> der richtige weg, aber sie können ihn vereinfachen, indem sie ein objekt zum speichern der rückgabewerte verwenden</p><pre><code>describe('my fn', function() { var params = { 'abc': 'jane', '123': 98765 } it('gets user name and id', function() { spyon(externalapi, 'get').and.callfake(function(myparam) { return params[myparam] }); }); }); </code></pre><p>je nach jasmine-version unterscheidet sich die syntax geringfügig:</p><ul> <li>1.3.1: <code>.andcallfake(fn)</code></li> <li>2.0: <code>.and.callfake(fn)</code></li> </ul><p><strong> ressourcen: </strong></p><ul> <li> <a href="https://jasmine.github.io/api/3.0/spy.html#withargs">withargs docs</a> </li> <li> <a href="http://jasmine.github.io/edge/introduction.html#section-spies:\_%3ccode%3eand.callfake%3c/code%3e">callfake docs</a> </li> <li> <a href="https://stackoverflow.com/questions/22041745/jasmine-spyonobj-method-andcallfake-or-and-callfake">andcallfake vs and.callfake</a> </li> </ul> <p>byval- und byref-argumente sollten basierend auf den anforderungen und dem wissen über ihre funktionsweise verwendet werden <em>nicht</em> in bezug auf die geschwindigkeit.</p><p> <a href="http://www.developer.com/net/vb/article.php/3669066">http://www.developer.com/net/vb/article.php/3669066</a> </p><p><em>als antwort auf einen kommentar von slough -</em></p><p><strong> was verbraucht zur laufzeit mehr ressourcen? </strong></p><p>parameter werden an den stapel übergeben. der stapel ist sehr schnell, weil seine speicherzuordnung einfach ein zeigerinkrement ist, um einen neuen &quot;rahmen&quot; oder &quot;zuordnungssatz&quot; zu reservieren. die meisten .net-parameter überschreiten die größe eines maschinenregisters nicht so wenig, wenn zum übergeben von parametern ein stack-speicherplatz verwendet wird. tatsächlich werden basistypen und zeiger auf dem stack zugewiesen. die stapelgröße in .net ist auf 1 mb begrenzt. dies sollte ihnen einen eindruck davon vermitteln, wie wenig ressourcen durch die parameterübergabe verbraucht werden.</p><p>sie können diese artikelserie interessant finden:</p><p><em> <a href="http://www.atalasoft.com/cs/blogs/rickm/archive/2008/04/15/improving-performance-through-stack-allocation-net-memory-management-part-2.aspx">verbesserung der leistung durch stapelzuordnung (.net-speicherverwaltung: teil 2)</a> </em> </p><p><strong> was ist schneller? byval oder byref. </strong></p><p>es ist im besten fall schwierig, genau und fair zu messen - abhängig vom kontext ihrer messung, aber ein benchmark, den ich geschrieben habe, als er eine methode 100 millionen mal aufgerufen hat, kam zu folgendem ergebnis:</p><ul> <li>reference type - passed byref: 420 ms</li> <li>reference type - passed byval: 382 ms</li> <li>value type - passed byref: 421 ms</li> <li>value type - passed byval: 416 ms</li> </ul><pre><code>public sub method1(byref s as string) dim c as string = s end sub public sub method2(byval s as string) dim c as string = s end sub public sub method3(byref i as integer) dim x as integer = i end sub public sub method4(byval i as integer) dim x as integer = i end sub sub main() dim s as string = "hello world!" dim k as integer = 5 dim t as new stopwatch t.reset() t.start() for i as integer = 0 to 100000000 method1(s) next t.stop() console.writeline("reference type - byref " &amp; t.elapsedmilliseconds) t.reset() t.start() for i as integer = 0 to 100000000 method2(s) next t.stop() console.writeline("reference type - byval " &amp; t.elapsedmilliseconds) t.reset() t.start() for i as integer = 0 to 100000000 method3(i) next t.stop() console.writeline("value type - byref " &amp; t.elapsedmilliseconds) t.reset() t.start() for i as integer = 0 to 100000000 method4(i) next t.stop() console.writeline("value type - byval " &amp; t.elapsedmilliseconds) console.readkey() end sub </code></pre><p>die variable und die zuordnung in jeder methode auskommentieren -</p><ul> <li>reference type - passed byref: 389 ms</li> <li>reference type - passed byval: 349 ms</li> <li>value type - passed byref: 416 ms</li> <li>value type - passed byval: 385 ms</li> </ul><p>man könnte daraus schließen, dass das übergeben von referenztypen (zeichenfolgen, klassen) byval einige zeit spart. sie können auch sagen, dass die übergabe von werttypen (ganzzahl, byte) - byval etwas zeit spart.</p><p>wieder ist die zeit im großen system der dinge vernachlässigbar. wichtiger ist, byval und byref richtig einzusetzen und zu verstehen, was &quot;hinter den kulissen&quot; los ist. die in ihren routinen implementierten algorithmen beeinflussen die laufzeit ihres programms höchstwahrscheinlich um ein vielfaches.</p> <p>eine sache, die wir dort getan haben, wo ich arbeite, ist eine "speicher" -dienst-api. grundsätzlich post ein json-objekt an den service, und es gibt eine uuid zurück. sie senden die uuid als abfrageparameter bei jedem nachfolgenden api-aufruf und es werden die parameter/daten aus dem speicher abgerufen dies ist besonders praktisch, wenn sie mehrere anrufe mit denselben daten tätigen, da sie diese nur einmal senden müssen.</p> <p>der grund, warum sie denselben inhalt erhalten, liegt in der eigenschaft <code>#cache</code> des render-arrays, die sie beim aufrufen der methode <code>render()</code> erhalten.</p><p>insbesondere setzen sie durch aufrufen von <code>setoffset()</code> früher in ihrem code implizit einen cache-schlüssel für das render-array (siehe <a href="https://api.drupal.org/api/drupal/core%21modules%21views%21src%21viewexecutable.php/function/viewexecutable%3a%3asetoffset/8.2.x">hier</a> ):</p><blockquote> <pre><code>$this-&gt;element['#cache']['keys'][] = 'offset:' . $offset; </code></pre></blockquote><p>diese cache-schlüsseleinstellung in kombination mit der eigenschaft <code>#theme</code> des render-arrays (<code>views\_view</code>), die für beide ansichten gleich ist, führt beim einschließen zu einer zwischengespeicherten version der ersten die zweite, da die kombination [<code>#theme</code>] + [<code>#cache</code>] [schlüssel] für beide ansichten identisch ist.</p><p>um das zu umgehen, könnten sie:</p><ol> <li>entfernen sie das caching für diese ansichten vollständig, oder</li> <li>vermeiden sie die verwendung von <code>setoffset()</code> oder</li> <li> <p>fügen sie dem cache spezifischere schlüssel hinzu, sodass sie für jede ansicht einen anderen cache haben. z.b.</p> <pre><code>$variables['viewcalls']['#cache']['keys'] = array\_merge( $variables['viewcalls']['#cache']['keys'], array('viewcalls') ); ... $variables['viewappointments']['#cache']['keys'] = array\_merge( $variables['viewappointments']['#cache']['keys'], array('viewappointments') ); </code></pre></li> </ol><p>ich habe ihren fall mit der option 3. getestet und es hat wie erwartet funktioniert. bevor sie sich für die verwendung eines anderen moduls entschieden haben, können sie versuchen, an ihrem ursprünglichen code festzuhalten und die eigenschaft <code>#cache</code> beide render-arrays entsprechend zu ändern.</p><p>viel glück!</p> Wenn eine Spalte in der Datenbank nullfähig ist, hat die Ergebnisklasse auch eine nullfähige Eigenschaft. 

 Wenn beispielsweise eine `Order` eine `Datetime` Spalte mit dem Namen `CreatedDate` , sieht die resultierende `Order` Klasse in etwa wie folgt aus: 


```
public partial class Order: EntityObject
{
  ...
  private DateTime? _createdDate;
  ...
}

```
  Wenn \_ `createdDate` keinen Wert hat, wenn Sie `ObjectContext.SaveChanges()` aufrufen, wird eine `System.DBNull` automatisch als Teil des Befehls insert oder update an die Datenbank gesendet. 

 Hoffe das hilft Alex <p>sie versuchen, ein problem zu lösen, das sie gar nicht erst haben sollten: <strong>kennwortwiederverwendung</strong></p><p>das konzept ist einfach. sie denken an ein "gutes" passwort und verwenden dieses für alles. ihr bankkonto, online-shopping, ihr e-mail-anbieter usw.</p><p>das problem ist, wenn alle von ihnen durchgesickert sind, sind alle anderen konten möglicherweise in gefahr. <strong>dies ist ein völlig unnötiges risiko!</strong></p><h1>was ist mit meinem vorgeschlagenen schema?</h1><p>sie selbst sagten, erfinden sie das rad nicht neu. wenn sie das tatsächlich tun würden, müssten sie entweder eine anwendung schreiben, die die hashes für sie berechnet, oder sie selbst berechnen und speichern.</p><p>es gibt bereits anwendungen, die das problem der speicherung von anmeldeinformationen lösen, und sie machen es viel besser: offline-passwort-manager</p><h1>warum sind offline-passwort-manager besser?</h1><p>weil sie wirklich zufällige und eindeutige passwörter generieren. es besteht keine notwendigkeit, kryptographie in diese zu bringen. ich muss mein passwort für mein e-mail-konto nicht daran binden, dass es die zeichenfolge "gmail.com" enthält.</p><p>weil <code>sn2\zj2cw92dqx^{$omqac\_p'xr|md)[</code> ist definitiv ein besseres passwort als die md5-summe von <code>hunter2+gmail.com</code> (es ist <code>01f9a94a0febf268495d08f5960e7f05</code>, falls du dich gewundert hast).</p> <h2>es kommt darauf an ™</h2><p>die fähigkeit, einen oder mehrere cursor zu umgehen, hängt davon ab, was in diesem cursor ausgeführt wird. ohne zu wissen, was darin vor sich geht, gibt es keine möglichkeit zu sagen. es kann sein, dass es keine problemumgehung gibt und sie eine zeilenweise verarbeitung durchführen müssen.</p><p>nachfolgend einige beispiele.</p><h2>funktioniert nicht in sets</h2><p>dieses beispiel ist das grundlegendste und ist einfach die tatsache, dass sie ihr gesamtes dataset oder teile ihres datasets auf einmal abfragen können, aber der cursor wurde erstellt und fragt die daten zeilenweise ab. übliche, durch die dies ersetzt werden kann, sind <code>join</code>, <code>cross apply</code>/<code>outer apply</code> und andere.</p><p><strong> betrachten sie den folgenden datensatz: </strong></p><pre><code>create table dbo.lotr(lotrid int, charactername varchar(255), val varchar(255)); create table dbo.lotrattributes(lotrattributeid int, lotrid int, attrval varchar(255)); insert into dbo.lotr(lotrid,charactername,val) values(1,'frodo','ring') ,(2,'gandalf','staff'); insert into dbo.lotrattributes(lotrid,lotrattributeid,attrval) values(1,1,'ringattribute1') ,(1,2,'ringattribute2') ,(1,3,'ringattribute3') ,(2,4,'staffattribute1') ,(2,5,'staffattribute2'); </code></pre><p>man könnte versuchen, jeden datensatz zu finden, und er stimmt separat überein, indem man die tabelle <code>lotr</code> durchläuft.</p><p><strong> cursor: </strong></p><pre><code>declare @lotrid int declare c cursor for select lotrid from dbo.lotr; open c fetch next from c into @lotrid; while @@fetch\_status = 0 begin select lotrattributeid from dbo.lotrattributes where lotrid = @lotrid; fetch next from c into @lotrid; end close c deallocate c </code></pre><p><strong> das ergebnis sind zwei ergebnismengen </strong></p><pre><code>lotrattributeid 1 2 3 lotrattributeid 4 5 </code></pre><p><strong> wenn dieses <code>inner join</code> verwendet wird, erhalten wir das gleiche ergebnis wie eine ergebnismenge. </strong></p><pre><code>select lotrattributeid from dbo.lotr l inner join dbo.lotrattributes la on l.lotrid = la.lotrid; lotrattributeid 1 2 3 4 5 </code></pre><hr><h2>string-manipulation</h2><p>häufig wird <code>for xml path('')</code> verwendet, um zeichenfolgenmanipulationen innerhalb von cursorn zu ersetzen.</p><p><strong> datensatz </strong></p><pre><code>create table dbo.lotr(lotrid int, charactername varchar(255), val varchar(255)); create table dbo.lotrattributes(lotrattributeid int, lotrid int, attrval varchar(255)); insert into dbo.lotr(lotrid,charactername,val) values(1,'frodo','ring'); insert into dbo.lotrattributes(lotrid,lotrattributeid,attrval) values(1,1,'ringattribute1') ,(1,2,'ringattribute2') ,(1,3,'ringattribute3'); </code></pre><p><strong> doppelcursor mit string-manipulation </strong></p><pre><code>declare @lotrid int, @charactername varchar(255), @val varchar(255) declare @lotrattributeid int, @attrval varchar(255) declare c cursor for select lotrid,charactername, val from dbo.lotr open c fetch next from c into @lotrid,@charactername,@val while @@fetch\_status = 0 begin set @charactername +='|'+ @val declare d cursor for select lotrattributeid, attrval from dbo.lotrattributes where lotrid = @lotrid open d fetch next from d into @lotrattributeid,@attrval while @@fetch\_status = 0 begin set @charactername +='['+@attrval+ '],' fetch next from d into @lotrattributeid,@attrval end close d deallocate d fetch next from c into @lotrid,@charactername,@val end close c deallocate c select left(@charactername,len(@charactername)-1); </code></pre><p><strong> ergebnis </strong></p><pre><code>(no column name) frodo|ring[ringattribute1],[ringattribute2],[ringattribute3], </code></pre><p><strong> entfernen der cursor mit for xml path ('') </strong></p><pre><code>select l.charactername +'|'+ l.val + (select stuff((select ','+quotename(attrval) from dbo.lotrattributes la where la.lotrid = l.lotrid for xml path('')), 1, 1, '')) from dbo.lotr l; </code></pre><h2>* *</h2><p>die eigentliche problemumgehung besteht darin, herauszufinden, warum die daten auf diese weise dargestellt werden, und die anwendung zu ändern, um sie nicht in diesem format zu benötigen, und sie irgendwo zu speichern.</p><p>wenn ihre hände gebunden sind, wäre dies das nächstbeste.</p><hr><h2>fügen sie die top-10-werte in eine temporäre tabelle ein, basierend auf den ids in einer anderen tabelle</h2><p><strong> daten </strong></p><p>create table dbo.sometable (inserttableid int, val varchar (255)); create table dbo.top10table (top10tableid int, inserttableid int, val varchar (255));</p><pre><code>insert into dbo.sometable(inserttableid,val) values(1,'bla') ,(2,'blabla'); insert into dbo.top10table(top10tableid,inserttableid,val) values(1,1,'wuw') ,(2,1,'wuw') ,(3,1,'wuw'); </code></pre><p><strong> cursor </strong></p><pre><code>create table #top10values(top10tableid int, inserttableid int, val varchar(255)) declare @inserttableid int; declare c cursor for select inserttableid from dbo.sometable; open c fetch next from c into @inserttableid; while @@fetch\_status =0 begin insert into #top10values(top10tableid,inserttableid,val) select top(10) top10tableid,inserttableid,val from dbo.top10table where inserttableid = @inserttableid order by top10tableid fetch next from c into @inserttableid; end close c deallocate c select * from #top10values; drop table #top10values; </code></pre><p><strong> ergebnis </strong></p><pre><code>top10tableid inserttableid val 1 1 wuw 2 1 wuw 3 1 wuw </code></pre><p><strong> ersetzen des cursors durch <code>cross apply</code> und ein <code>cte</code> </strong></p><pre><code>create table #top10values(top10tableid int, inserttableid int, val varchar(255)); ;with cte as ( select inserttableid from dbo.sometable ) insert into #top10values(top10tableid,inserttableid,val) select t1t.top10tableid,t1t.inserttableid,t1t.val from cte cross apply (select top (10) top10tableid,inserttableid,val from dbo.top10table t1t where t1t.inserttableid = cte.inserttableid ) t1t ; select * from #top10values; drop table #top10values; </code></pre><hr><h2>andere beispiele</h2><ul> <li>ein beispiel zum ersetzen eines cursors zur auswahl eines dynamischen satzes von artikeln pro lieferant mithilfe von <code>cross apply</code> <a href="https://dba.stackexchange.com/questions/100761/replacing-a-cursor-with-a-single-query-possible">hier</a> .</li> <li>ein beispiel für die verwendung von fensterfunktionen zum ersetzen eines cursors <a href="https://dba.stackexchange.com/questions/127083/improve-a-query-using-cursor-to-set-based-approach">hier</a> .</li> </ul><hr><p><strong> manchmal gibt es keine andere wahl </strong></p><p>wenn sie nicht in sätzen arbeiten können und zeilenweise arbeiten müssen, können sie den cursor trotzdem optimieren.</p><p>eine der größten änderungen bei der beschleunigung des cursors ist das hinzufügen von <code>local fast\_forward</code>.</p><pre><code>declare c cursor local fast\_forward for select lotrid from dbo.lotr </code></pre><p>schauen sie sich <a href="https://sqlperformance.com/2012/09/t-sql-queries/cursor-options">diesen blogpost</a> von @aaronbertrand an, wo er die möglichen leistungsunterschiede erklärt, wenn cursoreinstellungen wie <code>local</code> &amp; <code>fast\_forward</code> verwenden oder nicht.</p> <p>die standardmethode für die angabe des objekttyps ist entweder <a href="http://search.cpan.org/perldoc?perlfunc#ref"><code>ref</code></a> oder <a href="http://search.cpan.org/perldoc?scalar::util"><code>scalar::util</code></a> <code>::blessed</code>. wenn sie<em> wissen </em>das objekt gesegnet ist, geben sie dieselbe information zurück. </p><pre><code>my $class1 = blessed( $obj ); my $class2 = ref $obj; </code></pre><p><code>ref</code> gibt jedoch auch 'hash' für nicht gesegnete hashes zurück, während <code>blessed</code> dieses spiel ablehnt. </p><p>für eine liste von methoden ist es für den gesegneten zeigerstil eines perl-objekts einfach, eine davon selbst zu codieren. der folgende code funktioniert ziemlich gut für mich. es gibt die namen der funktionen (diejenigen, die den &quot;code-slot&quot; des angegebenen namens annehmen) zurück, die dem paket zugeordnet sind, das sie definiert. </p><pre><code>sub class\_methods { use class::isa; my $obj = shift; return unless ref( $obj ); my %meth\_names; foreach my $anc ( class::isa::self\_and\_super\_path( ref $obj ), 'universal' ) { my $stash = \%{"$anc\::"}; my @funcs = grep { m/^[\_\p{alpha}]/ # begins with \_ or alpha &amp;&amp; !exists $meth\_names{$\_} # no clobbering &amp;&amp; defined *{$stash-&gt;{$\_}}{code} # has a filled code slot } keys %$stash ; # assign to the "hash slice", keyed by all the entries in @funcs # the value of $anc repeated as many times as elements in @funcs. @meth\_names{@funcs} = ( $anc ) x @funcs; } return %meth\_names; } </code></pre><ul> <li><p>dies funktioniert auch für relativ komplexe objekte, aber wenn das besitzende paket eine menge generierten codes enthält, ist es nicht besonders hilfreich zu wissen, in welchem ​​paket die generatoren den code-pointer stecken. es wird mehr bedeuten, welches paket zu finden ist generiert den code. </p></li> <li><p>ist dies der fall, erhalten sie<em> might </em>den code aus der ausführung ihres codes, einschließlich <a href="http://search.cpan.org/perldoc?data::dumper"><code>data::dumper</code></a> und setzen von <code>$data::dumper::deparse</code> auf 1 wie folgt: (<code>local $data::dumper::deparse = 1;</code>) und dann den code-zeiger wie folgt: <code>say dumper( $code\_ref );</code></p></li> <li><p>es<em> won't </em>funktioniert für gültige methoden, die noch mit einer beliebigen <code>autoload</code>-methode erstellt werden müssen. wenn sie diese in der liste sehen, macht das objekt möglicherweise mehr, aber was es alles tut, wissen sie nicht.</p></li> <li><p>die &quot;basisklasse&quot; <code>universal</code> ist enthalten, da diese klasse verhalten enthält, das vom objekt verwendet werden kann. </p></li> </ul><p>viel glück.</p> <p>in ordnung. diese ausnahme wird angezeigt, wenn ihre suche die empfehlung zurückgibt und sie festlegen, dass die empfehlung ignoriert wird.</p><p>[verweis: wenn sie in ad suchen und ad glaubt, dass weitere informationen an einem anderen ort verfügbar sind, wird ein verweis [ort, an dem sie weitere informationen finden] zusammen mit ihren suchergebnissen zurückgegeben.]</p><p>sie können diese ausnahme vermeiden, indem sie <code>context.referral</code> bis <code>follow</code>. dann würde es auch in der überweisung suchen [deshalb dauert es mehr zeit, um das ergebnis zurückzugeben].</p><p>aber in meinem fall ist die überweisung ungültig und hat eine weitere ausnahme ergeben.</p><p>ich habe dieses problem behoben, indem ich die basedn (suchbasis) genauer geändert habe. z.b. <code>ou=users,dc=mydomain,dc=com</code>. jetzt wird diese ausnahme nicht angezeigt, da keine überweisung zurückgegeben wird.</p>