<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ivan Vydrin</title>
    <description>The latest articles on DEV Community by Ivan Vydrin (@ivanvyd).</description>
    <link>https://dev.to/ivanvyd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4234467%2Fc93ad182-4c9a-43ed-960c-acaa15ff105f.png</url>
      <title>DEV Community: Ivan Vydrin</title>
      <link>https://dev.to/ivanvyd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ivanvyd"/>
    <language>en</language>
    <item>
      <title>Resolve tenant membership before a Databricks SQL call</title>
      <dc:creator>Ivan Vydrin</dc:creator>
      <pubDate>Mon, 09 Nov 2026 05:53:19 +0000</pubDate>
      <link>https://dev.to/ivanvyd/resolve-tenant-membership-before-a-databricks-sql-call-49gi</link>
      <guid>https://dev.to/ivanvyd/resolve-tenant-membership-before-a-databricks-sql-call-49gi</guid>
      <description>&lt;p&gt;A tenant ID in a route identifies the tenant the caller requested. Before you run SQL, check the authenticated caller against your membership store.&lt;/p&gt;

&lt;p&gt;I maintain LakeWright.NET, an independent open-source project. This example uses its published 3.0.0 Core and Databricks packages in a .NET 8 console app. It checks membership, creates a tenant context for an allowed request, and runs a parameterized query. You can keep your own identity provider and membership store. The example runs without Signalboard or PostgreSQL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ivanvyd/LakeWright.NET" rel="noopener noreferrer"&gt;View LakeWright.NET on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run an allowed query and a denied request
&lt;/h2&gt;

&lt;p&gt;Install the &lt;a href="https://dotnet.microsoft.com/download/dotnet/8.0" rel="noopener noreferrer"&gt;.NET 8 SDK&lt;/a&gt;. Download the &lt;a href="https://compiletheory.com/library-guides/lakewright/lakewright-demo.zip" rel="noopener noreferrer"&gt;companion project&lt;/a&gt; and extract it. The &lt;a href="https://github.com/ivanvyd/LakeWright.NET/tree/3cf647ac9fa5780f9d466b0034fae8d21f17cf09/examples/package-quickstart" rel="noopener noreferrer"&gt;source directory&lt;/a&gt; is pinned to the tested revision. Run from the extracted project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet restore --configfile NuGet.Config
dotnet run -c Release -- --offline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALLOW: member-alice -&amp;gt; Acme; tenant-scoped query returned 1.
DENY: member-alice -&amp;gt; Globex; membership denied; no statement sent; transport requests remain 1.
OFFLINE_OK: published 3.0.0; no workspace credentials or external network calls.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restore downloads packages. The executable then talks only to its own loopback HTTP fixture. It checks the allowed request's catalog, schema, SQL and bound value. Alice's request for Globex returns no context, so the example stops before constructing a statement. The transport count stays at one.&lt;/p&gt;

&lt;p&gt;This is a console fixture. It does not host an ASP.NET endpoint or capture an HTTP &lt;code&gt;404&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=S38h1e6Ljvg" rel="noopener noreferrer"&gt;Watch the LakeWright demo on YouTube&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolve membership before constructing the statement
&lt;/h2&gt;

&lt;p&gt;The relevant part of the compiled example is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ResolveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MembershipResolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Acme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PrincipalId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The fixture member was not resolved."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;TenantScopedStatement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"SELECT :value AS value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;StatementParameter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resolver&lt;/code&gt; and &lt;code&gt;executor&lt;/code&gt; come from dependency injection. &lt;code&gt;MembershipResolver.Acme&lt;/code&gt; and &lt;code&gt;PrincipalId&lt;/code&gt; are demonstration identities defined in the project. The resolver checks a server-owned &lt;code&gt;(tenant, principal)&lt;/code&gt; mapping before calling &lt;code&gt;ITenantContextFactory&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In your application, replace that mapping with your trusted membership lookup. Obtain the principal from the authenticated server identity. Load the catalog and schema from stored tenant configuration. A caller-supplied tenant ID or principal string cannot establish membership.&lt;/p&gt;

