Danger of using reflection to add Connection Strings to ConfigurationManager.ConnectionString - asp.net

I have seen several post detailing how to get around the ReadOnly barrier in adding connection strings to ConfigurationManager.ConnectionStrings. See an example of one such post below. Along of each of these examples comes a dire, yet vague warning that the technique employed is "dangerous". What is the danger?
Dim cssc As ConnectionStringSettingsCollection = WebConfigurationManager.ConnectionStrings
Dim t As Type = cssc.GetType().BaseType ' System.Configuration.ConfigurationElementCollection
Dim fi As FieldInfo = t.GetField("bReadOnly", BindingFlags.Instance Or BindingFlags.NonPublic)
fi.SetValue(cssc, False)

One reason this is 'dangerous' is that you are relying on a private field named bReadOnly. That field is not part of the public .NET API and may change without notice in a future version of .NET. If that happens, and if you upgrade to that version, your code will no longer work.

Related

ASP.NET modify connectionstring at runtime

I need to change dataset connectionstrings to point to different DBs at run time.
I've looked at a number of solutions however they all seem to be related to WinForms or web application projects or other technology slightly different than what I'm using, so I haven't figured out how apply them.
The application is like a discussion. It's a web site project based on code originally written under VS2005, and there's no budget (or personal talent!) for major changes at this time. The app is written in vb.net; I can understand answers in c#. I'm working in VS2013.
The app has three typed datasets pointing to one MDF, call it "MainDB.mdf". There are dozens of tableadapters among the three datasets.
I'm going to deploy the app it as an "alpha/demo" version. I would like to use the same code base for all users, and a separate physical version of MainDB for each user, to reduce chances that the users crash each other.
The initial demo access URL will contain query string information that I can use to connect the user with the right physical database file. I should be able to identify the database name and thus the connection string parameters from the query string information (probably using replace on a generic connection string). If necessary I could use appsettings to store fully formed connection strings, however, I would like to avoid that.
I would like to be able to change the connection strings for all the datasets at the time that the entry point pages for the app are accessed.
Changing the tableadapter connection strings at each instantiation of the tableapters would require too much code change (at least a couple of hundred instantiations); I'd just make complete separate sites instead of doing that. That's the fall back position if I can't dynamically change the connectionstrings at runtime (or learn some other way to make this general scheme work).
Any suggestions on how to approach this would be appreciated.
Thanks!
UPDATE: Per comments, here is a sample instantiation of tableadapter
Public Shared Sub ClearOperCntrlIfHasThisStaff( _
varSesnID As Integer, varWrkprID As Integer)
Dim TA As GSD_DataSetTableAdapters.OPER_CNTRLTableAdapter
Dim DR As GSD_DataSet.OPER_CNTRLRow
DR = DB.GetOperCntrlRowBySesnID(varSesnID)
If IsNothing(DR) Then
Exit Sub
End If
If DR.AField = varWrkprID Then
DR.AField = -1
TA.Update(DR)
DR.AcceptChanges()
End If
End Sub
UPDATE: Below is the test code I tried in a test site to modify the connectionString in a single instantiation of a tableadapter. It feeds a simple gridview. I tried calling this from Page_Load, Page_PreLoad, ObjectDataSource_Init, and Gridview_Databind. At the concluding response.writes, the wrkNewConnString looks changed to TestDB2, and the TA.Connection.ConnectionString value looks changed to TestDB2, but the displayed gridview data is still from TestDB1. Maybe it needs to be called from somewhere else?
Sub ChangeTableAdapter()
Dim wrkNewConnStr As String = ""
Dim wrkSel As Integer
wrkSel = 2
wrkNewConnStr = wrkNewConnStr & "Data Source=.\SQLEXPRESS;"
wrkNewConnStr = wrkNewConnStr & "AttachDbFilename=D:\9000_TestSite\App_Data\TESTDB1.MDF;Integrated Security=True;User Instance=True"
Select Case wrkSel
Case 1
wrkNewConnStr = wrkNewConnStr.Replace("TESTDB1", "TESTDB1")
Case 2
wrkNewConnStr = wrkNewConnStr.Replace("TESTDB1", "TESTDB2")
Case 3
wrkNewConnStr = "Data Source=localhost; Initial Catalog=test01;"
wrkNewConnStr = wrkNewConnStr & " User ID=testuser1; Password=testuserpw1"
End Select
Try
Dim TA As New DataSetTableAdapters.NamesTableAdapter
TA.Connection.ConnectionString = wrkNewConnStr
Response.Write("1 - " & wrkNewConnStr)
Response.Write("<br/>")
Response.Write("2 - " & TA.Connection.ConnectionString)
Catch ex As Exception
Dim exmsg As String = ex.Message
Response.Write(exmsg)
End Try
End Sub
The connection string:
<add name="TestDB1ConnectionString"
connectionString="Data Source=.\SQLEXPRESS;
AttachDbFilename=D:\9000_TestSite\App_Data\TESTDB1.MDF;Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient" />
UPDATE: the following post has lots of solutions, however, they seem to focus on web application projects, that have a project file with settings, which this web site project does not.
link with possible solutions
UPDATE: this next link was brought to my attention, and in working on it I did get it to work, however, it still relies either on having a web application project (with project file) or modifying each table adapter as they are instantiated. So, while I'm not going to implement it, I believe that is the technical answer.
modifying connection strings
sorry if this answer is too late, but I have exactly the same problem and eventually came up with a solution using Reflection.
My solution was to "save" a new default value for the connection string in the settings at run time, which means any further use of the table adapter uses the the new connection string.
It should be noted the term "save" is misleading as the new value is lost when the application closes.
Have tested and worked perfectly.
public void ChangeDefaultSetting(string name, string value)
{
if (name == null)
throw new ArgumentNullException("name");
if (value == null)
throw new ArgumentNullException("value");
Assembly a = typeof({Put the name of a class in the same assembly as your settings class here}).Assembly;
Type t = a.GetType("{Put the full name of your settings class here}");
PropertyInfo propertyInfo = t.GetProperty("Default");
System.Configuration.ApplicationSettingsBase def = propertyInfo.GetValue(null) as System.Configuration.ApplicationSettingsBase;
//change the "defalt" value and save it to memory
def[name] = value;
def.Save();
}

