Output SqlParameter value not populated when using SqlCommand ExecuteReaderAsync with using declaration - using

I am running this stored procedure:
CREATE OR ALTER PROCEDURE dbo.usp_test
#Result int OUTPUT
AS
BEGIN
SELECT Id FROM Table1;
SET #Result = 101;
END
GO
In my code, I notice different behaviour depending on how wether I use a using statement, or a using declaration.
using statement (C# Reference)
With a using statement
await using var connection = new SqlConnection("...connectionstring");
await connection.OpenAsync();
await using var command = new SqlCommand("usp_test", connection) {CommandType = CommandType.StoredProcedure};
var outParam = new SqlParameter("Result", SqlDbType.Int) {Direction = ParameterDirection.Output};
command.Parameters.Add(outParam);
var items = new List<int>();
// using statement
await using (var reader = await command.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
var id = Convert.ToInt32(reader["Id"]);
items.Add(item);
}
}
var availableItems = Convert.ToInt32(outParam.Value);
In this case I get both the list of Id's in items, and the expected value of 101 in the output parameter result.
With a using declaration
await using var connection = new SqlConnection("...connectionstring");
await connection.OpenAsync();
await using var command = new SqlCommand("usp_test", connection) {CommandType = CommandType.StoredProcedure};
var outParam = new SqlParameter("Result", SqlDbType.Int) {Direction = ParameterDirection.Output};
command.Parameters.Add(outParam);
var items = new List<int>();
// using declaration
await using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var id = Convert.ToInt32(reader["Id"]);
items.Add(item);
}
var result = Convert.ToInt32(outParam.Value);
In the example above, I get the list of Id's in items, but the result is 0.
What is going on here?
I'll be adopting the pattern that works, but I worry that some well-intentioned future developer will see the using statement and replace it with a using declaration because it looks nicer and be unaware that he's breaking something.

Related

Unable to cast object of type 'Microsoft.Graph.CalendarEventsCollectionPage' to type 'System.Collections.Generic.IEnumerable`

My controller code is lke this,
public async Task<IEnumerable<CalendarEvent>> Get()
{
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "xxxxx";
var clientId = "xxxxxx";
var clientSecret = "xxxx";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphServiceClient = new GraphServiceClient(clientSecretCredential, scopes);
if (User == null!)
{
var user = await graphServiceClient.Users["xxxxx.com"].Calendar
.Events
.Request()
.Select("subject,body,bodyPreview,organizer,attendees,start,end,location")
.GetAsync();
return (CalendarEvent)user;
}
}
Iam getting an error like
Unable to cast object of type 'Microsoft.Graph.CalendarEventsCollectionPage' to type 'System.Collections.Generic.IEnumerable'
I need query that sholud be given in controller.
It's not exactly clear what you are trying to achieve but you can't convert CalendarEventsCollectionPage to IEnumerable. I am assuming that you want to return all events of specific user.
public async Task<List<Event>> GetEventsOfUser(string userId)
{
var events = new List<Event>();
var eventsPages = _client.Users[userId].Calendar.Events.Request()
.Select("subject,body,bodyPreview,organizer,attendees,start,end,location");
while (eventsPages != null)
{
var current = await eventsPages.GetAsync();
events.AddRange(current.CurrentPage);
eventsPages = current.NextPageRequest;
}
return events;
}
You need to fetch every page with NextPageRequest in order to get all events.

Creating DbCommand does get timeout assigned from DBContext

