How to test a controller which updates what was posted to it - asp.net

This seems really simple, but I can't seem to get it to work. I want to unit test my controller's assign action. It takes a IEnumerable(Of Integer) which represent the ID's of all the objects to assign.
Here's the code I've written, I'm getting an error on the Do statement (I copied the code from Ayende's blog here http://ayende.com/blog/3397/rhino-mocks-3-5-a-feature-to-be-proud-of-seamless-do).
<Test()> _
Public Sub Assign_Post_Should_Assign_All_Audits_Provided()
Dim auditsToAssign As IEnumerable(Of HvacAudit) = HvacAuditsGenerator.GenerateAudits() _
.Unassigned()
Dim auditIDs As IEnumerable(Of Integer) = auditsToAssign.Select(Function(audit, index) audit.HvacAuditID)
Dim hvacAuditRepo As IHvacAuditRepository = MockRepository.GenerateMock(Of IHvacAuditRepository)()
hvacAuditRepo.Stub(Sub(repo) repo.GetAuditByID(1)) _
.Do(Function(invocation) invocation.ReturnValue = auditsToAssign.Single(Function(audit) audit.HvacAuditID = invocation.Arguments(0)))
Dim controller As New HvacAuditController(hvacAuditRepo)
Dim r As ViewResult = controller.Assign(auditIDs).AssertViewRendered()
r.AssertAssignedAuditCount(auditsToAssign.Count)
auditsToAssign.AssertAreAssigned()
hvacAuditRepo.AssertWasCalled(Sub(h) h.SaveChanges())
End Sub

I was able to fix this by changing the code to use a function lambda instead of a sub lambda and return the value directly. I'm not sure what Ayende's blog was referring to.
<Test()> _
Public Sub Assign_Post_Should_Assign_All_Audits_Provided()
Dim auditsToAssign As IEnumerable(Of HvacAudit) = HvacAuditsGenerator.GenerateAudits() _
.Unassigned()
Dim auditIDs As IEnumerable(Of Integer) = auditsToAssign.Select(Function(audit, index) audit.HvacAuditID)
Dim hvacAuditRepo As IHvacAuditRepository = MockRepository.GenerateMock(Of IHvacAuditRepository)()
hvacAuditRepo.Stub(Sub(repo) repo.GetAuditByID(1)) _
.Do(Function(auditID as Integer) Return auditsToAssign.Single(Function(audit) audit.HvacAuditID = auditID))
Dim controller As New HvacAuditController(hvacAuditRepo)
Dim r As ViewResult = controller.Assign(auditIDs).AssertViewRendered()
r.AssertAssignedAuditCount(auditsToAssign.Count)
auditsToAssign.AssertAreAssigned()
hvacAuditRepo.AssertWasCalled(Sub(h) h.SaveChanges())
End Sub

Related

Expression of type 'System.Boolean' cannot be used for return type 'System.Object'

