Getting a ScriptReference from a ScriptResourceMapping definition in a custom control - asp.net

I am building a custom control with client side scripts that I would like to reference using ScriptManager.ScriptResourceMapping (to make use of the Path and DebugPath attributes).
I would like the custom control to be easily ported to other projects - i.e. I would like to drag and drop the codebehind files (and eventually make the control a separate DLL, but for now the drag and drop will suffice). I would therefore like to avoid (1) having the client script as an embedded resource, (2) referenced as a WebResource in the AssemblyInfo, or (3) have the ScriptManager.ScriptResourceMapping.AddDefinition in global.asax.
In simple terms can I get the script management code to be in just the custom control's code?
At the moment I am getting an error stating that the script reference cannot be found in the assembly, and I guess I am setting the wrong assembly.
My custom control code is as follows:
Public Class MyControl
Inherits System.Web.UI.LiteralControl
Implements ISectionControl, IScriptControl
Private _scriptReference As ScriptReference
Public Sub New()
' Add the resource mapping
ScriptManager.ScriptResourceMapping.AddDefinition("MyControlScript", New ScriptResourceDefinition With {
.ResourceAssembly = System.Reflection.Assembly.GetExecutingAssembly,
.ResourceName = "MyControlScript.js",
.Path = "Path.To.MyControlScript.minimised.js",
.DebugPath = "Path.To.MyControlScript.original.js"
})
' Set the script reference
_scriptReference = New ScriptReference("MyControlScript.js", Assembly.GetExecutingAssembly.FullName)
End Sub
Protected Overrides Sub OnPreRender(e As System.EventArgs)
MyBase.OnPreRender(e)
' Register the script
ScriptManager.GetCurrent(Page).RegisterScriptControl(Of MyControl)(Me)
' Some code to set the Text of the literal control
' ...
End Sub
Public Function GetScriptDescriptors() As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptDescriptor) Implements System.Web.UI.IScriptControl.GetScriptDescriptors
Return New ScriptDescriptor() {}
End Function
Public Function GetScriptReferences() As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptReference) Implements System.Web.UI.IScriptControl.GetScriptReferences
Return New ScriptReference() {_scriptReference}
End Function
End Class
I hope the question makes sense. Thanks for taking the time to read through.
Ali

Answered this myself, I was getting confused with the assemblies and the constructors for ScriptReference. I just wanted a ScriptReference with the (mapped) name so I used the blank constructor and then set Name. I could then remove the assembly information.
Adjusting the following sorted the problem out:
Public Sub New()
' Add the resource mapping
ScriptManager.ScriptResourceMapping.AddDefinition("MyControlScript", New ScriptResourceDefinition With {
.Path = "Path.To.MyControlScript.minimised.js",
.DebugPath = "Path.To.MyControlScript.original.js"
})
' Set the script reference
_scriptReference = New ScriptReference() With {.Name="MyControlScript"}
End Sub

Related

How to register Automapper into vb.net global.asax file?

I've a ASP.NET API Version 2.0 with VB.Net in back end where I'm trying to initialize Automapper in Global.asax file. Here, I'm using Auto Mapper version 5.2. I can initialize using the C# code but I am not so sure about the VB.Net. After googling I've found something and here is what I'm trying now:
Module AutoMapperConfiguration
Public MapperConfiguration As IMapper
Public Sub Configure()
Dim config = New MapperConfiguration(//in this line I'm getting an error:
Overload resolution failed because no accessible 'New' can be called with these arguments: 'Public Overloads Sub New(configurationExpression As MapperConfigurationExpression)': Lambda expression cannot be converted to 'MapperConfigurationExpression' because 'MapperConfigurationExpression' is not a delegate type.
Sub(cfg)
cfg.AddProfile(New EntityMapProfile())
End Sub)
MapperConfiguration = config.CreateMapper()
End Sub
End Module
Then I've called this module from the Application_Start()
AutoMapperConfiguration.Configure()
But last time I've done this using C# with the following line of code in the global.asax file
Mapper.Initialize(x =>
{
x.AddProfile<EntityMapProfile>();
});
Under Application_Start() which worked nicely but now even if I convert those above lines of code then still I'm facing issues. I would appreciate your help or suggestion on the above.
For whatever reason VB.NET is not using the correct constructor when you inline the Sub for the Action.
Module AutoMapperConfiguration
Public MapperConfiguration As IMapper
Public Sub Configure()
Dim configAction As Action(Of IMapperConfigurationExpression) = Sub(cfg) cfg.AddProfile(Of EntityMapProfile)()
Dim config = New MapperConfiguration(configAction)
MapperConfiguration = config
End Sub
End Module
The above will force your lambda to the correct type of Action(Of IMapperConfigurationExpression) and thereby force VB.NET to use the correct constructor overload.