&lt;p&gt;Register your resolver with &lt;code&gt;AddLakeWrightTenancy&amp;lt;YourMembershipResolver&amp;gt;()&lt;/code&gt;. Keep the context factory inside that trusted application layer. An endpoint should ask the resolver for a context and stop if it receives &lt;code&gt;null&lt;/code&gt;. The guide uses a &lt;code&gt;404&lt;/code&gt; policy for that case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the SQL in trusted application code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;StatementParameter.Int("value", 1)&lt;/code&gt; binds a value. The SQL text stays constant. Use parameters for values rather than building SQL by concatenation.&lt;/p&gt;

&lt;p&gt;A tenant context sets the &lt;a href="https://docs.databricks.com/api/statement-execution/v1/execute-statement" rel="noopener noreferrer"&gt;default catalog and schema&lt;/a&gt; for the statement. SQL can still name another catalog or schema explicitly. Use Databricks grants to enforce the storage boundary, and keep caller-supplied or Genie-generated SQL out of this execution path.&lt;/p&gt;

&lt;p&gt;The fixture tests the application's membership boundary and one serialized query. Separate server-side tenant schemas, exports and stored statement-ID ownership need their own checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to a workspace after the local check
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/ivanvyd/LakeWright.NET/blob/845f1c3616676e573ae747edfe0844b5d535a61c/docs/guides/add-to-existing-app.md" rel="noopener noreferrer"&gt;existing-application guide&lt;/a&gt; lists warehouse access, catalog/schema permissions and credential choices. The provided executable has no live switch. Adapting it for your application means replacing the fixture transport and membership data after those prerequisites are available.&lt;/p&gt;

&lt;p&gt;On 7 November 2026, a separate installed 3.0.0 .NET 8 consumer passed a bounded check in a nonproduction AWS workspace. A custom &lt;code&gt;TokenCredential&lt;/code&gt; adapter forwarded the selected Databricks CLI user's OAuth token. The bound query returned &lt;code&gt;1&lt;/code&gt;. Wrong-tenant membership denial and default-statement rejection added zero SQL calls.&lt;/p&gt;

&lt;p&gt;That check did not exercise Azure managed identity, business tables, billing or jobs. Its sample catalog/schema did not establish isolation between separate server-side tenant schemas. The &lt;a href="https://github.com/ivanvyd/LakeWright.NET/blob/845f1c3616676e573ae747edfe0844b5d535a61c/docs/compatibility.md" rel="noopener noreferrer"&gt;compatibility record&lt;/a&gt; records the tested path.&lt;/p&gt;

&lt;h2&gt;
  
  
  When setup fails
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The application asks for &lt;code&gt;--offline&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;This companion deliberately has no live mode. Use the guide when adapting it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The allowed fixture request fails&lt;/td&gt;
&lt;td&gt;Run from the complete extracted project, including its local build and NuGet configuration.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A real caller cannot resolve membership&lt;/td&gt;
&lt;td&gt;Check the authenticated principal and trusted membership mapping before changing SQL or grants.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retargeting to .NET 10 produces &lt;code&gt;NU1605&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Align the direct Configuration and DependencyInjection package pins with the tested .NET 10 adaptation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both .NET 8 and .NET 10 consumers passed the offline allow/deny fixture. The companion README includes the .NET 10 adaptation; it keeps LakeWright at 3.0.0 and aligns the direct Microsoft.Extensions dependencies.&lt;/p&gt;