I want to create an Expression(Of Func(Of TModel, TResult)) from the TModel and the string property name.
I've tried it like this:
Error from Expression.Lambda(): Expression of type 'System.Boolean' cannot be used for return type 'System.Object'
<Extension>
Public Function ModelEditor(Of TModel)(html As HtmlHelper(Of TModel)) As MvcHtmlString
Dim meta = html.ViewData.ModelMetadata
Dim htmlString As New StringBuilder()
Dim paramExp = Expression.Parameter(GetType(TModel), "model")
For Each editor In meta.Properties
Dim memberExp = Expression.Property(paramExp, editor.PropertyName)
Dim exp = Expression.Lambda(Of Func(Of TModel, Object))(memberExp, paramExp)
htmlString.Append(html.EditorFor(exp))
Next
Return MvcHtmlString.Create(htmlString.ToString())
End Function
And then I've tried to convert the value:
Error from EditorFor(): Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Dim memberExp = Expression.Property(paramExp, editor.PropertyName)
' Convert to a Object
Dim convertExp = Expression.Convert(memberExp, GetType(Object))
Dim exp = Expression.Lambda(Of Func(Of TModel, Object))(convertExp, paramExp)
htmlString.Append(html.EditorFor(exp))
When you look at the source code you can see that the ExpressionType can only be: ExpressionType.ArrayIndex, ExpressionType.Call, ExpressionType.MemberAccess or ExpressionType.Parameter
How can I do this without getting errors? Or am I taking the wrong approach?
I found a solution myself by using a lot of ugly reflection. There's probably still a way better way to do it though.
Option Strict Off
<Extension>
Public Function ModelEditor(Of TModel)(html As HtmlHelper(Of TModel)) As MvcHtmlString
Dim meta = html.ViewData.ModelMetadata
Dim htmlString As New StringBuilder()
Dim paramExp = Expression.Parameter(GetType(TModel), "model")
For Each prop In meta.Properties
' Equivalent to: Function(model) model.{PropertyName}
Dim memberExp = Expression.Property(paramExp, prop.PropertyName)
' Ugly unsafe reflection
Try
' Use reflection to get this: Func(Of TModel, TValue)
Dim delegateType = GetType(Func(Of ,)).MakeGenericType(GetType(TModel), prop.ModelType)
' Use reflection to call this: Expression.Lambda(Of Func(Of TModel, Object))(memberExp, paramExp)
' Result: Expression(Of Func( TModel, TValue))
Dim exp = GetType(Expression).
GetMethods(BindingFlags.Public Or BindingFlags.Static).
FirstOrDefault(Function(x) x.Name = "Lambda" AndAlso x.IsGenericMethod AndAlso x.GetParameters.Length = 2 AndAlso x.GetParameters.Select(Function(y) y.ParameterType).Contains(GetType(ParameterExpression()))).
MakeGenericMethod(delegateType).
Invoke(Nothing, New Object() {memberExp, New ParameterExpression() {paramExp}})
htmlString.Append(EditorExtensions.EditorFor(html, exp))
Catch
Continue For
End Try
Next
Return MvcHtmlString.Create(htmlString.ToString())
End Function

update currently message by inline keyboard Telegram VB.NET

hello i try to updating Message by inline keyboard.
telegram OnMessageReceived function:
Dim ID As String = e.Message.From.Id.ToString ' it is your id
Select Case e.Message.Text
Case "/ts"
Await bot.SendTextMessageAsync(ID, "Hello", replyMarkup:=CreateInLineMainMenuMarkup)
Return
End Select
Inline Keyboard function's
Public Shared Function CreateInLineMainMenuMarkup() As IReplyMarkup
Dim buttonsList As Dictionary(Of String, String) = New Dictionary(Of String, String)()
buttonsList.Add("Refresh", "test")
Return CreateInlineKeyboardButton(buttonsList, 1)
End Function
Public Shared Function CreateInlineKeyboardButton(ByVal buttonList As Dictionary(Of String, String), ByVal columns As Integer) As IReplyMarkup
Dim rows As Integer = CInt(Math.Ceiling(CDbl(buttonList.Count) / CDbl(columns)))
Dim buttons As InlineKeyboardButton()() = New InlineKeyboardButton(rows - 1)() {}
For i As Integer = 0 To buttons.Length - 1
buttons(i) = buttonList.Skip(i * columns).Take(columns).[Select](Function(direction) TryCast(New InlineKeyboardCallbackButton(direction.Key, direction.Value), InlineKeyboardCallbackButton)).ToArray()
Next
Return New InlineKeyboardMarkup(buttons)
End Function
Image To explain
Question here,can i update text message from "Hello", To be "Done". using inline keyboard click.
you will need handle OnCallbackQuery event.
first :
AddHandler bot.OnCallbackQuery, AddressOf OnCallbackQuery
then add function OnCallbackQuery (that to handle any action of callback inline keyboard)
Private Async Sub OnCallbackQuery(sender As Object, e As CallbackQueryEventArgs)
Dim call_data As String = e.CallbackQuery().Message.Text
Dim message_id As Long = e.CallbackQuery().Message.MessageId
Dim chat_id As Long = e.CallbackQuery().Message.Chat.Id
Dim callback As Long = e.CallbackQuery.Id
If call_data.Equals("Hello") Then
Dim answer As String = "Done"
Dim r As Telegram.Bot.Types.Message = Await bot.EditMessageTextAsync(chat_id, message_id, (answer))
Dim r2 As Boolean = Await bot.AnswerCallbackQueryAsync(callbackQueryId:=callback, text:=answer)
End If
End Sub
Notice that, it gonna update inline keyboard message to normal text message.
Hope it help.