can I pass an aspx page class to a subroutine?

Here's what I'd like to do: Let's say I have a page named "foo.aspx". The class is called "foo". On the page is a checkbox named "bar". I want a subroutine to update that checkbox.
So what I want to write is something like:
In foo.aspx.vb:
partial class foo
... whatever ...
dim util as new MyUtility
util.update_checkbox(me)
In MyUtility
public sub update_checkbox(foo1 as foo)
foo1.bar.checked=true
end sub
But this doesn't work, as Visual Studio doesn't accept "foo" as a class name. Why not? Is there a magic namespace on it, or something else I have to do to identify the class besides say "foo"?
(And yes, I realize that in this trivial example, I could just pass in the checkbox, or move the one line of code into the aspx.vb, etc. My real problem involves setting a number of controls on the form, and I want to be able to do this in a class that has subtypes, so I can create an instance of the proper subtype, then just call one function and set all the controls differently depending on the subtype.)
Update
NDJ's answer works. For anyone else dropping by here, let me add that I was able to do something a little more flexible than his suggestion. I was able to create a property that returns the control itself, rather than some attribute of the control. Namely:
public interface ifoo
readonly property bar_property as literal
end interface
partial class foo
inherits system.web.page
implements ifoo
Public ReadOnly Property bar_property As Literal Implements ITest.bar_roperty
Get
' assuming the aspx page defines a control with id "bar"
Return bar
End Get
End Property
...
dim util=new MyUtility()
util.do_something(me)
...
end class
public class MyUtility
public sub do_something(foo as IFoo)
foo.bar_property.text="Hello world!"
foo.bar_property.visible=true
end sub
end class
This is a bit of a pain as you have to create an interface, and then create a property for each control that you want to be able to manipulate, but it does appear to work.
If there's a way to make the aspx class itself public, this is all unnecessary baggage in most cases. (It might be valuable if you have multiple pages that have controls that you want to manipulate in the same way.) But I can't figure out how to do that.
You can do this, but there are a few hoops to jump through.
Using your example...
If you create an interface with a Boolean property, then implement it in your page, then you can pass the interface about and changing the property will automatically change the checkbox. i.e.
interface:
Public Interface IFoo
Property Bar As Boolean
End Interface
implementation:
Partial Class _Foo
Inherits Page
Implements IFoo
Public Property Bar As Boolean Implements IFoo.Bar
Get
Return Me.CheckBox1.Checked
End Get
Set(value As Boolean)
Me.CheckBox1.Checked = value
End Set
End Property
Then some handler just needs to accept the interface:
Public Module SomeModule
Public Sub SetValues(foo As IFoo)
foo.Bar = True
End Sub
End Module
and the caller from the page passes itself:
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SomeModule.SetValues(Me)
End Sub
You can expose the checkbox as a public property on the page. I don't write in VB.net, but it would look something like this in C#:
Can someone convert this to VB.Net?
public bool MyCheckBoxSetting
{
get { return mycheckbox.Checked; }
set { mycheckbox.Checked = value; }
}

How to set connection string in Linq to SQL constructor