I have ASP.NET Core 2.2 application using EF Core. In startup.cs i am setting CommandTimeout to 60 seconds
services.AddDbContext<CrowdReason.WMP.Data.Entities.WMPContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
sqlServerOptions => sqlServerOptions.CommandTimeout(60)));
Then i am executing the stored proc using the following code. Please note the values of t1, t2 and t3 in comments
public static async Task<int?> prcDoWork(this WMPContext dbContext, int id, int userID)
{
var t1 = dbContext.Database.GetCommandTimeout();
// t1 is 60. Same as what i set in startup.cs
using (var connection = dbContext.Database.GetDbConnection())
{
var t2 = connection.ConnectionTimeout;
//t2 is 15
using (var cmd = connection.CreateCommand())
{
var p1 = new SqlParameter("#ID", SqlDbType.Int)
{
Value = id
};
var p2 = new SqlParameter("#UserID", SqlDbType.Int)
{
Value = userID
};
cmd.CommandText = "dbo.prcDoWork";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
var t3 = cmd.CommandTimeout;
//t3 is 30
await dbContext.Database.OpenConnectionAsync().ConfigureAwait(false);
var result = await cmd.ExecuteScalarAsync().ConfigureAwait(false);
if (result != null)
{
return Convert.ToInt32(result);
}
return null;
}
}
}
I understand that ConnectionTimeout is different from CommandTimeout.
However issue is when i create command using connection.CreateCommand() its not automatically getting the timeout from DBContext
EF Core works on top of the underlying System.Data implementation (in your case System.Data.SqlClient) and uses it to perform its DB operations. All settings you make will only reflect the way EF uses this underlying implementation.
When you use the GetDbConnection method you get a reference to an SqlConnection class from the System.Data.SqlClient assembly that knows nothing about EF and its settings and cannot be expected to honor the RelationalOptionsExtension.CommandTimeout from the Microsoft.EntityFrameworkCore.Relational assembly.
To have EF settings respected you should use the RelationalDatabaseFacadeExtensions.ExecuteSqlCommandAsync method.

How to access Shared Mail Folder Using Microsoft Graph In .NET Application

