dal2 linq error in custom module when calling repository GetById - dotnetnuke

I have a simple custom module I am building but I have run into a problem when using the DAL2 GetById.
Here is the POCO declaration of the table i am using:
<TableName("KrisisStore_Products")> _
<PrimaryKey("ProductId", AutoIncrement:=True)> _
<Cacheable("Products", CacheItemPriority.Default, 20)> _
<Scope("PortalId")>
Public Class Product
Public Property ProductId As Int64
Public Property PortalId As Integer
Public Property ModuleId As Integer
''other columns here
End Class
I am attempting to delete a record from the database using the following (i removed other methods for clarity):
In the module view:
Dim pc As New ProductController
pc.DeleteItem(e.CommandArgument, PortalId)
Here is my product controller:
Imports System.Collections.Generic
Imports DotNetNuke.Data
Namespace Components
Public Class ProductController
Public Sub DeleteItem(ByVal itemId As Integer, ByVal PortalId As Integer)
Dim _item As Product = GetItem(itemId, PortalId)
DeleteItem(_item)
End Sub
Public Sub DeleteItem(ByVal p As Product)
Using ctx As IDataContext = DataContext.Instance()
Dim rep As IRepository(Of Product) = ctx.GetRepository(Of Product)()
rep.Delete(p)
End Using
End Sub
Public Function GetItem(ByVal itemId As Integer, ByVal PortalId As Integer) As Product
Dim p As Product
Using ctx As IDataContext = DataContext.Instance()
Dim rep As IRepository(Of Product) = ctx.GetRepository(Of Product)()
p = rep.GetById(Of Int32, Int32)(itemId, PortalId)
End Using
Return p
End Function
End Class
End Namespace
PROBLEM:
When the code gets to the following line in the GetITem function
p = rep.GetById(Of Int32, Int32)(itemId, PortalId)
The following error is generated:
Value cannot be null. Parameter name: source
Here is a little more detail from the stack trace:
InnerException: Value cannot be null. Parameter name: source
FileName:
FileLineNumber: 0
FileColumnNumber: 0
Method: System.Linq.Enumerable.SingleOrDefault
StackTrace:
Message: DotNetNuke.Services.Exceptions.ModuleLoadException: Value cannot be null. Parameter name: source ---> System.ArgumentNullException: Value cannot be null. Parameter name: source at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate) at DotNetNuke.Data.RepositoryBase`1.GetById[TProperty,TScopeType](TProperty id, TScopeType scopeValue) at Krisis.Modules.KrisisStore.Components.ProductController.GetItem(Int32 itemId, Int32 PortalId) in C:\websites\dnndev.me\DesktopModules\KrisisStore\Components\ProductController.vb:line 51
QUESTION
Can someone help me figure out why this linq error is being generated, the values being passed to the function are valid and the other repository function like GetItems work properly when supplied with a portalID as their scope.

In your model class, you defined the ProductId As Int64, but in your controller methods it is passed as an Integer or Int32. You would think this wouldn't matter, but I have experience other issues where PetaPoco requires very specific implementation in order to work properly. Perhaps this is an issue?

At first glance, it looks like the query is bringing back multiple records. Could there be more than one product row with that ID?

Related

How to inherit from an Object class in vb.net

I inherit from an Object class, my class clsMatrizDb to handle an Array(,); but when I do it generates the following
public objPubMatriz as clsMatrizDb(,)
objPubMatriz (0,0) = 1
I get an error saying that you can't convert from integer to clsXArrayDb
My class is inheriting like this
Public Class clsMatrizDb
Inherits Object
Private vIntRow As Integer
Private vIntColumn As Integer
Public Sub New()
MyBase.New
Me.vIntRow = 0
Me.vIntColumn = 0
End Sub
Public Sub New(ByVal pvIntRow As Integer, ByVal pvIntColumn As Integer)
Me.vIntRow = pvIntRow
Me.vIntColumn = pvIntColumn
End Sub
End Class
I'm missing something?
My comments ended up too long, so here's my explainer:
I agree with cleaning up comments made.
There is no need for Inherits Object. An instance of a class is an Object by default.
The default value of an Integer value is 0 so no need to set this explicitly in the New() sub. If you like, set it explicitly in the variable declarations.
There is no need to call MyBase.New because you are actually not inheriting from a base/abstract class.
You have stated that the error is converting from integer to clsXArrayDb but you don't mention this class at all. Do you mean clsMatrizDb?
The error you are getting is because you are trying to assign the integer value, 1 to an instance (object) of type clsMatrizDb.
If you want to create an instance of clsMatrizDb with Row and Column values initialised as 0, 0 then you just need to declare:
public objPubMatriz as New clsMatrizDb()
If you want to create an instance of clsMatrizDb with different Row and Column values (for example 2 and 4) then you would declare:
public objPubMatriz as New clsMatrizDb(2,4)
To be able to assign an integer value to your clsMatrizDb object, you will either need a default property as described by TnTinMn... which overrides a need for you to have a constructor that takes the Row and Column values. You would code:
public objPubMatriz as New clsMatrizDb()
objPubMatriz(2,4) = 1
The default property would be declared as:
Default Public Property Item(ByVal pvIntRow As Integer, ByVal pvIntColumn As Integer) As Integer
and you would set the values for vIntRow and vIntColumn as part of this property implementation.
If you want to keep the parameterised constructor then you will need to add a Value property to the class:
public property Value as Integer
With usage:
public objPubMatriz as New clsMatrizDb(2,4)
objPubMatriz.Value = 1

Formatting or conversion issue

I have a line of code saving a value to a database. The database class handles the saving of the data and is sound, the code I'm having an issue with is:
With New xdTableName
.Name = "Name"
.DayOfYear = Format(DayOfYear, "000")
.FullString = "Name" & Format(DayOfYear, "000") & Format(Number, "0000")
.Save
End With
All values for the table are saved as string, DayOfYear is an integer at first. I've tried DayOfYear.ToString.PadLeft(3, "0"c), DayOfYear.ToString("D3"), and maybe some others. Number is an integer as well and saves correctly as the left padded value of the integer it represents but for some reason DayOfYear will not save correctly. It should be saving as 012 but it only saves as 12 in both the FullString and the DayOfYear columns. Why? Even when using the same function as the Number value it still doesn't save correctly. The database column is a varchar(3) and shouldn't have any issue holding 012 as the saved data.
Your class only needs 2 properties for this example. Keep numbers as numbers, you can format them as you like when the need to be displayed.
Override ToString for the FullString and just call ToString on the xdTableName. This way, you are not storing redundant data.
Provide a parameterized constructor to easily flesh out a stable xdTableName.
Private Sub Button1_Click() Handles Button1.Click
Dim TN As New xdTableName("Name", Now.DayOfYear)
Debug.Print(TN.ToString)
TN.Save()
End Sub
Public Class xdTableName
Public Property Name As String
Public Property DayOfYear As Integer
Public Sub New(TName As String, TDay As Integer)
Name = TName
DayOfYear = TDay
End Sub
Public Overrides Function ToString() As String
Return "{Name} {DayOfYear:000}"
End Function
Public Sub Save()
'code to save
End Sub
End Class

Initializing Object with Arrays VBA

I'm trying to create a class with arrays in it, and I'm having issues creating the class for it...
CLASS:
Private pST(0 To 2) As String
Public Property Get ST() As String
ST() = pST()
End Property
Public Property Let ST(value() As String) '<---- ERROR HERE
pST() = value()
End Property
CODE RUN:
Sub test()
Dim foo As cPurchaseOrder
Set foo = New cPurchaseOrder
foo.ST(0) = "test"
Debug.Print foo.ST(0)
End Sub
THE ERROR:
Compile error:
Definitions of property procedures for the same property are inconsistent, or property procedure has an optional parameter, a ParamArray, or an invalid Set final parameter.
THE QUESTION:
How can I properly initialize a class with arrays as variables?
EDIT: in relation to Mat's Mug response
CLASS CHANGED:
Private pST As Variant
Public Property Get STContent(ByVal index As Long) As String
STContent = pST(index)
End Property
Public Property Let STContent(ByVal index As Long, ByVal value As String)
pST(index) = value
End Property
Private Sub Class_Initialize()
ReDim pST(0 To 2)
End Sub
CODE RUN TO TEST:
Sub test()
Dim foo As cPurchaseOrder
Set foo = New cPurchaseOrder
foo.STContent(0) = "test" '<--- Type mismatch here
Debug.Print foo.STContent(0)
End Sub
Your getter would need to return a String() array for the types to be consistent:
Public Property Get ST() As String()
However I wouldn't recommend exposing an array like this. First because assigning typed arrays is rather painful, second because the setter (Property Let) is actually cheating here:
Public Property Let ST([ByRef] value() As String)
Unless you specify ByVal explicitly, a parameter is always passed ByRef in VBA... except there's this quirk about Property Let - the RHS/value parameter is always passed ByVal at run-time.
And arrays can only ever be passed ByRef.
Therefore, a property that gets (or assigns, actually) a whole array doesn't make much sense.
A better way would be to encapsulate the array (I'd make it a Variant though), and expose its contents (not the array itself) through an indexed property:
Private internal As Variant 'String array
'...
Public Property Get Content(ByVal index As Long) As String
Content = internal(index)
End Property
Public Property Let Content(ByVal index As Long, ByVal value As String)
internal(index) = value
End Property
You have a lot of issues there.
First, your Property Get needs to return a String array. Second, your array needs to be dynamic, or you need to rewrite the whole thing so that you pass an index value to it, otherwise there is no way to indicate which value you are passing to the array. So, for example, using a dynamic array:
Private pST() As String
Public Property Get ST() As String()
ST = pST
End Property
Public Property Let ST(value() As String)
pST() = value()
End Property
and the calling code:
Sub test()
Dim foo As cPurchaseOrder
Set foo = New cPurchaseOrder
Dim asData() As String
ReDim asData(0)
asData(0) = "test"
foo.ST = asData
Debug.Print foo.ST()(0)
End Sub
Unfortunately, I couldn't be sure form the original what the intent was.
It is getting late here but give it a try. In the module:
Option Explicit
Sub Test()
Dim foo As cPurchaseOrder
Set foo = New cPurchaseOrder
foo.AddValueToSt "test", 1
Debug.Print foo.ST(1)
End Sub
In the Class:
Option Explicit
Private pST
Public Property Get ST() As Variant
ST = pST
End Property
Public Property Let ST(value As Variant)
pST = value
End Property
Public Function AddValueToSt(value As Variant, position As Long)
pST(position) = value
End Function
Private Sub Class_Initialize()
ReDim pST(2)
End Sub
This is my way to use the Factory Method Pattern. When I say "my way", for me this pattern is translated to "Whenever some OOP requires more than 5 minutes of thinking simply add a function."

VB.net staggerred array of different types

I'm creating a Windows application in VB.NET in which needs to store a decent amount of associative information about different applications. I'm trying to find out what the best way to store this data.
After doing some research the best solution that I have found is to use arrays of objects, or dictionaries of objects.
Here is my data structure that needs to be stored:
var Info[AppID]['AppName']-> String (returns the app name as String)
['Exes'] -> Array of Strings
['RegKeys'] -> Array of Strings
['Versions'][VersionID]['PCID'] -> String
['Date'] -> DateTime
['Size'] -> Integer
The keys without quotes are dynamic and represent the actual AppID/VersionID, the keys in quotes are static (so the 2nd key will always have 'AppName' 'Exes' etc.
So for example:
Info[123ab]['Name'] = 'Internet Exploder'
['Exes'] = {'iexplore.exe', 'whatever.exe'}
['RegKeys'] = {'hkey/local machine .....'}
['Versions'][1]['PCID'] = 'My Desktop'
['Date'] = Jan 1 1960
['Size'] = 9001
would be declared and set up as
Dim appinfo As New Dictionary(Of String, Object)
Dim Lv2 As New Dictionary(Of String, Object)
Dim Exes As New List(Of String)(New String() {"iexplorer.exe", "whatever.exe"})
Dim RegKeys As New List(Of String)(New String() {"blah"})
Dim Directories As New List(Of String)(New String() {"c:\program files\internet explorer"})
Dim Name As String = "Internet Exploder"
Dim Versions As New Dictionary(Of String, Object)
Dim VersionsLv2 As New Dictionary(Of String, Object)
Dim VersionID As String = "1"
Dim PCID As String = "My Desktop"
Dim TheDate As Date = Now
Dim Size As Integer = 9001
VersionsLv2.Add("PCID", PCID)
VersionsLv2.Add("Date", TheDate)
VersionsLv2.Add("Size", Size)
Versions.Add("VersionID", VersionsLv2)
Lv2.Add("Name", Name)
Lv2.Add("RegKeys", RegKeys)
Lv2.Add("Directories", Directories)
Lv2.Add("Versions", Versions)
appinfo.Add("abc12", Lv2)
I'm just wondering if anyone knows any better way to do this? I kind of hate having to work from the top of the tree down in order to initialize the variable, but this seems to work ok.
Thank you very much for your input!
It looks like you should be defining two types to begin with, e.g.
Class App
Private ReadOnly _exes As New List(Of String)
Private ReadOnly _regKeys As New List(Of String)
Private ReadOnly _versions As New List(Of Version)
Public Property AppId() As String
Public Property AppName() As String
Public ReadOnly Property Exes() As List(Of String)
Get
Return _exes
End Get
End Property
Public ReadOnly Property RegKeys() As List(Of String)
Get
Return _regKeys
End Get
End Property
Public ReadOnly Property Versions() As List(Of Version)
Get
Return _versions
End Get
End Property
End Class
Class Version
Public Property VersionId() As String
Public Property PcId() As String
Public Property [Date]() As Date
Public Property Size() As Integer
End Class
In that example, the Version objects are stored in an App object in a simple collection and you would get them by index and use LINQ to get one by ID. If you wanted, you could make the Versions property a Dictionary instead. You could then create an array, List or Dictionary to store your App objects.

VBA: Class Modules and Arrays Issue

I've been working on a small project in which I attempted to use class modules through VBA to achieve results.
First Question:
The following statements are from the class module:
Private xRef As Integer
Private yRef As Integer
Private bValue As Boolean
Private NextTiles(1 To 4, 1 To 4) As Boolean
Public Property Get PreviewTiles(ByVal xRef As Integer, ByVal yRef As Integer) As Boolean
PreviewTiles(xRef, yRef) = NextTiles(xRef, yRef)
End Property
Public Property Let PreviewTiles(ByVal xRef As Integer, ByVal yRef As Integer, ByVal bValue As Boolean)
NextTiles(xRef, yRef) = bValue
End Property
In the main submodule body, the following statement exists:
Public P1, P2 As TetrisPlayer
Set P1 = New TetrisPlayer
Set P2 = New TetrisPlayer
...
P1.PreviewTiles(1, 1) = True
MsgBox P1.PreviewTiles(1, 1)
Problem 1-
This returns saying that the value of P1.PreviewTiles(1,1) False when it should be true.
Also second question:
The following code below is based on a seperate submodule, with the collection Players which includes P1 and P2 (from a separate submodule).
Sub TETRIS_Start(FormName As String)
Dim Player As TetrisPlayer
For Each Player In Players
Call TETRIS_GenerateShape(FormName, Player, True)
Next Player
End Sub
Sub TETRIS_GenerateShape(FormName As String, Player As TetrisPlayer, Start As Boolean)
...
This works more-or-less fine (although it encounters problem 1). So I tried to debug with the following statement instead:
Sub TETRIS_Start(FormName As String)
Call TETRIS_GenerateShape(FormName, P1, True)
End Sub
Problem 2 -
This results in the object P1 (publically declared, I even tried to declare it locally) not being able to pass through to the submodule TETRIS_GenerateShape.
The error message that arises is:
Compile Error: ByRef argument type mismatch.
Any suggestions?
This:
Public P1, P2 As TetrisPlayer
isn't doing what you think it is. P1 is now a variant, P2 is a TetrisPlayer. Instead, use:
Public P1 as TetrisPlayer, P2 as TetrisPlayer
Use this in TetrisPlayer instead or the current code:
Public Property Get PreviewTiles(ByVal xRef As Integer, ByVal yRef As Integer) As Boolean
PreviewTiles = NextTiles(xRef, yRef)
End Property
Public Property Let PreviewTiles(ByVal xRef As Integer, ByVal yRef As Integer, ByVal bValue As Boolean)
NextTiles(xRef, yRef) = bValue
End Property
First, set a breakpoint on MsgBox P1.PreviewTiles(1, 1) then run the code to watch what happens.

Resources