Im trying to set a connection string for a my data access layer which is using a value from my Web.Config file from another project.
I create a new class library and add a dbml (Linq to SQL) file and drag a table.
I then add this code to the code file
Partial Public Class MyDataContext
Public Sub New()
MyBase.New(ConfigurationManager.ConnectionStrings("WebConnectionString").ConnectionString, mappingSource)
OnCreated()
End Sub
End Class
This gave me an error "'Public Sub New()' has multiple definitions with identical signatures."
I understood what the error means so did a quick search on a way around it as when i recompiled the project the same problem remained. The way to approach this was/is to override the OnCreated method so i changed the code to:
Private Sub OnCreated()
Me.New(ConfigurationManager.ConnectionStrings("WebConnectionString").ConnectionString, mappingSource)
End Sub
This gave the error "Constructor call is valid only as the first statement in an instance constructor" but not a lot of ways to overcome it (well i see a few C# examples but im sure im an converting it to the correct VB .Net code)
After further research no matter what i do, i dont seem to be able to set the connection string in my DAL which should be using the Web.Config connection string value from another project.
What could i be doing wrong?
OK, In C# first:
partial class MyDataContext
{
public static MyDataContext Create()
{
return new MyDataContext(ConfigurationManager.ConnectionStrings("WebConnectionString").ConnectionString,
mappingSource);
}
// etc
}
Now, my attempt to translate that in VisualBasic.NET
Partial Public Class MyDataContext
Public Shared Function Create() as MyDataContext
return New MyDataContext (ConfigurationManager.ConnectionStrings("WebConnectionString").ConnectionString,
mappingSource)
End Function
End Class
Called via:
Dim db as MyDataContext = MyDataContext.Create()

Instantiating a class within WCF

I'm writing a WCF WebMethod to upload files to, of which I taken snippets from around the web. The WCF interface looks like this:
<ServiceContract()>
Public Interface ITransferService
<OperationContract()>
Sub UploadFile(ByVal request As RemoteFileInfo)
End Interface
<MessageContract()>
Public Class RemoteFileInfo
Implements IDisposable
<MessageHeader(MustUnderstand:=True)>
Public FileName As String
<MessageHeader(MustUnderstand:=True)>
Public Length As Long
<MessageBodyMember(Order:=1)>
Public FileByteStream As System.IO.Stream
Public Sub Dispose() Implements IDisposable.Dispose
If FileByteStream IsNot Nothing Then
FileByteStream.Close()
FileByteStream = Nothing
End If
End Sub
End Class
Within ASP.NET, when the web method is consumed, for some reason it only works when the interface is used as part of the instantiation of RemoteFileInfo:
Protected Sub btn_Click(sender As Object, e As EventArgs) Handles btn.Click
If fu.HasFile Then
Dim fi As New System.IO.FileInfo(fu.PostedFile.FileName)
' this is the line in question --------------
Dim cu As ServiceReference1.ITransferService = New ServiceReference1.TransferServiceClient()
' -------------------------------------------
Dim uri As New ServiceReference1.RemoteFileInfo()
Using stream As New System.IO.FileStream(fu.PostedFile.FileName, IO.FileMode.Open, IO.FileAccess.Read)
uri.FileName = fu.FileName
uri.Length = fi.Length
uri.FileByteStream = stream
cu.UploadFile(uri)
End Using
End If
End Sub
Can anyone advise why it is not possible to create an instance of TransferService using the following approach:
Dim cu As New ServiceReference1.TransferServiceClient()
If I try the above, it breaks this line:
cu.UploadFile(uri)
...and UploadFile must be called with three parameters (FileName, Length, FileByteStream) even there is no method that uses this signature.
Why is the Interface required when creating an instance of this class please?
When you create the proxy for your service with the "Add Service Reference" dialog, by default the proxy creation code will "unwrap" message contracts, like the one you have. If you want the message contract to appear as you defined on the server side on your proxy, you need to select the "Advanced" tab, and check the "Always generate message contracts" option. With that you'll get the message contract in your client as well.
The issue is that when a MessageContract is encountered as a parameter, the WCF client generation assumes by default that you want to implement a messaging-style interface, and provides the discrete properties from the message contract as part of the client-side interface.
The Using Messaging Contracts article in MSDN contains a very detailed description of what can be done with a messaging contract and I suspect that Microsoft chose this default behavior because of some of the "games" that you can play with the messages.
However, if you examine the code generated for your UploadFile on the client side, there are some interesting tidbits that help to explain what is going on.
The first is the comments for the UploadFile method in the interface:
'CODEGEN: Generating message contract since the operation UploadFile is neither RPC nor document wrapped.
...
Function UploadFile(ByVal request As ServiceReference1.RemoteFileInfo) As ServiceReference1.UploadFileResponse
This implies that the contract would have been generated differently if the message contract had a different implementation.
The second is that you will see that there is nothing special about the code that is used to actually make the service call:
Public Sub UploadFile(ByVal FileName As String, ByVal Length As Long, ByVal FileByteStream As System.IO.Stream)
Dim inValue As ServiceReference1.RemoteFileInfo = New ServiceReference1.RemoteFileInfo()
inValue.FileName = FileName
inValue.Length = Length
inValue.FileByteStream = FileByteStream
Dim retVal As ServiceReference1.UploadFileResponse = CType(Me,ServiceReference1.ITransferService).UploadFile(inValue)
End Sub
So in this case, your code is doing exactly what the generated code does. However, if the MessageContract were more complex, I suspect that this would no longer be the case.
So, for your question:
Can anyone advise why it is not possible to create an instance of
TransferService using the following approach...
There is no reason not to take this approach as long as you verify that the implementation of the method call is functionality equivalent to your code.
There are a couple of options for changing the default generation of the method in the client:
1) Remove the MessageContract attribute from the RemoteFileInfo class.
2) Although it seems to be counter-intuitive, you can check the Always generate message contracts checkbox in the Configure Service Reference Dialog Box.

ASP.NET Server Control - Is shared Viewstate between controls possible

