Using reflection I need to invoke a method with (of T) parameter - vb.net

I'm trying to make this code with reflection since I want it to manage Technician and other types too.
m_Technician = m_Entities.CreateObject(Of Technician)() 'line#1
m_Technician.IDTechnician = Guid.NewGuid()
m_Entities.AddObject("Technicians", m_Technician)
I used this code with reflection to fill the entity and it work perfectly.
m_Entity = GetType(RFOPSEntities). _
GetMethod(FillMethodName).Invoke(m_Entities, New Object() {uniqueKey})
So I tried something like that for the line #1 :
m_Entity = GetType(RFOPSEntities). _
GetMethod("CreateObject"). _
Invoke(m_Entities, New Object({GetType("Technician")})
I think my difficulty is to pass the (Of Technician)
Thank you

You can use the MakeGenericMethod function to produce a generic MethodInfo from which you can invoke.
m_Entity = GetType(RFOPSEntities). _
GetMethod("CreateObject").MakeGenericMethod(GetType(Technician)). _
Invoke(m_Entities)

Related

Entity Framework 5 is sending back the wrong data

I have a very serious issue with EF sending back the wrong data. I know its wrong because I can run the same query against the same table in management studio and get a completely different result.
I did research the problem and found that some people were able resolve the issue by adding:
*DbContextSummary.Refresh(RefreshMode.StoreWins, result);*
When I try adding that I get this error:
The element at index 0 in the collection of objects to refresh has a null EntityKey property value or is not attached to this ObjectStateManager.
After I got that error I tried to do: DbContextSummary.Attach(result) and I received another error.
What is the proper solution and why does this happen?
Relevant Code (without the refresh call):
Private _dbContextSummary As BudgetEntities
Public ReadOnly Property DbContextSummary() As BudgetEntities
Get
If _dbContextSummary Is Nothing Then
_dbContextSummary = New BudgetEntities
End If
Return _dbContextSummary
End Get
End Property
<WebMethod()> _
Public Function GetSummaryData(ByVal month As String, ByVal year As String, ByVal expenseLine As String, ByVal organization As String) As List(Of SummaryModel)
Dim result As List(Of SummaryModel) = Nothing
result = (From s In DbContextSummary.FPRs _
Where s.FY = year _
Where s.Month = month _
Where s.FPRLine = expenseLine _
Where s.VP = organization
Group s By s.MgrID, s.ProgAdm, s.FinanceNumber, s.FinanceNumberName Into g = Group _
Order By g.FirstOrDefault.MgrLastName, g.FirstOrDefault.ProgAdm Ascending
Select New SummaryModel With { _
.Actual = g.Sum(Function(a) a.Actual), _
.Plan = g.Sum(Function(p) p.Plan), _
.YTDActual = g.Sum(Function(ya) ya.YTDActual), _
.YTDPlan = g.Sum(Function(yp) yp.YTDPlan), _
.FinanceNumber = g.FirstOrDefault.FinanceNumber, _
.FinanceNumberName = g.FirstOrDefault.FinanceNumberName, _
.MgrLastName = g.FirstOrDefault.MgrLastName + ", " + g.FirstOrDefault.MgrFirstName, _
.PlanYearTotal = g.FirstOrDefault.PlanYearTotal, _
.ProgAdm = g.FirstOrDefault.ProgAdm _
}).ToList()
Return result
End Function
What is the proper solution and why does this happen?
Simply put, there is no proper solution in your scenario. Refresh is not applicable at all after you have performed a projection (select new) into a type (SummaryModel) that isn't an entity. Your result collection does not contain entities but SummaryModels. You neither can apply Refresh nor Attach to this collection or its elements. Both methods are intended to work with model entities: Refresh updates properties of an already attached entity from the data store and Attach adds an entity to the context in state Unchanged.
I suggest that you open a new question and describe why you think that "EF is sending back the wrong data" and try to solve this problem. It's unlikely that re-loading provides correct data when loading delivers a wrong result.

Only parameterless constructors and initializers are supported in LINQ to Entities

Help me understand why this isn't working:
Dim i = (From f In EfUtil.Db.EMAILADDRESSHISTORY _
Where f.EMAILADDRESS.CUSTOMERCONTACTPERSON.CUSTOMERguid = New Guid(Request.QueryString("customer")) _
Select New With {.guid = f.ACTIONguid, .name = f.ACTIONname}).ToList
I'm getting the following error
Only parameterless constructors and initializers are supported in LINQ
to Entities.
Altough I'm finding a lot of examples where this construction works. What am I missing?
The examples you are finding most likely are LINQ to Objects examples and not LINQ to Entities.
You can work around this by declaring the GUID beforehand:
Dim customerGuid as Guid = New Guid(Request.QueryString("customer"))
Dim i = (From f In EfUtil.Db.EMAILADDRESSHISTORY _
Where f.EMAILADDRESS.CUSTOMERCONTACTPERSON.CUSTOMERguid = customerGuid _
Select New With {.guid = f.ACTIONguid, .name = f.ACTIONname}).ToList
To be clear: The problem here is not your Select. The problem is the New Guid(...) in the Where condition.

Tidy way of passing ADO.net SqlParameters to a method as an optional list/array/other

I have a very simple ADO.net helper class (copy here) that helped us move from working with classic adodb to ado.net, i.e. usage is very simple:
Public db As New SimpleDataAccess
Public RS As New DataTable
db.ConnectionStringName = "DEV"
db.ConnectDatabase()
db.Execute_Query(RS, "SELECT * FROM whatever WHERE IntColumn = " & tools.parseint(EmployeeID, 0) & " or TextColumn = '" & db.Escape("bla'blabla") & "'")
For Each DB_Row As DataRow In RS.Rows
response.write(DB_Row("IntColumn"))
Next
db.CloseDatabase()
Now whilst this works great it relies on the user to use a parseint() and Escape() function to prevent sql injection etc (and even then in a crude way) - what i would like to do is develop an 2nd version of the Execute_NonQuery() & Execute_Query() functions that accepts SqlParameter()'s
in some way.
What would be the tidiest way to pass mutliple SqlParameter()'s optionally to the Execute_NonQuery() & Execute_Query() methods?
PS: This is used in .net 2.0 & .net 3.5 frameworks only
Edit 1 - My first attempt
In my first attempt ive just given the method a new name (V2), You'll also note that ive added an optional parameter called ParamList which is a List(Of SqlParameter):
Public Function Execute_QueryV2(ByRef TargetDataTable As DataTable, ByVal CommandText As String, Optional ByVal ParamList As List(Of SqlParameter) = Nothing, Optional ByVal CommandType As CommandType = CommandType.Text) As Boolean
If CommandText = "" Then
m_ErrorDesc = "Error running query: Query text was blank"
EmailError(m_ErrorDesc)
Return False
Else
Try
NukeDataTable(TargetDataTable)
m_db_Command = New SqlCommand(CommandText, m_db)
m_db_Command.CommandType = CommandType
If IsNothing(ParamList) = False Then
For i As Integer = 0 To ParamList.Count - 1
m_db_Command.Parameters.Add(ParamList(i))
Next
End If
m_db_DataAdapter.SelectCommand = m_db_Command
m_db_DataAdapter.Fill(TargetDataTable)
m_db_DataAdapter.Dispose()
Return True
Catch ex As Exception
m_ErrorDesc = "Error running query: " & ex.Message & "<br /><br />Query:<hr />" & CommandText
EmailError(m_ErrorDesc)
Return False
End Try
End If
End Function
This means that my usage can be done as follows:
Dim QueryParameters As New List(Of SqlParameter)
QueryParameters.Add(New SqlParameter("#value", Trim(Request.QueryString("value"))))
db.Execute_QueryV2(rs, "SELECT * FROM whatever WHERE value = #value", QueryParameters)
My only problem with this is that any page using my db class will need to include System.Collections.Generic - is there a tider way of doing this that wouldnt require the extra include in all calling pages?
You cannot override a non virtual method. However, you can create a new class derived from SimpleDataAccess class and mark a new method as Shadows. Also, to hide it from the users of your class, mark it with the EditorBrowsable(EditorBrowsableState.Never) attribute. Now, the VS intellisence will not display it and you can create new public methods with the required signature.
Update
I would pass the command object itself. It contains the list of parameters and all required infrastructure. If it is impossible, I would either create a Dictionary which is a dictionary containing pairs parameterName and its value, or a List object containing the list of required parameters with values set.

Implement Generic Interface via CodeDom

The CodeDom is not generating legal VB for me when I try to implement a generic interface.
Here is my VB code to generate the VB code.
Private Sub RunTest()
Dim compileUnit = New CodeCompileUnit
Dim ns As New CodeNamespace()
compileUnit.Namespaces.Add(ns)
ns.Imports.Add(New CodeNamespaceImport("System"))
ns.Imports.Add(New CodeNamespaceImport("System.Collections.Generic"))
Dim fooCollection = New CodeTypeDeclaration("FooCollection")
ns.Types.Add(fooCollection)
fooCollection.TypeAttributes = Reflection.TypeAttributes.Public
fooCollection.IsClass = True
fooCollection.BaseTypes.Add(New CodeTypeReference(GetType(System.Object)))
fooCollection.BaseTypes.Add(New CodeTypeReference( _
"System.Collections.Generic.IEnumerable" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
))
Dim method = New CodeMemberMethod
fooCollection.Members.Add(method)
method.Attributes = MemberAttributes.Private
method.Name = "GetEnumerator"
method.ReturnType = New CodeTypeReference( _
"System.Collections.Generic.IEnumerable" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
)
method.PrivateImplementationType = New CodeTypeReference( _
"System.Collections.Generic.IEnumerable" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
)
Dim provider = New Microsoft.VisualBasic.VBCodeProvider
Dim options = New Compiler.CodeGeneratorOptions
Dim writer = New IO.StringWriter
provider.GenerateCodeFromCompileUnit(compileUnit, writer, options)
Console.WriteLine(writer.ToString)
End Sub
And that will generate:
Public Class FooCollection
Inherits Object
Implements System.Collections.Generic.IEnumerable(Of Foo)
Function System_Collections_Generic_IEnumerable`1_GetEnumerator() _
As System.Collections.Generic.IEnumerable(Of Foo) _
Implements System.Collections.Generic.IEnumerable(Of Foo).GetEnumerator
End Function
End Class
The problem is the name of the function. The tick mark in the function name doesn't make for a legal function name.
It seems that when using the PrivateImplentationType property of the CodeMethodMethod the Name property gets used as the name of the method you are implementing, not the name of the function.
How do you explicitly set the function name or at least how do I get it to be something legal?
To get a compilable output, just use ImplementationTypes: I just changed one line of your code (but I include a simple line for reference, and the corrected ReturnType):
method.Name = "GetEnumerator"
method.ReturnType = New CodeTypeReference( _
"System.Collections.Generic.IEnumerator" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
)
method.ImplementationTypes.Add(New CodeTypeReference( _
"System.Collections.Generic.IEnumerable" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
))
There doesn't seem to be a simple workaround using PrivateImplementationType. Microsoft's code in Microsoft.VisualBasic.VBCodeGenerator.GenerateMethod (which is in a Friend class so not easily overridden) caters for .'s, replacing them with _, but forgets to cater for ` in .NET 2.0-3.5 and (Of <type>) in 4.0. It is probably worth logging a bug at Connect, including pointing out the method name should be separately overridable compared to the method name on the interface being implemented.
You seem to need the functionality of PrivateImplementationType to support the non-generic GetEnumerator. Here is my simple adjustment of your original code to produce a single compilable library, with only warnings about empty code. This code still has "issues", such as the method.Attributes = MemberAttributes.Private being ignored where PrivateImplementationType is used...

How to use late binding to invoke method with ByRef parameters

I have a COM component that I want to call using late-binding from VB.NET (using the painful Primary Interop Assembly - PIA method)
My IDL signature for the COM method looks like:
HRESULT Send([in]BSTR bstrRequestData,
[out]VARIANT *pvbstrResponseData,
[out]VARIANT *pvnExtCompCode,
[out,retval]int *pnCompletionCode);
So 2 'ByRef' parameters in VB.NET lingo, and a return value.
I attempt to invoke this method like so:
Dim parameters(2) As Object
parameters(0) = "data"
parameters(1) = New Object()
parameters(2) = New Object()
Dim p As New ParameterModifier(3)
p(1) = True
p(2) = True
Dim parameterMods() As ParameterModifier = {p}
objReturn = MyObject.GetType().InvokeMember("Send", _
BindingFlags.InvokeMethod, _
Nothing, _
MyObject, _
parameters, _
parameterMods, _
Nothing, _
Nothing)
This fails spectactularly with an exception: {"Invalid callee. (Exception from HRESULT: 0x80020010 (DISP_E_BADCALLEE))"}
I assume this means I'm doing something wrong in my parameterMods array. Because if I comment out setting any value of the ParameterMods array to 'True' - it works. It of course doesnt update the parameters that are [out] parameters and so it's not working as intended.
Is there something else to consider since the method also has a return value? The MSDN example pretty much does exactly what I am doing, with the exception that example did not have a return value. Any help is appreciated.
One issue is that your argument and ParameterModifier arrays have different sizes. I believe they must match up such so that the CLR/BCL can match every argument with a ParameterModifier.
If the PIA was generated with the preserve signature attributes, the method actually has 4 arguments instead of 3. You'll need to extend the arrays to hold 4 members and the return value of pnCompletionCode will be in the last index of the argument array after the call completes.
Also I'm curious why you're using this method of invocation. Since you're using VB.Net why not disable Option Explicit and use the VB late binder. It's much easier than writing out the reflection code yourself (and will typically be a bit more correct because it will deal with weird marshalling rules).
Option Explicit Off
...
Dim obj As Object = DirectCast(MyObject,Object)
obj.Send("data", new Object(), new Object())