I have Generated Microsoft Graph app in ASP.NET MVC platform, that I have downloaded from Microsoft Graph site. I need to access the shared mail folder not sure exactly how can I get that?? In the following code I can access my mailFolder but not shared mailfolder!
public static async Task<IEnumerable<MailFolder>> GetMailFolderAsync()
{
var graphClient = GetAuthenticatedClient();
var mailFolder = await graphClient.Me.MailFolders.Request().GetAsync();
var sharedMailFolder = await graphClient.Users.Request().GetAsync();
return mailFolder;
}
Also, I want to know in above code where I can pass the parameter to access next page or all pages??
private static GraphServiceClient GetAuthenticatedClient()
{
return new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string signedInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
SessionTokenStore tokenStore = new SessionTokenStore(signedInUserId,
new HttpContextWrapper(HttpContext.Current));
var idClient = new ConfidentialClientApplication(
appId, redirectUri, new ClientCredential(appSecret),
tokenStore.GetMsalCacheInstance(), null);
var accounts = await idClient.GetAccountsAsync();
var result = await idClient.AcquireTokenSilentAsync(
graphScopes.Split(' '), accounts.FirstOrDefault());
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", result.AccessToken);
}));
I think it is not possible to access shared folders I am investigating as well. In regards to the question of getting pages, as soon as you get the first request
public static async Task<IEnumerable<MailFolder>> GetMailFolderAsync()
{
var graphClient = GetAuthenticatedClient();
var mailFolder = await graphClient.Me.MailFolders.Request().GetAsync();
var sharedMailFolder = await graphClient.Users.Request().GetAsync();
return mailFolder;
}
then you can review for example, mailFolder.NextPageRequest, if it is not null then you can request it by doing mailFolder.NextPageRequest.GetAsync() and you can use it as a loop conditional
while(mailfoldersCollection != null) {
// Do your stuff with items within for(var folder in mailfoldersCollection) {}
// when read all items in CurrentPage then
if (mailFolder.NextPageRequest != null) {
mailfoldersCollection = await mailFolder.NextPageRequest.GetAsync();
}
hope it works for you!

Using Postal and Hangfire in Subsite

I have been trying to use Postal on my MVC5 site. When I host my webpage a subsite ie, http://localhost/Subsite I am receiving the error
The virtual path '/' maps to another application, which is not allowed
I have debugged it down to when the ControllerContext is being created the HttpContext isn't getting set correctly. Since I'm running Postal from Hangfire the HttpContext.Current is always null. Postal creates the ContollerContext using the code below.
ControllerContext CreateControllerContext()
{
// A dummy HttpContextBase that is enough to allow the view to be rendered.
var httpContext = new HttpContextWrapper(
new HttpContext(
new HttpRequest("", UrlRoot(), ""),
new HttpResponse(TextWriter.Null)
)
);
var routeData = new RouteData();
routeData.Values["controller"] = EmailViewDirectoryName;
var requestContext = new RequestContext(httpContext, routeData);
var stubController = new StubController();
var controllerContext = new ControllerContext(requestContext, stubController);
stubController.ControllerContext = controllerContext;
return controllerContext;
}
string UrlRoot()
{
var httpContext = HttpContext.Current;
if (httpContext == null)
{
return "http://localhost";
}
return httpContext.Request.Url.GetLeftPart(UriPartial.Authority) +
httpContext.Request.ApplicationPath;
}
How can I specify the UrlRoot so that instead of pulling the default of localhost to pull it based on my subsite?
I followed the directions here http://docs.hangfire.io/en/latest/tutorials/send-email.html to send my email. The method in the tutorial is below
public static void NotifyNewComment(int commentId)
{
// Prepare Postal classes to work outside of ASP.NET request
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var engines = new ViewEngineCollection();
engines.Add(new FileSystemRazorViewEngine(viewsPath));
var emailService = new EmailService(engines);
// Get comment and send a notification.
using (var db = new MailerDbContext())
{
var comment = db.Comments.Find(commentId);
var email = new NewCommentEmail
{
To = "yourmail#example.com",
UserName = comment.UserName,
Comment = comment.Text
};
emailService.Send(email);
}
}
I found the issue was that the FileSystemRazorViewEngine was not being used bty postal. To get the this to work I had to make sure that the FileSystemRazorViewEngine was the first engine in the available. I then removed it because I did not want it to be the default engine. Below is my updated method.
public static void NotifyNewComment(int commentId)
{
// Prepare Postal classes to work outside of ASP.NET request
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var eng = new FileSystemRazorViewEngine(viewsPath));
ViewEngines.Engines.Insert(0, eng);
var emailService = new EmailService(engines);
// Get comment and send a notification.
using (var db = new MailerDbContext())
{
var comment = db.Comments.Find(commentId);
var email = new NewCommentEmail
{
To = "yourmail#example.com",
UserName = comment.UserName,
Comment = comment.Text
};
emailService.Send(email);
ViewEngines.Engines.RemoveAt(0)
}
}
Below is another possible solution that I think is more elegant than above. It also resolves an issue that appears when accessing the MVC application while the background process is being executed.
public static void SendTypedEmailBackground()
{
try
{
var engines = new ViewEngineCollection();
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var eng = new FileSystemRazorViewEngine(viewsPath);
engines.Add(eng);
var email = new WebApplication1.Controllers.EmailController.TypedEmail();
email.Date = DateTime.UtcNow.ToString();
IEmailService service = new Postal.EmailService(engines);
service.Send(email);
}
catch(Exception ex)
{
throw ex;
}
}

Entity Framework data insert in foreign key

An error occurs when I call the function shown below:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
Function:
[HttpPost]
public ActionResult Index(InsertPo model)
{
var context = new UsersContext();
var po = new Po();
var user = new User();
po.PoId = 12;
po.PoNumber = model.Po.PoNumber;
po.Style = model.Po.Style;
po.Quantity = model.Po.Quantity;
po.Status = "hhh";
po.OrderDate = Convert.ToDateTime("30-12-2011");
po.ShipmentDate = Convert.ToDateTime("2-12-2011");
po.ProductionRate = 10;
po.UserId = 2;
/*buyer.BuyerName = model.Buyer.BuyerName;*/
/* buyer.BuyerId = 1;
buyer.PoId = 10;*/
context.Pos.Add(po);
context.SaveChanges();
return RedirectToAction("Index");
}
Try putting this line outside of your Action method.
var context = new UsersContext();

Resources