&lt;p&gt;If you also use LakeWright's durable-operation modules, read &lt;a href="https://github.com/ivanvyd/LakeWright.NET/blob/845f1c3616676e573ae747edfe0844b5d535a61c/docs/decisions/0033-operation-admission-and-deletion-liveness.md" rel="noopener noreferrer"&gt;ADR 0033&lt;/a&gt; before upgrading to 3.0.0. Admission rejects caller-owned RepeatableRead and Serializable transactions. Invoke &lt;code&gt;ClaimNextAsync&lt;/code&gt; outside that caller transaction, or use ReadCommitted. Commit a returned claim before submitting remote work. After the deletion grace period, purge may discard unclaimed Pending entries that were never submitted. Other incomplete work still blocks purge. The Core/Databricks console example does not exercise those optional flows.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>databricks</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Call Databricks Genie from C# with LakeSpeak</title>
      <dc:creator>Ivan Vydrin</dc:creator>
      <pubDate>Mon, 09 Nov 2026 05:52:28 +0000</pubDate>
      <link>https://dev.to/ivanvyd/call-databricks-genie-from-c-with-lakespeak-3ahh</link>
      <guid>https://dev.to/ivanvyd/call-databricks-genie-from-c-with-lakespeak-3ahh</guid>
      <description>&lt;p&gt;A Genie question takes more than one API call. Your app starts a conversation, waits for Genie to finish, then reads the answer, SQL, or rows it returns.&lt;/p&gt;

&lt;p&gt;I maintain LakeSpeak.NET, an independent open-source project. In this walkthrough, a .NET 8 console app uses &lt;code&gt;LakeSpeak.Genie&lt;/code&gt; 0.4.0. Run it against a local HTTP fixture first, then use the same client with a Genie Agent you can access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ivanvyd/LakeSpeak.NET" rel="noopener noreferrer"&gt;View LakeSpeak.NET on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the example before configuring a workspace
&lt;/h2&gt;

&lt;p&gt;Install the &lt;a href="https://dotnet.microsoft.com/download/dotnet/8.0" rel="noopener noreferrer"&gt;.NET 8 SDK&lt;/a&gt;. The first run needs NuGet access. It does not need a Databricks account or the LakeSpeak CLI.&lt;/p&gt;

&lt;p&gt;Download the &lt;a href="https://compiletheory.com/library-guides/lakespeak/lakespeak-demo.zip" rel="noopener noreferrer"&gt;companion project&lt;/a&gt; and extract it. The &lt;a href="https://github.com/ivanvyd/LakeSpeak.NET/tree/c275383b058dffde3b925a5cd6a902686d89726f/examples/package-quickstart" rel="noopener noreferrer"&gt;source directory&lt;/a&gt; is pinned to the tested revision. Run these commands inside the extracted project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet restore --locked-mode --source https://api.nuget.org/v3/index.json
dotnet build -c Release --no-restore
dotnet run -c Release --no-build --no-restore -- --fixture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OFFLINE HTTP fixture: no workspace, credentials, or Databricks CLI calls.
There were 42 orders yesterday.
SQL: SELECT count(*) AS order_count FROM orders WHERE order_date = current_date() - 1
order_count
42
OFFLINE fixture PASS (published LakeSpeak.Genie 0.4.0).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FixtureHandler&lt;/code&gt; supplies the conversation and query-result responses in memory. The installed client parses those responses. The companion checks the returned SQL and one complete row containing &lt;code&gt;42&lt;/code&gt;. The SQL shown here is fixture data. This command does not execute it against a warehouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=-t_S79iC5Vs" rel="noopener noreferrer"&gt;Watch the LakeSpeak demo on YouTube&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Register the client in your application
&lt;/h2&gt;

&lt;p&gt;The live path uses these registrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ServiceCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AddDatabricksCliAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profileName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AddLakeSpeak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profileName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BuildServiceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;genie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetRequiredService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IGenieClient&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;genie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AskAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete project includes the &lt;code&gt;using&lt;/code&gt; directives and argument handling. In a hosted application, use your existing dependency-injection container and supply the cancellation token from the request or background task.&lt;/p&gt;

&lt;p&gt;The response can contain answer text, generated SQL and a tabular result. Genie can also ask for clarification. Check whether a result exists before using its rows. The example prints an incomplete-result warning and exits with code &lt;code&gt;1&lt;/code&gt; if the result is truncated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask an existing Agent
&lt;/h2&gt;