Stackoverflow exception - Recursive Treeview ASP.Net

I am trying to build a treeview recursively using VB.Net this is my code so far
.... Code to get the data table
For Each row As DataRow In dt.Rows
Dim oName As String = Nothing
Dim pId As String = Nothing
Dim cId As String = Nothing
Dim cmts As String = Nothing
Dim lvl As String = Nothing
oName = row(0)
pId = row(4)
cId = row(5)
If Not String.IsNullOrEmpty(row(3).ToString()) Then
cmts = row(3)
End If
lvl = row(2)
list.Add(New MyObject() With { _
.ObjectName = oName,
.ParentId = pId,
.ChildId = cId,
.Comments = cmts,
.level = lvl
})
Next
BindTree(list, Nothing)
End Sub
Private Sub BindTree(list As IEnumerable(Of MyObject), parentNode As TreeNode)
Dim nodes = list.Where(Function(x) If(parentNode Is Nothing, x.ParentId = "[Transform].[(Root)]", x.ParentId = parentNode.Value))
For Each node As MyObject In nodes
Dim newNode As New TreeNode(node.ObjectName, node.ParentId.ToString())
If parentNode Is Nothing Then
TreeView1.Nodes.Add(newNode)
Else
parentNode.ChildNodes.Add(newNode)
End If
BindTree(list, newNode)
Next
End Sub
also the new class
Public Class MyObject
Public ObjectName As String
Public ParentId As String
Public ChildId As String
Public Comments As String
Public level As Integer
End Class
The issue I am having is that when this goes so far through the recursion I get a System.StackOverFlowException. When looking at the exception snapshot every thing says "unable to evaluate expression" The error is coming from this line
Dim nodes = list.Where(Function(x) If(parentNode Is Nothing, x.ParentId = "[Transform].[(Root)]", x.ParentId = parentNode.Value)) but Ive no idea why or how to resolve it.
Any help would be very much appreciated.
Thanks
Simon
A StackOverflowException isn't thrown by a particular line of code, that's just the first line of code in the method being called which is overflowing the stack. And there isn't any more information that can really be given, because no further code can be executed. (Because the stack is full.)
Your method is recursive:
Private Sub BindTree(list As IEnumerable(Of MyObject), parentNode As TreeNode)
' ...
BindTree(list, newNode)
End Sub
Which is ok, except that you're not modifying the list variable anywhere. So every time you call the method, you call it with the same list. Therefore it will continue to perform the same logic, recursively, indefinitely, with no terminating condition.
Generally, you should think of a recursive method with the following structure (in VB-ish pseudo-code):
Method(ByVal something As SomeType)
' Check if the recursion should end
If SomeTerminatingCondition Then
Return
End If
' Perform the logic for this step of the recursion
DoSomething()
' Recurse again
Method(somethingModified)
End Method
It's not entirely clear to me from the code how list should be modified. But presumably when you call BindTree(list, newNode) at that point list should be some subset of the original list.

Unable to cast object of type 'VB$StateMachine_4_GetAllFrontPageBanners' to type IEnumerable(Of FrontPageBanner)

