I've been developing an app which uses strongly typed datasets and stored procedures. I've just graduated and this was the method that was sold to us as the way to go. I'm starting to have severe doubts.
My client has advised me that he might change from SQL Server to MySQL. From what I've read it might be better to not use stored procedures as migrating could become more difficult. So anyhow I've just implemented a new table adapter query using the wizard and selected Use SQL Statements rather than Create new stored procedure.
My call to the query
Intranet.administratorsDataTable dt = taAdministrators.GetAdministrators();
now generates this error:
executereader requires an open and available connection. the
connection's current state is closed
I have no idea why this auto generated code doesn't have a connection and I'm hungover and in no shape to deal with this. I decided to just go back to the SP's for the moment so I can get some work done. This error is still being thrown (same table adapter, same method name but reconfigured to use a SP). All of my other DB calls work fine.
I'm assuming the generated SQL code is still floating around somewhere even though I changed the adapter to use SP's. Can someone tell me where it is so I can delete it?
On another note I'm really starting to think that using SqlConnection and SqlCommand manually is a much better option, as using these query 'Tools' are just way to much trouble when it comes to flexibility such as modifying database tables etc. Can any of you more experienced people tell me if that's correct or do you advocate using table adapters?
*Edit
it also throws these:
Invalid operation. The connection is closed.
and
There is already an open DataReader associated with this Command which must be closed first.
The solution was to go to the query properties in the tableAdapter and manually change the "Command Type" to StoredProcedure.
highlight the query > go to properties window > change the command type
Seem this didn't (or doesn't) get auto updated when I reconfigure the query.
if you provide some code, it would be better.
I think, you need to open the the connection.
SqlCommand Cmd= new SqlCommand();
Cmd.Open();
// then u can use Cmd.ExecuteReader();
OK, I think I'm getting mad here...
I thought this should be super simple, but I just can't figure out how to do that.
This is what I'm trying to do:
I want to create an rdlc report using the ReportViewer control in ASP.NET 4 (VS 2010), and, as its datasource, use a class with some properties.
I tried everything I can think of, but I just can't figure this out. All the docs I found state that the object should appear in the DataSource pane of the website, but I can't make it appear there.
I would like the fields of the class to appear in the report desiger so I can use them - but I can't do that either.
Using the designer, I can only define new dataset - I don't want to use dataset, but business objects!
So - how can I do that? Do I have to use some kind of DataSource control? How can I make the report designer know about the business object?
Thanks!
Memi
did you follow this tutorial?
everything you must do is:
define your DTO classes or generate it using EF4 (for example)
define your business classes with some methods (like GetAll...)
build your solution (that's important)
now from your report designer you can choose methods from business classes as dataset and drag and drop field from the DTO classes
when you choose that report to display in the reportviewer, the datasource object will be added for you
I found this blog very helpful.
When you create a new datasource for your rdlc, in the Dataset Properties dialog:
1) In the Data source drop down, select the namespace that contains the class which contains the public method (see #2).
2) In the Available datasets drop down, select the public method that returns an IQueryable of your business objects.
Is your business object class marked as public? I've seen in a video that it must be public.
I have the same problem and found a way around it. For some reason if you develop a ASP.NET application Microsoft took away add new datasource functionality. The way around is not great but it does work. I use all objects and I use the Enterprise library and I want to use my objects for my reports it only makes sense why they don't enable you to do this. I have no idea why Microsoft would not allow this functionality for web apps.
But that leaves windows apps it works so what I did was create a separate windows project include my objects that I want to bind to in that project and create the report on the forms project. I then bring that report into my Asp.net web app and call it through code. Here is a few pieces of code that I use to do this. This is in VB but could be converted to C#. I also have a drop down list that selects the report that is needed and a case statement that gets the data.
Private Sub LoadReport()
Try
pnlReport.Visible = True
Dim Dal As New DataAccess
Dim objRptOutputData = New Model.RptClientCollection
Dim lr As LocalReport = OutputReportViewer.LocalReport
Dim rds As New ReportDataSource
lr.DataSources.Clear()
OutputReportViewer.Visible = True
OutputReportViewer.ProcessingMode = ProcessingMode.Local
OutputReportViewer.LocalReport.EnableHyperlinks = True
Dim SelectedReport As Integer = 0
If Me.ddlReport.SelectedItem.Value IsNot "" Then
SelectedReport = Me.ddlReport.SelectedItem.Value
End If
Select Case SelectedReport
Case ConstantEnum.Reports.ActiveWaitingList
objRptOutputData = Dal.GetRptClientsByStatus(ConstantEnum.Status.ActiveWaitingList)
lr.ReportPath = "Reporting\Report1.rdlc"
rds.Name = "dsClient"
rds.Value = objRptOutputData
Me.lblCount.Text = "Count: " & objRptOutputData.Count
Case ConstantEnum.Reports.InactiveWaitingList
' This is a small app I have about 15 case statements if it was bigger I would of done this selection a bit different.
End Select
lr.DataSources.Add(rds)
lr.Refresh()
OutputReportViewer.DataBind()
Catch ex As Exception
ExceptionUtility.SendError(ex, "Reports", "LoadReport")
End Try
End Sub
Have you seen this earlier version? Is this what you need:
http://msdn.microsoft.com/en-us/library/ms252073(v=VS.80).aspx
The ASP.NET web application I am developing needs to support two different types of databases, namely Access and MS SQL Server 2008 Express.
I already have connection strings for each database type stored in web.config, and I have another web.config value that specifies which one to use. So I can get the proper connection string with no problem.
The big problem comes with the database objects. For Access, which I have already implemented, I am using the objects OleDbConnection, OleDbCommand and OleDbDataReader in the code to make the database calls.
It appears that for SQL Server, I can't use those objects, but rather I would need to use the objects SqlConnection, SqlCommand and SqlDataReader to do essentially the same things.
I want to reuse as much of my current code as possible and not have to create two separate blocks for each database type. (I have a lot of methods that take an OleDbDataReader as a parameter - I do not want to have to make 2 of each of those methods, for example.)
I noticed that the connection objects both inherit from DbConnection. And the same is true for the data readers (DbDataReader) and the commands (DbCommand).
Would it be possible to take my existing code for Access, replace all of the Ole objects with the Db objects, and then cast those objects as the proper type depending on the current database type?
Are there any best practices for supporting two database types in one ASP.NET application?
I can add some of my code if that would help. Thanks.
Yes, from framework 2.0 all data readers inherit from the DbDataReader class, so your methods could take a DbDataReader isntead of an OleDbDataReader, and you could use the methods with any database.
However, the databases have different dialects of SQL, so you either have to stay on a narrow path of features that work in all databases that you use, or have separate queries for some tasks.
A specific example of differences is that Access uses data literals like #2010-09-24# while SQL Server uses date literals like '2010-09-24'. Generally most that has to do with dates differs.
The link you're likely missing is the functionality of the DbProviderFactories class. Using this class (and associated helpers also in System.Data.Common), you can abstract the provider and use references to the base classes (such as DbConnection and DbCommand) to do the work. It'd look something like this:
private void DoSomething(string provider, string connectionString, string something)
{
DbProviderFactory factory = DbProviderFactories.GetFactory(provider);
DbConnection connection = factory.CreateConnection();
connection.ConnectionString = connectionString;
DbCommand command = connection.CreateCommand();
command.CommandText = something;
DbDataReader reader = command.ExecuteReader();
//etc...
}
The provider name is a bit tricky to acquire, but should be the invariant class name that matches one of those returned by DbProviderFactories.GetFactoryClasses(). Or, you can simply hard code them. They don't change much, but it is a magic string embedded in your code and may cause issues eventually.
Additional features can be accessed through factory.CreateCommandBuilder that can help you traverse the differences in how the providers handle things like parameters and such.
I am working on a content management system which is being sort of retrofitted onto an existing database, and the database has many many tables. There will be a staging database, where we will make changes and allow users to 'preview in place'. Then any changes have to be approved, and to publish them we will connect to a live version of the same database (same schema) and play-forward the captured changes.
I have found some code (called Doddle Audit) which, with some customization, is giving me great information about what is changing. I am able to get a list of all columns, before and after, for updates, inserts, and deletes. But what I would really like to have is the underlying SQL being run by SubmitChanges(). LinqToSql has to generate this, so why can't I have it?
I have googled around and looked at code involving SubmitChanges, mousing over stuff, and I can't seem to find it. Does anyone know of a way to obtain this?
Use the DataContext.Log property like this:
using(DataContext dc = new DataContext()){
StringBuilder sb = new StringBuilder();
dc.Log = new StringWriter(sb);
}
You will see the generated query in debug mode.
Linq To Sql Profiler. It does that and a whole lot more.
You can try this:
Console.WriteLine(context.GetCommand(query).CommandText);
Hope this helps.
Thanks,
Raja
You could use SQL Server Profiler, not as good as Linq To SQL Profiler, but free.
To get the sql-statement in a string, you can do something like this:
using (var context = new MyDataContext())
{
var query = from p in context.Persons
select p;
string sql = context.GetCommand(query).CommandText;
}
Currently I run an classic (old) ASP webpage with recordset object used directly in bad old spagethi code fasion.
I'm thinking of implementing a data layer in asp.net as web serivce to improve manageability. This is also a first step towards upgrading the website to asp.net.
The site itself remains ASP for the moment...
Can anybody recommend a good way of replacing the recordset object type with a web service compatible type (like an array or something)?
What do I replace below with?:
set objRS = oConn.execute(SQL)
while not objRS.eof
...
name = Cstr(objRS(1))
...
wend
and also mutliple recordsets can be replaced with?
I'm talking :
set objRS = objRs.nextRecordset
Anybody went through this and can recommend?
#AdditionalInfo - you asked for it :-)
Let me start at the beginning.
Existing Situation is:
I have an old ASP website with classical hierachical content (header, section, subsection, content) pulled out of database via stored procedures and content pages are in database also (a link to html file).
Now bad thing is, ASP code everywhere spread over many .asp files all doing their own database connections, reading, writing (u have to register for content). Recently we had problems with SQL injection attacks so I was called to fix it.
I could go change all the .asp pages to prevent sql injection but that would be madness. So I thought build a data layer - all pages using this layer to access database. Once place to fix and update db access code.
Coming to that decision I thought asp.net upgrade isn'f far away, why not start using asp.net for the data layer? This way it can be re-used when upgrading the site.
That brings me to the questions above!
First my favorite advice of this week: do not treat your Web Service like it if was a local object or you are going to pay a very hefty performance price. Essentially, don't do things like this in your web application:
MyDataWebService ws = new MyDataWebService();
foreach(DataItem item in myData)
{
ws.Insert(item);
}
You should always prefer to minimize calls to your Web Service (and SQL):
MyDataWebService ws = new MyDataWebService();
ws.Insert(myData); // Let the web service process the whole set at once.
Now, as far as the data type to use for your web service calls, you basically have two choices:
DataSet
Everything else (Array)
Most collections returned from a web service (like a List<MyData>) actually convert to an Array during the Web Service invocation. Remember that Web Services don't return objects (data + behavior) but just data structures (or a sequence of). Therefore, there is little distinction between a List and an Array.
DataSets are more complex classes; they use their own custom serializer and pretty much get fully recreated in the calling application. There is a cost in performance to be paid for using DataSets like that, so I don't usually recommend it for most scenarios. Using arrays to pass data back and forth tends to be more efficient, and quite frankly it's easier to do.
Your case is a bit different; because you are converting an existing site that already uses ADO, an ADO.NET DataSet might be your best updgrade path. ADO.NET and ADO are similar enough that a straight update might be easier that way. It kind of depends how your web site is built.
For the last part of your question, DataSets do support multiple recordsets similar to ADO's Recordset. They are called DataTables. Every DataSet has at least one DataTable and you can read them in any order.
Good luck.
If you wanted to stick with Classic ASP then I would suggest creating a Database handling object via ASP Classes then just use that object to do your recordset creations. This would centralize your database handling code and make it so that you only have to handle SQL Injection attacks in a single location.
A simple example.
Class clsDatabase
Private Sub Class_Initialize()
If Session("Debug") Then Response.Write "Database Initialized<br />"
End Sub
Private Sub Class_Terminate()
If Session("Debug") Then Response.Write "Database Terminated<br />"
End Sub
Public Function Run(SQL)
Set RS = CreateObject("ADODB.Recordset")
RS.CursorLocation = adUseClient
RS.Open SQLValidate(SQL), Application("Data"), adOpenKeyset, adLockReadOnly, adCmdText
Set Run = RS
Set RS = nothing
End Function
Public Function SQLValidate(SQL)
SQLValidate = SQL
SQLValidate = Replace(SQLValidate, "--", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, ";", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, "SP_", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, "##", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, " DECLARE", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, "EXEC", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, " DROP", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, " CREATE", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, " GRANT", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, " XP_", "", 1, -1, 1)
SQLValidate = Replace(SQLValidate, "CHAR(124)", "", 1, -1, 1)
End Function
End Class
Then to use this you would change your calls to:
Set oData = new clsDatabase
Set Recordset = oData.Run("SELECT field FROM table WHERE something = another")
Set oData = nothing
Of course you can expand the basic class to handle parametrized stored procedures or what not and more validations etc.
I'd suggest using the XmlHttp class in your ASP code.
Assuming you have an ASMX web service similar to this, in MyService.asmx:
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
You could call it in ASP something like this:
Dim xhr
Set xhr = server.CreateObject("MSXML2.XMLHTTP")
xhr.Open "POST", "/MyService.asmx/HelloWorld", false
xhr.SetRequestHeader "content-type", "application/x-www-form-urlencoded"
xhr.Send
Response.Write(xhr.ResponseText)
ResponseText would be an XML response of:
<string>Hello World</string>
Assuming your service returned a collection of data, you could iterate over it using XPath or any other XML processing technique/library.
Googling around about MSXML2 will probably answer any specific questions you have, since it's specific to ASP classic.
Instead of thinking in layers, why not try taking vertical slices through the application and converting those to .net. That way you will get entire features coded in .net instead of disjoint parts. What is the business value in replacing perfectly working code without improving the user experience or adding features?
You might also consider the trade-off of performance you are going to give up with a Web Service over direct ado calls. Web Services are a good solution to the problem of multiple disjoint applications/teams accessing a common schema; they do not make a single isolated application more maintainable, only slower and more complex.
Another alternative is to use COM Interop to create an assembly in .NET that is callable from classic ASP.
To create a COM Interop assembly from Visual Studio (e.g. Microsoft Visual C# 2005 Express Edition):
Create a new Class Library project
Open the project properties
Under Application select Assembly Information... and enable "Make assembly COM-Visible"
Under Signing enable Sign the assembly and create or select an existing strong name key file
Write and build the library
COM Interop classes must have a default constructor and only non-static classes and methods are published
Copy the .dll to the desired folder/machine
Register the .dll for COM using RegAsm
For example (adjust as necessary):
"C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe" "C:\path\to\assembly.dll" /tlb /codebase
Call the assembly from ASP
For example (adjust as necessary):
Dim obj, returnValue
Set obj = Server.CreateObject("MyProject.MyClass")
returnValue = obj.DoSomething(param1, param2)
Note:
the assembly must be re-registered via RegAsm when it's updated
See also:
Exposing .NET Framework Components to COM (MSDN)
CodeProject: Call a .NET component from an ASP Page
Sql injection should be handled by using parametrized sql queries. Not only will this eliminate the security risk but it will significantly speed up your database performance because it will be able to reuse an execution plan instead of recalcing it every time. The suggestion to handle it through string replacements is foolish. VB is terrible at handling strings and those "replace" statements will be extremely costly in performance and memory (also, you actually only need to handle the ' character anyway)
Moving code to .net doesn't make it better. Having db code in your pages isn't bad; especially if id you are talking about a small site with only a couple devs. Thousands of sites use that technique to process bazillions of dollars in transactions. Now, unparameterized dynamic sql is bad and you should work to eliminate that, but that doesn't require a rewrite of the app or .net to do it. I'm always curious why people see .net as a defacto improvement to their app. Most of the bad code and bad habits that existed in the COM model just propagate forward during a conversion.
You either need to make a commitment to creating a truly cohesive, minimally coupled, OO design; or just keep what you have going because it isn't all that bad.
Too bad I did not see this question in 2008.
For me it looks like your site is using Justa framework.
Simple way is to modify Justa code for submit of search and data inputs to urlencode.
I did it and work perfectly for me.
Rest of the code secure enough to prevent any type os SQL injections or other attempt to get into database.