Was sind die Hauptkriterien bei der Auswahl zwischen verschiedenen ORMs?




 In meinem neuen Projekt bin ich sehr verwirrt, welche ORMs zu verwenden sind. Unsere Datenbankabfragen sind sehr komplex. Wir sind hauptsächlich zwischen Dapper und EF 5. Ich weiß, Dapper hat Leistungsvorteile und ich denke, EF hat seine Vorteile bei der Wartung und Codegenerierung. 

 Glauben Sie mir, ich suche viel, um die Hauptkriterien zu finden, um eine Wahl zu treffen, und einen guten Vergleich zwischen diesen beiden, aber finde keine befriedigende Antwort. 

 Also welche Dinge muss ich dabei beachten.
Es lohnt sich, einen Blick auf diesen [`Blog Post`](http://lostechies.com/jimmybogard/2012/07/20/choosing-an-orm-strategy/) werfen, aber letztendlich werden die Kriterien von Ihnen festgelegt. Sie müssen entscheiden, ob 

* schnelle Entwicklung
* Performance
* Code-zuerst
* DB zuerst

 oder was auch immer Sie suchen und priorisieren Sie die Liste basierend auf, was noch wichtiger ist. Dann verwenden Sie diese Liste, um Ihre Forschung zusammenzustellen.
<p>sie können ihre aufgabe mit <code>backgroundworker</code> erledigen. es ist eine bekannte klasse, und viele leute haben sie benutzt.</p><p>die neuen schlüsselwörter c # 5 <code>async</code> und <code>await</code> erleichtern im grunde nur das schreiben von lesbarem asynchronem code. möglicherweise gibt es weniger tutorials und beispiele, wie verschiedene aufgaben mit diesen schlüsselwörtern ausgeführt werden können, als <code>backgroundworker</code>.</p><p>sofern sie keine ältere version von c # verwenden müssen, empfehle ich, die verwendung von <code>async</code> und <code>await</code> zu erlernen.</p> <p>ich würde mich zur tabs-lösung beugen. im moment verwenden sie viel vertikalen raum, und wenn zwei listen übereinander liegen, ist die anzeige schwierig. stellen sie sich vor, sie haben diese beiden toolboxen aus ihrem beispiel übereinander.</p><p>der ansatz mit registerkarten spart platz und ermöglicht es ihnen, einige coole dinge zu erledigen, z. b. kleine hinweise im apple-stil auf der registerkarte, die die anzahl neuer oder wichtiger elemente in dieser registerkarte anzeigen. sie müssen nicht einmal per se "tabulatoren" sein - nur schaltflächen, die den status des bedienfelds unter ihnen umschalten.</p> <p>es gibt keine einfache antwort auf ihre frage, aber hier sind einige dinge, über die sie nachdenken sollten.</p><p>erstens ist die skalierung nicht das einzige, worüber man sich sorgen machen muss. was sie mit ihren daten machen, ist. wenn sie 500 tabellen mit 30 tb daten) haben und einfach oltp mit sehr wenig berichterstellung) arbeiten, werden sie wahrscheinlich nicht zu viele probleme haben es gibt 32-tb-datenbanken auf postgresql. gleichzeitig wird sich die leistung jedoch etwas verschlechtern, da die festplatte auf alles übertragen werden muss. wenn sie über 50 tb daten verfügen, aber häufig einen treffer von etwa 100 gb haben, können sie dies auch erstellen sie einen server mit genügend ram, um diesen teil der datenbank im speicher zu halten, und sie sind golden.</p><p>wenn sie jedoch versuchen, den modus (häufigster wert) aus 1 tb daten zu entfernen, spielt es keine rolle, welches system sie verwenden. dies wird <em> schmerzhaft sein </em> mit oder ohne scherben. (bearbeiten: sharding kann dieses problem <em> tatsächlich verschlimmern. </em>)</p><p>die hauptprobleme, auf die sie mit riesigen datenbanken unter mysql und postgresql stoßen werden, sind die tatsache, dass keine der beiden parallelen zwischen abfragen unterstützt. mit anderen worten, eine abfrage wird von einem einzelnen thread als einzelner block ausgeführt und kann nicht in teile zerlegt und separat ausgeführt werden. dies ist meistens ein problem, wenn große analytische abfragen über große datenmengen ausgeführt werden. hier kommen postgres-xc und green plum zur rettung, da sie speicher von ausführung trennen und dies auf koordinatorebene tun können. beachten sie, dass postgres-xc und green plum im wesentlichen intern sharding verwenden, die koordinatoren jedoch die gesamte konsistenz global erzwingen.</p><p>mit intraquery-parallelität können sie die abfrage aufteilen, verschiedene prozessoren/festplatten-e/a-kanäle teile davon ausführen lassen und teile der ergebnismenge zurückmelden, die zusammengestellt und an die anwendung zurückgegeben werden sollen. auch dies ist normalerweise am hilfreichsten bei analytischen und nicht bei transaktionsverarbeitungslasten.</p><p>die zweite sache ist, dass einige systeme wie vertica oder greenplum informationsspalten zusammen speichern. dies erschwert die verwendung des systems aus der perspektive von oltp] und verringert die leistung dort, erhöht jedoch die leistung für große analytische workloads drastisch. dies ist also ein workload-spezifischer kompromiss.</p><p>die antwort ist also, dass sie <em>, sobald sie über 1-2 tb in der größe) </em> stehen, möglicherweise mit einer zahl konfrontiert werden dies ist wiederum spezifisch für datenbanken, größe der arbeitssätze usw. an diesem punkt müssen sie sich jedoch wirklich für schneeflockensysteme entscheiden, dh solche, die einzigartig und auf ihre arbeitslast zugeschnitten sind.</p><p>dies bedeutet natürlich, dass die grenzwerte im allgemeinen nicht quantifizierbar sind.</p><p><strong>bearbeiten</strong>: ich habe jetzt mit einer 9-tb-datenbank gearbeitet, die eine mischung aus entscheidungsunterstützung und transaktionsverarbeitungs-workloads in postgresql verarbeitet. die größte herausforderung besteht darin, dass sie bei fragen, die große teile des datensatzes betreffen, eine weile auf die antwort warten müssen.</p><p>bei sorgfältiger beachtung der grundlagen (einschließlich indizes, autovakuum, funktionsweise auf niedrigem niveau usw.) und ausreichender rechenressourcen sind diese jedoch vollständig verwaltbar (und ich schätze, dass sie bis weit in den 30-tb-bereich in pg verwaltbar sind).</p><p><strong>edit2</strong>: sobald sie 100 tb erreicht haben, hängt es von ihrem datensatz ab, was funktioniert. ich arbeite gerade an einem, der nicht in diesen bereich skaliert, da er zuerst das limit von 32 tb pro tabelle in postgresql erreicht.</p> <p>da ich magento noch nie benutzt habe, würde ich sagen, geh mit drupal + drupal commerce ;-)</p><p>lesen sie jedoch die folgenden artikel:</p><ul> <li> <p> <a href="http://www.richardcarterconsultancy.com/2012/07/magento-vs-drupal-commerce/">magento vs drupal commerce</a> </p> </li> <li> <p> <a href="http://www.quora.com/e-commerce/which-cms-is-better-for-e-commerce-magento-vs-drupal-others">e-commerce: welches cms ist besser für e-commerce? magento vs. drupal? andere? (quora)</a> </p> </li> <li> <p> <a href="http://www.ehow.com/info\_12184137\_drupal-vs-magento.html">drupal gegen magento (ehow)</a> </p> </li> <li> <p> <a href="http://drupal.org/node/710466">magento vs drupal commerce (drupal.org)</a> </p> </li> <li> <p> <a href="http://lmgtfy.com/?q=drupal%20commerce%20vs%20magento">google; -)</a> </p> </li> </ul><p>mit drupal können sie produkte mit einer kombination aus <a href="http://www.drupal.org/project/feeds">feeds</a> und <a href="http://drupal.org/project/commerce\_feeds">commerce feeds</a> oder <a href="http://drupal.org/project/commerce\_migrate">commerce migrate</a> importieren (letzteres erfordert etwas mehr arbeit).</p><p>ich denke, am ende des tages kommt es auf ihre persönlichen bedürfnisse an. wenn sie kein voll funktionsfähiges cms (oder cmf ... <a href="http://palantir.net/blog/drupal-not-cms">entscheiden sie sich selbst</a> ) benötigen, ist der teufel, den sie kennen, besser. könnte genauso gut mit magento gehen.</p><p>wenn sie eine vollständig integrierte lösung wünschen, ist drupal eindeutig der gewinner. ich bin mir ziemlich sicher, dass es ein paar entwicklerhäuser gibt, in denen ich websites von magento nach drupal commerce migriere, aber ich habe noch nie von jemandem gehört, der es umgekehrt macht.</p><p>abhängig von der komplexität ihres projekts müssen sie sich möglicherweise auch mit drupal die hände schmutzig machen, aber es ist nicht so schwer :).</p> Am besten einen Vergleich machen. Speziell auf Senioren zugeschnittene Browser gibt es nicht. Alle Browser sind aber absolut seniorentauglich. Wüsstest Du mein Alter, wüsstest Du, ich weiß, wovon ich schreibe.


In der Urfassung sind der Firefox, Google Chrome und der IE schnell in den Griff zu bekommen. Und nur darum geht es ja, denn Senioren sind geistig nicht weniger gut drauf, als Mitvierziger oder Teenager. Was die Browser unübersichtlich machen kann, sind nur viele selbst installierte Erweiterungen.


Den IE empfehle ich nicht, weil Microsoft bekannt gewordene Sicherheitslücken erst sehr spät schließt. Also zu viel Risiko.


**Erste Wahl bleiben Firefox und Google Chrome. Da ist es sicher am cleversten, Du installierst erst einmal beide und schaust dann, mit welchem Du am besten zurecht kommst. Den wählst Du dann zum Standardbrowser.** <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> <ul> <li> <p><strong>wählen sie option 1, wenn</strong> die site enthält 'einfache' daten. zum beispiel einfacher text, ein kommentar oder etwas öffentliches</p> </li> <li> <p><strong>wählen sie option 3, wenn</strong> sie möchten nicht, dass der benutzer seitenelemente versehentlich ändert. zum beispiel würde ich in einem buchhaltungsprogramm definitiv option 3 verwenden, aber in einer einfachen notizblock-app (insbesondere auf mobilgeräten) würde ich definitiv option 1 wählen.</p> </li> </ul> <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>sie sollten sich mit ziemlicher sicherheit <a href="http://clojure.org/">clojure</a> ansehen - meiner meinung nach ist es die beste moderne sprache für multi-core-programmierung und äußerst produktiv.</p><p>hauptmerkmale:</p><ul> <li>es ist eine <strong> funktionale sprache </strong>, die sowohl für die parallelität als auch für ihre fähigkeit, sich mit höheren abstraktionen zu entwickeln, von vorteil ist. es verfügt über vollständig unveränderliche persistente datenstrukturen und verzögerte sequenzen, die jedem bekannt sind, der erfahrung mit funktionalen sprachen wie haskell hat.</li> <li>es verfügt über ein sehr neuartiges <strong> software-transaktionsspeichersystem </strong> für den gleichzeitigen sperrenfreien zugriff auf den veränderlichen zustand. das gleichzeitige sicherstellen von code ist oft so einfach wie das einschließen in einen (dosync ....) block.</li> <li>es ist ein <strong> [~ # ~] lisp [~ # ~] </strong> - was es extrem leistungsfähig für die makrobasierte metaprogrammierung und codegenerierung macht. dies kann signifikante <a href="http://www.paulgraham.com/avg.html">produktivitätsvorteile</a> bringen (aufsatz von paul graham - "beating the averages")</li> <li>es ist eine <strong> jvm-sprache </strong> - so erhalten sie nicht nur zugriff auf die große auswahl an bibliotheken und tools in java ökosystem profitieren sie auch von dem enormen technischen aufwand, der dazu beigetragen hat, die jvm zu einer effektiven plattform für gleichzeitige serverseitige anwendungen zu machen. aus praktischen gründen bietet dies einen großen vorteil gegenüber sprachen, die nicht über diese grundlage verfügen auf.</li> <li>es ist <strong> dynamisch </strong> - was zu sehr präzisem code und viel produktivität führt. beachten sie jedoch, dass sie bei bedarf optionale statische typhinweise für die leistung verwenden können.</li> <li>die sprache <strong> basiert auf abstraktionen </strong>, was etwas schwer zu erklären ist, aber der nettoeffekt besteht darin, dass sie eine reihe relativ orthogonaler merkmale erhalten, die sie kombinieren können, um sie zu lösen deine probleme. ein beispiel wäre die sequenzabstraktion, mit der sie code schreiben können, der sich mit jedem "sequentiellen" objekttyp befasst (der alles aus listen, zeichenfolgen, java arrays, unendlich faulen sequenzen, zeilen) enthält aus einer datei gelesen werden etc.)</li> <li>es gibt eine <strong> großartige community </strong> - hilfreich, aufschlussreich, aber vor allem sehr pragmatisch - der fokus in clojure liegt im allgemeinen auf "dinge erledigen".</li> </ul><p>einige mini-codebeispiele mit einer nebenläufigkeit:</p><pre><code>;; define and launch a future to execute do-something in another thread (def a (future (do-something))) ;; wait for the future to finish and print its return value (println @a) ;; call two functions protected in a single stm transaction (dosync (function-one) (function-two)) </code></pre><p>insbesondere lohnt es sich, eines oder mehrere dieser videos anzusehen:</p><ul> <li> <a href="http://www.infoq.com/presentations/value-identity-state-rich-hickey">http: //www.infoq.com/presentations/value-identity-state-rich-hickey</a> </li> <li> <a href="http://www.infoq.com/presentations/are-we-there-yet-rich-hickey">http: //www.infoq.com/presentations/are-we-there-yet-rich-hickey</a> </li> <li> <a href="http://www.infoq.com/presentations/clojure-the-art-of-abstraction">http: //www.infoq.com/presentations/clojure-the-art-of-abstraction</a> </li> </ul>