How to use caching?

Basically, I retrieved a dataset and I want to cache it in the server memory for one month. So I don't need to call the query again and again when running the page within this month.
I did some research and think http://msdn.microsoft.com/en-us/library/system.web.caching.cacheitemremovedcallback%28v=vs.110%29.aspx is the way to do the cache, basically i modified the sub codes to fit into my application
Public Sub RemovedCallback(k As String, v As Object, r As CacheItemRemovedReason)
itemRemoved = True
reason = r
End Sub
Function AddItemToCache(cacheKey as String, ds as Dataset)
itemRemoved = False
onRemove = New CacheItemRemovedCallback(AddressOf Me.RemovedCallback)
If (IsNothing(Cache(cacheKey))) Then
Cache.Add(cacheKey, ds, Nothing, DateTime.Now.AddMonths(1), TimeSpan.Zero, CacheItemPriority.High, onRemove)
End If
End Function
There are quite a few errors in this piece of code. One of the error is for Cache(cacheKey) says that " Cache is a type and cannot be used for expression"? where did i do wrong?
Sounds like you're using IIS cache. First of all, if this is your route - if you have an assemblies that may use cache (when available), you need to create Caching assembly in which you check for HTTPcontext. If it is null - you are running outside of IIS and caching will not be available.
A good alternative is to download Enterprise Library Caching blocks if you working with framework up to 3.5. If you use FW4.0+ you use system.runtime.caching. This way cache will be available always. There is also AppFaric and some third party cache implementations but this is probably outside the scope for you.
For your error, it sounds like your identifier Cache [your code is not showing how you assign it] is actually a type. That is if you did this
If Integer Is Nothing....
What you need is to use is syntax
System.Web.Caching.Cache.Add...
Now, this is instance. So, what you can do is
Dim c as Cache = System.Web.Caching.Cache
c.Add(....

How to pass custom sql command to fastreport.net through application using c#?

I start using fastreport.net to generate report in c# since Crystal reports is not compatible with .net4. Its so simple but it is so complicated also.
I try to pass an sql command to my report through my applicate but it doesnt work.Can anyone help me with that ? Here is the Code :
Report rpt = new Report();
rpt.Load("H:\\MyReport.frx");
rpt.SetParameterValue("Parameter", "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Confictionary");
FastReport.Data.TableDataSource data = rpt.GetDataSource("Contact") as FastReport.Data.TableDataSource;
data.SelectCommand = "SELECT * from contact";
rpt.Prepare();
rpt.ShowPrepared();
It throws an exception in line 5 : "Object reference not set to an instance of an object."
Please sb help me.
tnx a lot
I think SQL command should be in report template set, not dynamically, unless this would be used only for the first time for creating report file.
i use this:
FReport.SetParameterValue("SQL_rysys", ConnectionString);
if (FReport.Dictionary.Connections.Count > 0)
{
FReport.Dictionary.Connections[0].ConnectionString = ConnectionString;
FReport.Dictionary.Connections[0].ConnectionStringExpression = "[SQL_rysys]";
FReport.Dictionary.Connections[0].CommandTimeout = 60;
}
This code assigns value for Parameter "SQL_rysys" (SQL_connection in english), i pass SQL connection string. Also if there is any connections in datasource dictionary-i assign connection string to the first one.
So my reports always gets the correct connection string, BUT the SQL query is already included IN REPORT. Cause there is no other way of creating report in designer without using query to get data, and that query is saved in XML report file (frx).
I was asking in fastreport support how to assign query, but their support is AWFUL and noobs works there.

Replacing the obsolete System.Xml.XmlDataDocument?

I have a System.Web.UI.WebControls.Xml control (Xml1) in a webforms app that I have upgraded from .NET 2.0 to .NET 4.0
I am getting two warnings from the code-behind page that I'd like to do something about.
...
Dim ds As DataSet = app.GetObjects
Dim xmlDoc As New System.Xml.XmlDataDocument(ds)
Xml1.Document = xmlDoc
Xml1.TransformSource = "~/xslt/admin_objectslist.xslt"
...
From the second line I get the warning:
'System.Xml.XmlDataDocument' is obsolete: 'XmlDataDocument class will be removed in a future release.'.
And from the third line I get the warning:
'Public Property Document As System.Xml.XmlDocument' is obsolete: 'The recommended alternative is the XPathNavigator property. Create a System.Xml.XPath.XPathDocument and call CreateNavigator() to create an XPathNavigator.
What is the recommended .NET 4.0 replacement for this?
ds.I ran into this problem with 3.5 as well. Here is what I came up with:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(ds.GetXml());
xml1.XPathNavigator = xmlDoc.CreateNavigator();
xml1.TransformSource = #"~/XSLT/LogEntryTransform.xslt";
Hope it helps.
Use Linq2XML - it's way more powerful than any of the other XML tools.... allows you to query and create/read/update/delete (CRUD) the XML just like you would a dataset or other strongly typed data source.
Once you get started with Linq you'll never go back to the old ways... it absolutely rocks!

What's the best way to display an image from a sql server database in asp.net?

I have a sql server database that returns byte for the image. If I use the tableadapter wizard and set it to my stored procedure and preview data, it pulls back an image. It automatically turns it into an image in the preview data. I don't see it as a string of Ints or anything.
How can I display it on my asp.net webpage with a gridview and objectdatasource?
I have searched and foudn where the imagefield can point to a url on another page that does the byte transformation but I'm not sure it's the best. I found another way that creates a temp file.
Just trying to see the best way to do it.
edit - I am trying not to use a temp file. If I cannot use a gridview a regular image field is ok.
asp.net 2.0, c#.
Thank you for any help.
edit
ended up with:
protected void Page_Load(object sender, EventArgs e)
{
string id = Request["id"];
string connstr = "DSN=myserver";
OdbcConnection conn = new OdbcConnection(connstr);
OdbcCommand cmd = new OdbcCommand("{call mySP (?)}", conn);
cmd.CommandType = CommandType.StoredProcedure;
// Add the input parameter and set its properties.
OdbcParameter parameter = new OdbcParameter();
parameter.ParameterName = "#MyParam";
parameter.OdbcType = OdbcType.VarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = id;
// Add the parameter to the Parameters collection.
cmd.Parameters.Add(parameter);
conn.Open();
OdbcDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
byte[] buffer = (byte[])dr[0];
Response.ContentType = "image/jpg";
Response.BinaryWrite(buffer);
Response.Flush();
}
}
and this on the calling page:
<asp:Image ID="Image1" ImageAlign="Middle" ImageUrl="show.aspx?id=123" Runat="server" />
Two options:
Create a temp file - The problem with this approach is that you have to create the file, which means your web must have write access to a directory which is not a great thing. You also need to have a way to clean up the images.
Serve it from another URL - This is my preferred method, as you have no disk access required. A simple http handler (ashx) is a great method to serve up the image.
Edit
If you need session state in the ashx, check out: Asp.net System.Web.HttpContext.Current.Session null in global.asax.
Edit
Couple more thoughts. There are some cases where using a temp file might be better. For example if your images are requested frequently by a lot of users. Then storing the images on the disk would make sense, since you could write the file once, this does increase the maintance complexity but depending on traffic it might be worth it since this would let you avoid calling back into the .net stack and leverage IIS caching of static content.
I wrote the SqlReader plugin for open-source ImageResizing.Net library to allow you to serve and display images from a SQL database in the most performance-optimal way.
Even if you don't need to do any image processing whatsoever, it's still (a) the easiest, and (b) the most efficient way to do it. You can combine it with disk caching (which provides automatic cleanup) to get the best performance that is possible.
Installation is easy - 2 nuget commands, or copy & paste into Web.Config, your pick.
If you need help, support is free and fast.
The sample code you added is good but you should move it to a .ashx file which is meant for such things.
Here is some example code on how to do this.

Resources