SqlFunction fails to open context connection despite DataAccessKind.Read present - c#

I've got a SqlServer project with a very simple test for a Table-Valued-Function:-
[SqlFunction(TableDefinition = "forename nvarchar(50)", FillRowMethodName = "TestFillRow", DataAccess = DataAccessKind.Read)]
public static IEnumerable TestConn(int ID)
{
using (SqlConnection con = new SqlConnection("context connection=true"))
{
//con.Open();
yield return "Anthony";
}
}
public static void TestFillRow(object obj, out string forename)
{
forename = (string)obj;
}
Note the Open on the connection is currently commented out. Once deployed I can execute like this in SQL:-
SELECT * FROM [dbo].[TestConn](1)
All works fine.
Now I uncomment the con.open() and it fails with:-
Data access is not allowed in this
context. Either the context is a
function or method not marked with
DataAccessKind.Read or
SystemDataAccessKind.Read, is a
callback to obtain data from FillRow
method of a Table Valued Function, or
is a UDT validation method.
I don't see what the problem is, the TestConn function has got DataAccessKind.Read.
Anyone know of any other reasons for getting this error?

The problem is the following:
SQLCLR does not allow any data access inside TestFillRow
Even though it "looks" like your TestFillRow doesnt access data, the way the compiler translates code with "yield" statements is by actually deferring it's execution until the first .MoveNext() call to the iterator. Therefore the following statement:
using (SqlConnection con = new SqlConnection("context connection=true"))
gets executed inside TestFillRow... which is illegal.
Do not use yield return; instead load the whole result to a List<> and return the list at the end of the UD Function.

"SQLCLR does not allow any data access inside TestFillRow" is a mistake.
If you don't use context connection = true connetion string you can access data inside FillRow method.

Related

Prevent object dispose inside using block