If I have a master 'composite custom server control', with several child custom controls, is it possible, for the master control to share its viewstate with the child controls, which are also custom composite server controls,(all of the controls are composite custom server controls)?
To expand a little more, say I have a Person control, Address control, Phone control and Notes control. Address, Phone and Notes can exist as either independant controls, or as part of the Person control. Since each control is responsible for its own viewstate, and stores required data in it, so it can render/postback etc, it ends up that there is a lage duplication in the viewstate, since the Person control stores all the data, and then each child control stores its own data again. Also, to further complicate things, the Person Control adds the child controls dynamically, so its possible to add a 2nd address/phone number/note etc,which can cause yet a larger viewstate(up to 1MB).
In the case of when all the Address/Phone/etc controls are children of the the Person Control, is it possible for me to somehow share common viewstate data, so I dont have 2/3/4 copies of some stuff in the viewstate, and when they are not components, just act as normal?
I do have one idea for a solution, but its fairly nasty imo, where I could modify the Person control, expose its viewstate or the data, and then in the child control, would check the control hierarchy, and if the child control is part of a Person, dont use to its own viewstate, use the exposed one. This would require me to rework alot of the existing code, which I'd rather avoid if possible.
The first question I'd ask is, "How much of this data do you really need to keep on the ViewState?" For example, is there enough information in a typical page request to be able to rebuild all of the information that is in some of these controls?
I find that ASP.NET typically uses ViewState far more than I really need it to, and if you're getting a 1MB viewstate, I suspect the same is probably true for you. I highly recommend reading this article to get a more full understanding of how ViewState works, what causes values to get saved to ViewState, and how to avoid using it where it's not necessary. You may be able to solve the bulk of your problem simply by doing more of your work in Page_Init instead of Page_Load, for example.
You can "share" a ViewState between any number of objects by passing it around as a StateBag type, or by using delegate functions that return the ViewState that needs to be shared. The usage of this, however; should be limited to very specific circumstances, because typically controls use Properties to expose their ViewState data to other objects (See the link #StriplingWarrior posted here). That being said, here is some example code:
User Control: SharedState.ascx
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="SharedState.ascx.vb" Inherits="TestSite.SharedState" %>
User Control: SharedState.ascx.vb
Public Class SharedState
Inherits System.Web.UI.UserControl
Public Delegate Function GetStateDelegate() As StateBag
Public Const SharedValueKey = "The key used to access the ViewState dictionary"
Public Property GetState() As GetStateDelegate
' Different ways to get values
Public Function GetTypedValue(Of TValue)() As TValue
Return CTypeDynamic(GetValue(), GetType(TValue))
End Function
Public Function GetValue() As Object
' Use the delegate to get the view state from the parent
Return GetState.Invoke()(SharedValueKey)
End Function
Public Function GetValue(state As StateBag) As Object
Return state(SharedValueKey)
End Function
' Different ways to set values
Public Sub SetTypedValue(Of TValue)(value As TValue)
Me.SetValue(value)
End Sub
Public Sub SetValue(value As Object)
GetState.Invoke()(SharedValueKey) = value
End Sub
Public Sub SetValue(state As StateBag, value As Object)
state(SharedValueKey) = value
End Sub
' Set ViewState value using a key and the delegate
Public Sub SetStateWithKey(key As String, value As Object)
GetState.Invoke()(key) = value
End Sub
' Get ViewState value using a key and the delegate
Public Function GetStateWithKey(key As String) As Object
Return GetState.Invoke()(key)
End Function
End Class
Page: SharedStatePage.aspx
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="SharedStatePage.aspx.vb" Inherits="TestSite.SharedStatePage" %>
<%# Register src="SharedState.ascx" tagname="SharedState" tagprefix="uc1" %>
<uc1:SharedState ID="SharedState1" runat="server" />
<uc1:SharedState ID="SharedState2" runat="server" />
Page: SharedStatePage.aspx.vb
Public Class SharedStatePage
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Set up SharedState 1 & 2 to use the delegate '
SharedState1.GetState = AddressOf GetState
SharedState2.GetState = AddressOf GetState
' Set the view state from the page '
ViewState(SharedState.SharedValueKey) = 23
' Read the view state from the user controls '
Dim sharedInt As Integer
sharedInt = SharedState1.GetTypedValue(Of Integer)()
sharedInt = SharedState1.GetValue()
sharedInt = SharedState1.GetValue(ViewState)
sharedInt = SharedState2.GetValue()
' Set the view state from one of the user controls '
SharedState2.SetTypedValue(Of String)("Hello")
Dim sharedStr As String = ViewState(SharedState.SharedValueKey)
sharedStr = SharedState1.GetTypedValue(Of String)()
sharedStr = SharedState2.GetValue()
' Use a different key to set and get view state data '
ViewState("MyKey") = New With {.Name = "Some Object", .Value = 46}
Dim myObj = SharedState1.GetStateWithKey("MyKey")
SharedState2.SetStateWithKey("MyKey", "New Value")
myObj = ViewState("MyKey")
End Sub
Private Function GetState() As StateBag
Return ViewState
End Function
End Class
Use with caution, or don't use with abandon.

Resources