Blazor Server - Return Count from SQL - Returns System.Collections.Generic.List`1[System.Object] - sql

I'm trying to return a count of row's from a stored procedure in SQL. I've been working at this for hours now. I'm at a complete loss.
SQL Code: Returns result of 49 when run in SQL.
begin
select Count (EmployeeID)
from dbo.Employees
Where (Employees.Active = 'True')
end
Component Code:
#attribute [Authorize(Roles = "Admin")]
#using System.Collections;
#using WahlenDataAccessLibrary;
#using WahlenDataAccessLibrary.Models;
#using BlazorApp1.Models;
#inject IEmployeesData employeeData;
<div class="widget">
<h5 class="widget--title">Active Employees</h5>
<div class="widget--body">
<p class="widget--number">
<span>#employeeCount</span>
Active Employees
</p>
</div>
</div>
#code {
private string employeeCount { get; set; }
//private IEmployeeModel employeeCount = new EmployeeModel();
protected override async Task OnInitializedAsync()
{
var count = await employeeData.EmployeeCount();
//string employeeCount = await employeeData.EmployeeCount();
string employeeCount = count.ToString();
Console.WriteLine(employeeCount);
if (employeeCount != null)
{
Console.WriteLine("generic value");
}
else
{
Console.WriteLine("no value");
}
}
}
DataFile Code: To get the value from stored procedure.
public async Task<string> EmployeeCount()
{
var employeeCount = await _db.LoadValue("dbo.spWidget_EmployeeCount", "DefaultConnection");
return employeeCount.ToString();
}
}
The DataFile where 'LoadValue' is used. This is linked back to my SqlDataAccess File which uses this code.
public async Task<string> LoadValue(string storedProcedure, string connectionStringName)
{
string connectionString = _config.GetConnectionString(connectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
var data = await connection.QueryAsync(storedProcedure,
commandType: CommandType.StoredProcedure);
return data.ToString();
}
}
When the application is running the console writes.
System.Collections.Generic.List`1[System.Object]
no value