&lt;p&gt;For a live question, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://docs.databricks.com/aws/en/dev-tools/cli/install" rel="noopener noreferrer"&gt;Databricks CLI&lt;/a&gt; available to the process.&lt;/li&gt;
&lt;li&gt;An existing Genie Agent shared with your identity.&lt;/li&gt;
&lt;li&gt;Permission to use the Agent's warehouse and underlying data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copy the Agent's ID from Databricks. This console example does not resolve display names. Replace &lt;code&gt;YOUR_WORKSPACE_HOST&lt;/code&gt; and &lt;code&gt;YOUR_AGENT_ID&lt;/code&gt; before running these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;databricks auth login --host &lt;span class="s2"&gt;"https://YOUR_WORKSPACE_HOST"&lt;/span&gt; --profile my-workspace
dotnet run -c Release --no-build --no-restore -- my-workspace &lt;span class="s2"&gt;"YOUR_AGENT_ID"&lt;/span&gt; &lt;span class="s2"&gt;"How many orders were placed yesterday?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selected CLI profile owns login and token refresh. Check &lt;code&gt;DATABRICKS_HOST&lt;/code&gt; too: an existing environment value can override the profile's host. The &lt;a href="https://github.com/ivanvyd/LakeSpeak.NET/blob/89fc345147e62d74124f7990824c8bbd45f3e1cb/docs/authentication.md" rel="noopener noreferrer"&gt;authentication guide&lt;/a&gt; documents that precedence and the OAuth M2M path for unattended applications.&lt;/p&gt;

&lt;p&gt;A live question creates conversation history and can use warehouse compute. Inspect the returned SQL and compare the answer with a result you understand before relying on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  When setup fails
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The process cannot find &lt;code&gt;databricks&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Confirm the CLI is installed and on the PATH inherited by the application.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The request reaches the wrong workspace&lt;/td&gt;
&lt;td&gt;Check the selected profile and any &lt;code&gt;DATABRICKS_HOST&lt;/code&gt; override.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Agent is not found or access is denied&lt;/td&gt;
&lt;td&gt;Check that you passed an ID, then check Agent sharing, warehouse access and data permissions for that identity.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The response has text but no rows&lt;/td&gt;
&lt;td&gt;Read the response. It may be a clarification rather than a completed query result.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The .NET SDK cannot be selected&lt;/td&gt;
&lt;td&gt;The companion pins a .NET 8 SDK. Use its README's tested .NET 10 adaptation if you are working on .NET 10.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What the tests covered
&lt;/h2&gt;

&lt;p&gt;The published 0.4.0 client passed this offline fixture on .NET 8 and .NET 10. A separate installed .NET 8 consumer also completed one live synthetic question in a nonproduction AWS workspace. Its existing starter Agent generated &lt;code&gt;SELECT 1 AS probe_value&lt;/code&gt; and returned one complete row containing &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/ivanvyd/LakeSpeak.NET/actions/runs/42409476224" rel="noopener noreferrer"&gt;current-source .NET 10 M2M suite&lt;/a&gt; passed all nine live tests on 7 November 2026. That suite covers questions, follow-up, re-execution, feedback and client cancellation. It is separate from the installed-package check. Client cancellation does not establish that remote warehouse work stopped.&lt;/p&gt;

&lt;p&gt;These checks do not establish business-answer accuracy or GCP compatibility. The &lt;a href="https://github.com/ivanvyd/LakeSpeak.NET/blob/89fc345147e62d74124f7990824c8bbd45f3e1cb/docs/compatibility.md" rel="noopener noreferrer"&gt;compatibility record&lt;/a&gt; keeps the dated checks and their limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the client or the CLI
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.databricks.com/aws/en/dev-tools/cli/reference/genie-commands" rel="noopener noreferrer"&gt;official Databricks CLI&lt;/a&gt; already provides Genie terminal commands. Use the .NET client when the conversation belongs inside your C# application. Databricks also provides &lt;a href="https://docs.databricks.com/aws/en/genie-agents/monitor" rel="noopener noreferrer"&gt;native benchmark evaluations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;LakeSpeak's separate Question Pack CLI supports versioned questions and reviewable reports. In 0.4.0, &lt;code&gt;behavior.requireQueryResult: true&lt;/code&gt; makes a pack reject missing or truncated results. A complete zero-row result succeeds. The default is &lt;code&gt;false&lt;/code&gt;; this setting does not judge whether an answer is correct.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>databricks</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