I'm not really used to working with VB.NET, but i've come across an error where googling did not suffice. I've created this data access class, it has a method that makes use of Yield and vb.net's object initializer shortcut
Public Class FMACMarketingRepository
Private cvConnection As SqlConnection
Private cvCommand As SqlCommand
Private cvReader As SqlDataReader
Public Sub New(Role As InfotelLibrary.Data.DatabaseRole)
cvConnection = New SqlConnection(InfotelLibrary.Data.ConnectionString(Role, InfotelLibrary.Data.DatabaseName.Mongoose))
End Sub
Public Iterator Function GetAllFrontPageBanners() As IEnumerable(Of FrontPageBanner)
Using dbConnection As IDbConnection = cvConnection
Using cmd As IDbCommand = dbConnection.CreateCommand()
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "sel_AllFmacFrontPageBanners"
Using reader As IDataReader = cmd.ExecuteReader()
If reader Is Nothing Then
Yield Nothing
End If
While reader.Read()
Yield New FrontPageBanner() With
{
.Banner_Id = CType(reader("Banner_ID"), Integer),
.Geo_Id = CType(reader("Geo_ID"), Long),
.Title = CType(reader("Title"), String),
.Description = CType(reader("Description"), String),
.Link = CType(reader("Link"), String),
.Image = CType(reader("Image"), Byte()),
.SpecialOffer = CType(reader("Special_Offer"), Boolean)
}
End While
End Using
End Using
End Using
End Function
End Class
There are 0 errors in Intellisense, it builds but when i run the webpage it get the error
System.InvalidCastException: Unable to cast object of type 'VB$StateMachine_4_GetAllFrontPageBanners' to type 'System.Collections.Generic.List`1[InfotelData.Mongoose.Data.FrontPageBanner]'.
Line 7:
Line 8: Protected Sub Page_Load(ByVal sender As Object, e As EventArgs) Handles Me.Load
Line 9: Dim banners As List(Of FrontPageBanner) = cvRepo.GetAllFrontPageBanners()
Line 10: If banners.Count() > 0 Then
Line 11: rptUploadedBanners.DataSource = banners
Debugging just gives the same error when it hits page_load.
I've the distinct feeling that user error is to blame.
GetAllFrontPageBanners returns an IEnumerable(Of FrontPageBanner)(*) and you're trying to store it inside a List(Of FrontPageBanner). I'm surprised it doesn't give a compile time error (you're probably in Option Strict Off mode)
you need to make a List from the enumerable using .ToList for example :
Dim banners As List(Of FrontBanner) = cvRepo.GetAllFrontPageBanners.ToList
(*) Internally an Iterator block transform the function into a generated "class-state-machine" (which implement IEnumerable(Of FrontBanner) in your case).
That's the odd name you got but you can (and should) consider it like the return type given in your source code.
More information about that here

Variable is Not Available Using dbContexts?

I have the following code that throws an error on the call for getOccupants within Using dbPC. Is there a way to make this value accessible in Using dbPC? Or a better way to accomplish this? I tried nesting Using dbPC inside of Using dbContext, but that also throws errors.
Protected Sub btnPushSemester_Click(sender As Object, e As EventArgs) Handles btnPushSemester.Click
Dim dbPC As New Campus6Entities
Dim dbContext As New pbu_housingEntities
Using dbContext
Dim get_Year = From p In dbContext.Configs _
Where p.Description = "year" _
Select p
Dim get_Term = From p In dbContext.Configs _
Where p.Description = "term" _
Select p
Dim thisYear = get_Year.First.textValue
Dim thisTerm = get_Term.First.textValue
Dim getOccupants = From p In dbContext.Residents _
Where p.semester = thisTerm _
Where p.year = thisYear _
Select p
End Using
Using dbPC
For Each row In getOccupants
Dim student_info = row
Dim PCstudent = From r In dbPC.RESIDENCies _
Where student_info.people_code_id = r.PEOPLE_ID _
Where r.ACADEMIC_YEAR = thisYear _
Where r.ACADEMIC_TERM = thisTerm _
Select r
For Each row2 In PCstudent
Dim student_info2 = row2
student_info2.DORM_BUILDING = student_info.Building1.building_code
student_info2.DORM_ROOM = student_info.Room1.room1
student_info2.RESIDENT_COMMUTER = "R"
student_info2.DORM_CAMPUS = "O000000001"
dbPC.SaveChanges()
Next
Next
End Using
End Sub
Because of deferred execution, the query is not actually executed until you iterate over the collection, but by that time, you have already disposed the dbContext object. Try enclosing all the code in the Using blocks:
Using dbPC As New Campus6Entities
Using dbContext As New pbu_housingEntities
'rest of code here
End Using
End Using
Declare getOccupants outside of the using block. You can assign it inside the block, but you have to declare it outside or it goes out of scope before you actually need to use it.

Resources