The
System.Collections.Generic.List`1[System.Object]
comes from
var data = await connection.QueryAsync(storedProcedure,
commandType: CommandType.StoredProcedure);
return data.ToString();
the var data is actually an object. Callint .ToString() on a object will print out the type of the object (if the method is not over written).
Please check if the QueryAsync has a generic version. Usually it does and will make a type cast for you in the background. Try something like QueryAsync<int>. Or try to find the rigth method (with a single return item, instead of a list) on your ORM/db provider.
From the current context it is not possible to tell what db provider you are using.

I'm going to update the code here, as I've got it working.
Task<IEnumerable> was more so the answer here. If you have an sql query that is simply counting rows, this is how you access that data on Blazor on your front end. You can return the value as a string.
My sqlaccess file code now looks like this.
public async Task<IEnumerable<int>> LoadValue(string storedProcedure, string connectionStringName)
{
string connectionString = _config.GetConnectionString(connectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
var data = await connection.QueryAsync<int>(storedProcedure,
commandType: CommandType.StoredProcedure);
return data;
}
}
And this is on the front end component.
private string employeeCount { get; set; }
protected override async Task OnInitializedAsync()
{
employeeCount = await employeeData.EmployeeCount();
Console.WriteLine(employeeCount);
if (employeeCount != null)
{
Console.WriteLine("generic value");
}
else
{
Console.WriteLine("no value");
}

Related

Is there a way to improve the code? .Net Core 6.0 and Cosmos Database with SQL API

It's my first time using .Net Core 6.0 WebAPI and Cosmos Database with SQL API, so I'm concerned that I've done things correctly.
I have used /address/zipcode as a partition key in the below sample
public class FamilyService : IFamilyService
{
public FamilyService(CosmosClient dbClient, string databaseName, string containerName)
{
this._container = dbClient.GetContainer(databaseName, containerName);
}
public Task<Family> GetFamilyDataAsync(string id, string zipcode)
{
return Task.FromResult(_container.GetItemLinqQueryable<Family>(allowSynchronousQueryExecution: true)
.Where(p => p.Id == id && p.Address.ZipCode == zipcode)
.ToList().First());
}
public async Task AddFamilyDataAsync(Family family)
{
await this._container.CreateItemAsync<Family>(family, new PartitionKey(family.Address.ZipCode));
}
public async Task UpdateFamilyDataAsync(Family family)
{
await this._container.ReplaceItemAsync<Family>(family, family.Id, new PartitionKey(family.Address.ZipCode));
}
public async Task DeleteFamilyDataAsync(string id, string zipcode)
{
await this._container.DeleteItemAsync<Family>(id, new PartitionKey(zipcode));
}
private async Task<bool> GetFamilyDataFromId(string id, string zipcode)
{
string query = $"select * from c where c.id=#familyId";
QueryDefinition queryDefinition = new QueryDefinition(query).WithParameter("#familyId", id);
List<Family> familyResults = new List<Family>();
FeedIterator streamResultSet = _container.GetItemQueryStreamIterator(
queryDefinition,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey(zipcode),
MaxItemCount = 10,
MaxConcurrency = 1
});
while (streamResultSet.HasMoreResults)
{
using (ResponseMessage responseMessage = await streamResultSet.ReadNextAsync())
{
if (responseMessage.IsSuccessStatusCode)
{
dynamic streamResponse = FromStream<dynamic>(responseMessage.Content);
List<Family> familyResult = streamResponse.Documents.ToObject<List<Family>>();
familyResults.AddRange(familyResult);
}
else
{
return false;
}
}
}
if (familyResults != null && familyResults.Count > 0)
{
return true;
}
return familyResults;
}
Especially I want to focus more on those methods that retrieves the data from the database.
Update#1 : I have updated the code as suggested by #Mark Brown.
public class FamilyService : IFamilyService
{
private Container _container;
private static readonly JsonSerializer Serializer = new JsonSerializer();
public FamilyService(CosmosClient dbClient, string databaseName, string containerName)
{
this._container = dbClient.GetContainer(databaseName, containerName);
}
public async Task<Family> GetFamilyDataAsync(string id, string zipCode)
{
return await _container.ReadItemAsync<Family>(id, new PartitionKey(zipCode));
}
public async Task<List<Family>> GetAllFamilyDataAsync(string zipCode)
{
string query = $"SELECT * FROM Family";
QueryDefinition queryDefinition = new QueryDefinition(query);
List<Family> familyResults = new List<Family>();
FeedIterator<Family> feed = _container.GetItemQueryIterator<Family>(
queryDefinition,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey(zipCode),
MaxItemCount = 10,
MaxConcurrency = 1
});
while (feed.HasMoreResults)
{
FeedResponse<Family> response = await feed.ReadNextAsync();
foreach (Family item in response)
{
familyResults.Add(item);
}
}
return familyResults;
}
public async Task<List<Family>> GetAllFamilyDataByLastNameAsync(string lastName, string zipCode)
{
string query = $"SELECT * FROM Family where Family.lastName = #lastName";
QueryDefinition queryDefinition = new QueryDefinition(query).WithParameter("#lastName", lastName);
List<Family> familyResults = new List<Family>();
FeedIterator<Family> feed = _container.GetItemQueryIterator<Family>(
queryDefinition,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey(zipCode),
MaxItemCount = 10,
MaxConcurrency = 1
});
while (feed.HasMoreResults)
{
FeedResponse<Family> response = await feed.ReadNextAsync();
foreach (Family item in response)
{
familyResults.Add(item);
}
}
return familyResults;
}
public async Task<List<Family>> GetAllFamilyDataPaginatedAsync(int pageNumber, string zipCode)
{
int pageSize = 2;
int itemsToSkip = (pageSize * pageNumber) - pageSize;
QueryRequestOptions options = new QueryRequestOptions { MaxItemCount = pageSize };
string continuationToken = null;
List<Family> familyResults = new List<Family>();
IQueryable<Family> query = this._container
.GetItemLinqQueryable<Family>(false, continuationToken, options)
.Where(i => i.Address.ZipCode == zipCode)
.Skip(itemsToSkip);
using (FeedIterator<Family> iterator = query.ToFeedIterator<Family>())
{
FeedResponse<Family> feedResponse = await iterator.ReadNextAsync();
foreach (Family item in feedResponse)
{
familyResults.Add(item);
}
}
return familyResults;
}
public async Task AddFamilyDataAsync(Family family)
{
await this._container.CreateItemAsync<Family>(family, new PartitionKey(family.Address.ZipCode));
}
public async Task UpdateFamilyDataAsync(Family family)
{
await this._container.ReplaceItemAsync<Family>(family, family.Id, new PartitionKey(family.Address.ZipCode));
}
public async Task DeleteFamilyDataAsync(string id, string zipCode)
{
await this._container.DeleteItemAsync<Family>(id, new PartitionKey(zipCode));
}
}
If you are searching for something with the partition key and id then that can only return a single item as id must be unique within a pk. So GetFamilyDataAsync() can be implemented using ReadItemAsync() rather than a query. PS: Not sure why you would ever do this, allowSynchronousQueryExecution: true
GetFamilyDataFromId() also can be implemented using ReadItemAsync(). Even if this did require a query, it doesn't make sense to implement to return a stream, then deserialize into an object. Just use the variant that can deserialize directly into a POCO.
Can't say if this is completely optimal for everything, but you can go here to find more things as a place to start, https://learn.microsoft.com/en-us/azure/cosmos-db/sql/best-practice-dotnet
The other thing to call out is zip code as a partition key. This might work at small scale, but it could run into issues at larger scale depending on how many families and how many records for each family are stored within a single partition. Max size for a single partition key value is 20 GB. Fine for small workloads but possibly an issue at larger scale.

How to get a list of all Table names through asp.net API controller

So I want to get a list of all the table names from the database through a controller as an ASP.net API project. I tried to do it through raw sql query without entity and it looked something like this.
public async Task<ActionResult<IList>> GetAllTableNames()
{
using (var context = new DbContext())
{
List<string> results = context.Database.SqlQuery<string>("SELECT name FROM sys.tables").ToListAsync();
}
}
But when I try to use the SqlQuery method I get the error
" 'DatabaseFacade' does not contain a definition for 'SqlQuery' and no accessible extension method 'SqlQuery' ". Anybody that has any idea how to solve this?
First create an Helper method in your controller as shown below
using System.Data.SqlClient;
public async IAsyncEnumerable<string> GetTableNamesAsync()
{
using var connection = new SqlConnection(_dbContext.Database.GetConnectionString());
var command = new SqlCommand("SELECT name FROM sys.tables",connection);
await connection.OpenAsync();
var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
yield return reader.GetString(0);
}
}
Then call in your action Like this
public async Task<IActionResult> Index()
{
var list=new List<string>();
await foreach (var item in GetTableNamesAsync())
{
list.Add(item);
}
return Ok(list);
}

Npgsql - BulkCopy from ConcurrentQueue<T>

Let's say I have a method like this:
public async Task BulkCopy(ConcurrentQueue<Model> modelQueue, string connectionString)
{
while(modelQueue.IsEmpty == false)
{
try
{
using(NpgsqlConnection connection = new NpgsqlConnection(connectionString))
{
await connection.OpenAsync();
using(var writer = connection.BeginBinaryImport("COPY myTable (Id,Name,Something,SomethingElse)"))
{
// Is this what I'm supposed to do?
foreach(Model item in modelQueue)
{
writer.WriteRow(item);
}
}
}
}
}
}
Model has properties Guid Id, string Name, string Something, string SomethingElse(just like the table).
Can I use WriteRow() and pass in an entire object? Is this implementation way to go or I am doing it wrong?
The answer to this is pretty simple, since ConcurrentQueue implements IEnumerable all I had to do is run through the queue and write the data.
public async Task BulkCopy(ConcurrentQueue<Model> modelQueue, string connectionString)
{
while(modelQueue.IsEmpty == false)
{
try
{
using(NpgsqlConnection connection = new NpgsqlConnection(connectionString))
{
await connection.OpenAsync();
using(var writer = connection.BeginBinaryImport("COPY myTable (Id,Name,Something,SomethingElse) FROM STDIN (FORMAT BINARY)"))
{
foreach(Model item in modelQueue)
{
writer.StartRow();
writer.Write(item.Id, NpgsqlTypes.NpgsqlDbType.Uuid);
writer.Write(item.Name);
writer.Write(item.Something);
writer.Write(item.SomethingElse);
}
}
}
}
}
}
This seems to do the job. The time needed for 30000 records is average 540ms.

Value cannot be null. Parameter name: view

I was trying to render a dynamic view into string.I got the idea from here and it is working well locally but on hosting I am getting the following error.My attempt is to convert the string to bytes for sending email
Value cannot be null. Parameter name: view
Controller
public ActionResult Index(int RegId)
{
try
{
var _mdlReceiptPdf = new ReceiptPdfVM
{
...
...
};
string result = RazorViewToString.RenderRazorViewToString(this, Server.MapPath("~/Views/Shared/_Receipts.cshtml"), _mdlReceiptPdf);
StringReader _sr = new StringReader(result);
return Json(result, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(ex.Message, JsonRequestBehavior.AllowGet);
}
}
Util.cs
public static class RazorViewToString
{
public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
{
controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
}
I had this problem. In my case, it turned out that the partial view wasn't being uploaded to the hosted website because in the File Properties for the partial view, the BuildAction was set to None instead of Content
Yeah looks like this error is saying view can't be found.
In my case I had a typo for the view name in the controller.
_QuickView.cshtml instead of QuickView.cshtml

MVC aynchronous method

I am working on a MVC project that submits a request via a third party.
In my controller, I have a SubmitClaims() action that receive ajax request and then calls RunAsync(). RunAsync submits a request by using HttpClient.
I am not sure if I did a right thing here.
Also I have two version of SubmitClaims(), both work. But I don't know which version is better.
version 1
[HttpPost]
public async Task<string> SubmitClaims()
{
string result = "";
result = await RunAsync();
return result;
}
version 2 learn from Cannot implicitly convert type 'string' to 'System.Threading.Tasks.Task<string>'
[HttpPost]
public async Task<string> SubmitClaims()
{
return await Task.Run(() =>
{
return RunAsync();
});
}
static async Task<string> RunAsync()
{
string result = "Failed.";
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("http://peter:8001/internal/uickpost");
client.DefaultRequestHeaders.Add("contenttype", "application/xml");
client.DefaultRequestHeaders.Add("hiconline.protocol.content.role", "REQUEST");
client.DefaultRequestHeaders.Add("hiconline.protocol.content.transactionid", "asdfsdf");
client.DefaultRequestHeaders.Add("hiconline.protocol.remote.contenttype", "TestDataType");
client.DefaultRequestHeaders.Add("hiconline.protocol.remote.mode", "P");
client.DefaultRequestHeaders.Host = "peter:8001";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
string opv = "Test Data";
HttpContent _content = new StringContent(opv);
_content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
_content.Headers.Add("contenttype", "TestDataType");
HttpResponseMessage response1 = await client.PostAsync(client.BaseAddress, _content);
if (response1.IsSuccessStatusCode)
{
Uri gizmoUrl = response1.Headers.Location;
result = response1.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
result = ex.Message;
}
return result;
}
}
Option 1 is better. RunAsync() already returns a task, so why create another one?
Even better would be return await RunAsync();. Even better would just be calling RunAsync directly, since the wrapper doesn't add anything.