Workflow:
I have a winform app with two forms, in the 1st form I query a liteDB and it manipulates an IEnumerable<T> instance inside a using block with retrieved data.
IEnumerable<student> searchResult;
using(var db = new LiteDatabase(#"C:\Temp\MyData.db"))
{
var col = db.GetCollection<student>("students");
col.EnsureIndex(x => x.contact.phone);
searchResult = col.Find(x => x.contact.phone == "123456789");
}
Form2 frm2 = new Form2();
Form2.profileData = searchResult.AtElement(index);
Problem:
I then, need to send an element of searchResult<student> to 2nd form in order to show user, as you can see in the last 2 lines of above code.
But since it's inside using block, I get System.ObjectDisposedException.
Data types and exception:
studentCollection.Find():
searchResult:
Exception:
Addition:
What I already though of as possible way is:
Override and nullify existing dispose() method then call my own implemented method after I'm done; Which is basically equals to not having a using block, except that I don't have to take care of disposing other objects in above using block, but only searchResult<student>.
P.S:
I'm newbie at whole thing, appreciate the help and explanation
I'm not familliar with LiteDb, but I would assume it returns a proxy object for the database. So when the database is disposed, the proxy-object is no longer usable.
The simple method to avoid the problem is to add .ToList() after the .Find(...). This will convert the proxy-list to an actual List<T> in memory, and it can be used after the database is disposed. It is possible that the student objects inside the list are also proxies, and if that is the case this will fail.
If that is the case you either need to find some way to make the database return real, non-proxy objects, or extend the lifetime of the database to be longer than that of your form, for example
IList<student> myIList;
using(var db = new LiteDatabase(#"C:\Temp\MyData.db"))
{
var col = db.GetCollection<student>("students");
col.EnsureIndex(x => x.contact.phone);
myIList = col.Find(x => x.contact.phone == "123456789");
using(var frm2 = new Form2()){
frm2.profileData = myIList.AtElement(index);
frm2.ShowDialog(this);
}
}
Note the usage of .ShowDialog, this will block until the second form has been closed. That is not strictly necessary, but it makes it much easier to manage the lifetime of the database.
You need to access the element before exiting the using block.
using(var db = new LiteDatabase(#"C:\Temp\MyData.db"))
{
var col = db.GetCollection<student>("students");
col.EnsureIndex(x => x.contact.phone);
var searchResult = col.Find(x => x.contact.phone == "123456789");
Form2 frm2 = new Form2();
Form2.profileData = searchResult.AtElement(index);
}

c# methods not running in the sequence I wrote them

I got two standard projects in a solution. The UI and the Logic.
As usual, you need to take the inputs from the UI and do whatever you want with them in the back end part.
So in the UI class, I have this
private void btnAddItems_Click(object sender, RoutedEventArgs e)
{
item_name = lbl_item_name.Text;
item_quantity = lbl_item_quantity.Text;
store_ime = store_Name.Text;
logika.storeInDb(store_ime, item_name, item_quantity);
}
It just stores the input in variables and then sends them to this
public void storeInDb(string store_name, string item_name, string item_quantity)
{
using (MySqlConnection mySqlConn = new MySqlConnection(Logic.connStr))
{
dbInsert($"INSERT INTO soping(store_name, item_name, item_quantity, payment_type, date) VALUES('{store_name}', '{item_name}', '{item_quantity}', 'visa', 'danas')");
}
}
And this is the dbInsert method
public void dbInsert(string query)
{
using (MySqlConnection mySqlConn = new MySqlConnection(Logic.connStr))
{
try
{
mySqlConn.Open();
MySqlCommand cmd = new MySqlCommand(query, mySqlConn);
cmd.ExecuteNonQuery();
mySqlConn.Close();
}
catch (MySqlException e)
{
System.Diagnostics.Debug.WriteLine(e);
}
}
}
It doesn't store anything. And when I use breakpoints, it seems like the button method runs after storeInDb, even though the variables in the query are perfectly fine. And I can't find anything wrong with the code that would make it behave weird like this.
This code have some issues:
1- You should use parameters instead of direct strings in your sql query;
2- You don't need a connection outside your dbInsert Method
However, this code should work. I guess the problem you are having is located elsewhere, not in the code you posted here. Something simpler, maybe connectionstring problem (saving in other place where you don't expect to) or bad uses of threads...Maybe hitting deadlocks, long processing or something like that (the only way i can think of having button click apparently happenning after the code it calls).

When Entity Framework Connection Gets Open & Closed

I have an asp.net web forms application it uses EF for all database activities. When page loads I have to fetch lot of data from different tables. I have a DataAccessor class where I am having a member variable of Entity Framework DbContext (MyDBEntities). See class definition below.
public class DataAccessor
{
public static DataAccessor Instance = new DataAccessor();
private MyDBEntities dbEntities = new MyDBEntities();
private DataAccessor()
{
}
public FetchTable_1_Data()
{
return dbEntities.Table1.Where( x => x.Id = something).List();
}
public FetchTable_2_Data()
{
return dbEntities.Table2.Where( x => x.Id = something).List();
}
public FetchTable_n_Data()
{
return dbEntities.TableN.Where( x => x.Id = something).List();
}
}
Using data accessor as below in page load
Page_Load()
{
Repeater1.DataSource = DataAccessor.Instance.FetchTable_1_Data();
Repeater1.DataBind();
Repeater2.DataSource = DataAccessor.Instance.FetchTable_2_Data();
Repeater2.DataBind();
}
My Questions are,
When DB connection is getting open in my case
When DB connection getting closed?
Do I need to use using(MyDBEntities dbEntities = new MyDBEntities()) instead of using member variable
If i would need to use as question #3, do I need to open connection using "using" statement in each fetch methods?
My database connection is broken sometimes and system performance is getting degrade, i suspect my usage of EF. Can someone advice me how to use EF?
Some more questions,
How connection pooling is working with EF?
connectionString="metadata=res:///ReviewsDb.csdl|res:///ReviewsDb.ssdl|res://*/MyDb.msl;provider=System.Data.SqlClient;provider connection string="data source=SERVER;initial catalog=MyDB;persist security info=True;user id=sa;password=mypwd;multipleactiveresultsets=True;application name=EntityFramework"" providerName="System.Data.EntityClient" />
I am using above connection string, do I have connection pool with above connection string?
How can I configure connection in EF
Appreciate any help on these questions.
thank you friends
You dataaccessor is a static member.
Stay away from using static since it may survive between page accesses. It is not hard to imagine what damage that could cause.
I have got database errors from the previous page access when I was doing that. I had to scratch my head a lot.
Also use == when comparing instead of =.
You donĀ“t have to use using.
I would actually advise you to have a data accessor that your Page_Load will create with something like var accessor = new DataAccesssor(); instead of using your approach with a private constructor.
Now it will be clear for you which lifetime your MyDbEntities instance will have. It will be scoped to your Page_Load function.

Nhibernate transaction locks a tabel

I have developed a WCF api which is using nHibernate. I am new to this. I have used session.update to take care of transaction. I have a for loop in which based on select condition I am updating a record ie. If A is present in tabel1 then I am updating the table else inserting a new entry.
I am getting "could not execute query." when trying to execute a select query on a table which was previously being updated by adding a new entry in the table.
What I think is, because I am using session.save(table1) and then trying select entries from that table I am getting an error. Since session.save temporarily locks the table I am not able to execute a select query on that table.
What can be the solution on this?
Update:
This the for loop I am using to check in the database for some field:
using (ITransaction tranx = session.BeginTransaction())
{
savefunction();
tranx.Commit();
}
Save function:
public void savefunction()
{
for (int i = 0; i < dictionary.Count; i++)
{
ICandidateAttachmentManager candidateAttach = new ManagerFactory().GetCandidateAttachmentManager();
CandidateAttachment attach = new CandidateAttachment();
attach = checkCV();
if(attach == null)
{
//insert new entry into table attach
session.save(attach);
}
}
}
checkCV function:
public void checkCV()
{
using (ICandidateAttachmentManager CandidateAttachmentManager = new ManagerFactory().GetCandidateAttachmentManager())
{
IList<CandidateAttachment> lstCandidateAttachment = CandidateAttachmentManager.GetByfkCandidateId(CandidateId);
if (lstCandidateAttachment.Count > 0)
{
CandidateAttachment attach = lstCandidateAttachment.Where(x => x.CandidateAttachementType.Id.Equals(FileType)).FirstOrDefault();
if (attach != null)
{
return null;
}
else
{
return "some string";
}
}
}
}
What happening here is in the for loop if say for i=2 the attach value comes to null that I am entering new entry into attach table. Then for i=3 when it enters checkCV function I get an error at this line:
IList lstCandidateAttachment =
CandidateAttachmentManager.GetByfkCandidateId(CandidateId);
I think it is because since I am using session.save and then trying to read the tabel contents I am unable to execute the query and table is locked till I commit my session. Between the beginTransaction and commit, the table associated with the object is locked. How can I achieve this? Any Ideas?
Update:
I read up on some of the post. It looks like I need to set isolation level for the transaction. But even after adding it doesn't seem to work. Here is how I tried to inplement it:
using (ITransaction tranx = session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
saveDocument();
}
something I don't understand in your code is where you get your nHibernate session.
Indeed you use
new ManagerFactory().GetCandidateAttachmentManager();
and
using (ICandidateAttachmentManager CandidateAttachmentManager = new ManagerFactory().GetCandidateAttachmentManager())
so your ManagerFactory class provides you the ISession ?
then you do:
CandidateAttachment attach = new CandidateAttachment();
attach = checkCV();
but
checkCV() returns either a null or a string ?
Finally you should never do
Save()
but instead
SaveOrUpdate()
Hope that helps you resolving your issue.
Feel free to give more details

How can i use response.redirect from inside a function defined in Class file in c# 3.0

I have a simple function GetPageName(String PageFileName, String LangCode) defined inside a class file. I call this function from default.aspx.cs file, In this function I am not able to use Response.Redirect("Error.aspx") to show user that error has been generated.
Below is example of Code
public static string GetPageName(String PageFileName, String LangCode)
{
String sLangCode = Request("Language");
String pgName = null;
if ( sLangCode.Length > 6)
{
Reponse.Redirect("Error.aspx?msg=Invalid Input");
}
else
{
try
{
String strSql = "SELECT* FROM Table";
Dataset ds = Dataprovider.Connect_SQL(strSql);
}
catch( Exception ex)
{
response.redirect("Error.aspx?msg="+ex.Message);
}
}
return pgName;
}
I have may function defined in Business and Datalayer where i want to trap the error and redirect user to the Error page.
HttpContext.Current.Response.Redirect("error.aspx");
to use it your assembly should reference System.Web.
For a start, in one place you're trying to use:
response.redirect(...);
which wouldn't work anyway - C# is case-sensitive.
But the bigger problem is that normally Response.Redirect uses the Page.Response property to get at the relevant HttpResponse. That isn't available when you're not in a page, of course.
Options:
Use HttpContext.Current.Response to get at the response for the current response for the executing thread
Pass it into the method as a parameter:
// Note: parameter names changed to follow .NET conventions
public static string GetPageName(String pageFileName, String langCode,
HttpResponse response)
{
...
response.Redirect(...);
}
(EDIT: As noted in comments, you also have a SQL Injection vulnerability. Please use parameterized SQL. Likewise showing exception messages directly to users can be a security vulnerability in itself...)

Categories

Resources