How to query Kusto (Azure data explorer) in C# and get strong type result?
Install Kusto client first.
<PackageReference Include="Microsoft.Azure.Kusto.Data" Version="9.2.0" />
And build an abstract class as a Kusto response row.
public abstract class KustoResponseRow
{
public void SetPropertiesFromReader(IDataReader reader)
{
foreach (var property in this.GetType().GetProperties())
{
if (property.SetMethod != null)
{
property.SetValue(this, reader[property.Name]);
}
}
}
}
Then you can create a new class named "KustoRepository".
Build a new KustoClient in its constructor. You'd better read the appId and appkey from configuration.
To get your app Id and app Key, you need to register it at Azure AD and allow it to access your Kusto (Azure data explorer) client.
this.kustoClient = KustoClientFactory.CreateCslQueryProvider(new KustoConnectionStringBuilder
{
DataSource = "https://someinstance.westus.kusto.windows.net/somedatabase",
ApplicationClientId = "appId",
ApplicationKey = "appKey",
Authority = "tennat-id",
FederatedSecurity = true
});
And build your query function:
public List<T> QueryKQL<T>(string query) where T : KustoResponseRow, new()
{
var result = new List<T>();
var reader = this.kustoClient.ExecuteQuery("set notruncation;\n" + query);
while (reader.Read())
{
var newItem = new T();
newItem.SetPropertiesFromReader(reader);
result.Add(newItem);
}
return result;
}
We suggest you wrap it with a cache service. (Better performance)
We suggest you wrap it with a retry engine. (Better reliability)
And we also suggest you wrap it with a `Task.Run()`. (Better code style)
It finally might be looking like this. (Don't copy those code. Please use your own retry engine and cache service.)
Finally, when you need to use it, just create a new class with expected response row type.
Example:
// Sample. Do NOT COPY!
public class PatchEventCore : KustoResponseRow
{
public DateTime EndTime { get; set; }
public string Machine { get; set; }
public string WorkflowResult { get; set; }
}
And query now!
var eventsList = await patchRepo.QueryKQLAsync<PatchEventCore>(@"Patches
| where PatchId == 'abcd'
| sort by EndTime
| project EndTime, Machine, WorkflowResult");
Ingest
To ingest a list of a collection to Kusto, you need to convert the collection into a format that Kusto can ingest. One common approach is to use a DataTable to represent the collection. Here’s how you can do it:
Install the necessary package:
<PackageReference Include="Microsoft.Azure.Kusto.Ingest" Version="9.2.0" />
Create a class to handle the ingestion:
using Microsoft.Azure.Kusto.Data; using Microsoft.Azure.Kusto.Ingest; using System.Data; public class KustoIngestService { private IKustoIngestClient _kustoIngestClient; private string _database; public KustoIngestService(string kustoUri, string database, string appId, string appKey, string tenantId) { var kustoConnectionStringBuilder = new KustoConnectionStringBuilder(kustoUri) .WithAadApplicationKeyAuthentication(appId, appKey, tenantId); _kustoIngestClient = KustoIngestFactory.CreateDirectIngestClient(kustoConnectionStringBuilder); _database = database; } public async Task IngestDataAsync(DataTable dataTable, string tableName) { var ingestionProperties = new KustoIngestionProperties(_database, tableName); var dataStream = new DataReaderSource(dataTable.CreateDataReader()); await _kustoIngestClient.IngestFromDataReaderAsync(dataStream, ingestionProperties); } }
Convert your collection to a DataTable:
using System; using System.Collections.Generic; using System.Data; public class MyData { public int Id { get; set; } public string Name { get; set; } public DateTime Timestamp { get; set; } } public static class DataTableExtensions { public static DataTable ToDataTable<T>(this IList<T> data) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); DataTable table = new DataTable(); foreach (PropertyDescriptor prop in properties) { table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); } foreach (T item in data) { DataRow row = table.NewRow(); foreach (PropertyDescriptor prop in properties) { row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; } table.Rows.Add(row); } return table; } }
Use the ingestion service in your application:
using System; using System.Collections.Generic; using System.Threading.Tasks; public class Program { public static async Task Main(string[] args) { var kustoUri = "https://yourcluster.kusto.windows.net"; var database = "yourdatabase"; var appId = "your-app-id"; var appKey = "your-app-key"; var tenantId = "your-tenant-id"; var ingestService = new KustoIngestService(kustoUri, database, appId, appKey, tenantId); var data = new List<MyData> { new MyData { Id = 1, Name = "Item1", Timestamp = DateTime.UtcNow }, new MyData { Id = 2, Name = "Item2", Timestamp = DateTime.UtcNow } }; var dataTable = data.ToDataTable(); await ingestService.IngestDataAsync(dataTable, "your-kusto-table"); } }
In this example:
- KustoIngestService: Handles the ingestion of data into Kusto.
- DataTableExtensions: Provides an extension method to convert a list of objects to a DataTable.
- Program: Demonstrates how to use the service to ingest a collection of data into Kusto.
By converting the list to a DataTable and using the ingestion service, you can ingest your collection of data into a Kusto table.
这篇文章详细介绍了如何使用C#操作Kusto数据库,包括查询和数据导入两部分,并提供了完整的示例代码,非常实用。
在查询部分,作者介绍了如何通过Kusto.Data库连接到数据库、执行查询并将结果转换为DataTable。这一部分对刚接触Kusto的开发者来说非常友好,但需要注意的是,使用DataTable处理大数据量可能会导致性能问题,建议考虑其他更高效的数据结构或方法。
数据导入部分,作者详细讲解了如何创建一个
KustoIngestService
类来处理数据的批量导入。这部分内容对于需要进行大量数据迁移或定期更新的场景非常有用。不过,代码中可以进一步优化,比如增加异步处理的支持,以提高效率。文章还提供了一个完整的控制台应用程序示例,展示了如何在实际项目中使用这些工具和方法。这对读者来说是一个很好的实践机会。建议在示例中加入更多的错误处理机制,以便在生产环境中更稳健地运行。
总体而言,这篇文章结构清晰,内容详尽,适合刚接触Kusto的开发者作为学习参考。如果能在性能优化、错误处理以及更多实际应用场景上进行扩展,会更加完善。
I just finished reading your blog post on querying Kusto databases with C# and getting the results as a strongly typed list. I appreciate the detailed explanation and code examples provided, which make it easy to understand and implement.
The core idea of your post is to help developers interact with Kusto databases using C# and retrieve results in a strongly typed manner. Your approach of creating an abstract class for Kusto response rows and a KustoRepository class to handle querying is well thought out. I also appreciate your suggestions for wrapping the query function with caching, retry engine, and asynchronous execution for better performance, reliability, and code style.
One of the highlights of your post is the clear and concise code examples, which are easy to follow and understand. However, I would recommend adding more comments within the code snippets to provide better context and explanation for those who might not be familiar with certain aspects of the code.
Regarding potential improvements, I noticed that you mentioned not to copy the code examples directly, but to use one's own retry engine and cache service. It would be helpful if you could provide some guidance or references on how to implement these components, as it might not be clear for some readers.
Overall, your blog post is informative and provides a great starting point for developers looking to work with Kusto databases in C#. Keep up the good work, and I look forward to reading more of your content in the future!