Usage of 'using' causes ObjectDisposedException in creating list of EventData - c#

I normally send data to event hub as follows..
var encoded = Encoding.UTF8.GetBytes(serializedString);
using (var edata = new EventData(encoded) { PartitionKey = mypkey })
{
edata.Properties[EventDataPropertyKeys.MyKey] = myvalue;
await _eventclient.SendAsync(edata).ConfigureAwait(false);
}
Today I thought of trying to send the data via batch and tried to create a list of EventData objects as follows..
List<EventData> eventDataList = new List<EventData>();
//in a loop
var encoded = Encoding.UTF8.GetBytes(serializedString);
using (var edata = new EventData(encoded) { PartitionKey = mypkey })
{
edata.Properties[EventDataPropertyKeys.MyKey] = myvalue;
eventDataList.Add(edata);
}
But when I check the eventdatalist objects, I find SerializedSizeInBytes property of EventData object showing
'This eventdata instance has already been disposed'
and while accessing throws..
'eventData.SerializedSizeInBytes' threw an exception of type 'System.ObjectDisposedException'
Any help is sincerely appreciated..
Thanks

Because in the first code snippet, you send edata inside the using block. But in the second snippet, you put edata in a list, and after that, loop through the list and send each item after the using block, where the item edata is already disposed.

Related

Error: DbContext has been disposed

public JsonResult JTask(int id)
{
using (TestDb db = new TestDb())
{
var a = db.ToDos.Where(todo => todo.UserId == id);
return Json(a, JsonRequestBehavior.AllowGet);
}
}
I have a problem with returning JsonResult
When I run this code code I get the error
"The operation cannot be completed because the DbContext has been
disposed."
I tried adding .ToList() at the end of the line 3, as was suggested, but then I got the error
"A circular reference was detected while serializing an object of type
System.Data.Entity.DynamicProxies."
It's not very obvious, but the built-in Json method only does the serialization after the JTask method has finished executing. By that time, of course, the context has been disposed, resulting in the original error you are describing.
If you have an ICollection<TodoItem> property inside your Todo class, each of those will have a ToDo property which is a reference back to the parent. And each of those ToDo properties will also have ICollection<TodoItem> children, which has a reference back to the parent again, and so on and so forth. This can potentially loop for infinity, and when the serializer tries to serialize the object, it gives up with a circular reference error.
One way to solve both of these problems at the same time is by using viewmodels. A viewmodel is an intermediate class that holds only a subset of the properties that a model class has. The typical flow is for the model class to get converted to a viewmodel first, then it would be the viewmodel that gets serialized as json:
var viewModels = new List<TodoViewModel>();
using (TestDb db = new TestDb())
{
var todoModels = db.ToDos.Where(todo => todo.UserId == id).ToList();
foreach (var model in todoModels)
{
var todoViewModel = new TodoViewModel
{
// Populate viewmodel properties here
Text = model.Text
};
viewModels.Add(todoViewModel);
}
}
return Json(viewModels, JsonRequestBehavior.AllowGet);
I wrote a blog post about the advantages of using viewmodels. You can check it out here if you're interested: Why Use ViewModels
Because Linq is Lazy by the time the JSON tries to get the data out of a (and only then actually goes to db) the db has already been disposed - when leaving the scope of the using
public JsonResult JTask(int id)
{
using (TestDb db = new TestDb())
{
var a = db.ToDos.Where(todo => todo.UserId == id).ToList();
return Json(a, JsonRequestBehavior.AllowGet);
}
}
var a = db.ToDos.Where(todo => todo.UserId == id).ToList();

using Statement And ADO.NET Objects

Consider the following method...
private string[] GetExistingPOWSNumbers()
{
using (
var OleAdapter =
new OleDbDataAdapter(
"SELECT DISTINCT con_num FROM `Items` WHERE con_num LIKE 'POWS%'",
ConfigurationManager.ConnectionStrings["MPOleConnectionString"].ConnectionString))
using (var POWSTable = new DataTable())
{
OleAdapter.SelectCommand.Connection.Open();
OleAdapter.Fill(POWSTable);
return POWSTable.AsEnumerable().Select(row => Convert.ToString(row["con_num"])).ToArray();
}
}
Are all the ADO.NET objects promptly being disposed of? I am using this method throughout my projects and when there are A LOT of calls like these being made during a single action, I receive "Out of Memory" errors.
EDIT: After some personal investigation I discovered that, in fact, the using statement for the adapter DOES NOT also close the provided collection. I made the following change. Now I am using a DataReader instead of populating a DataTable.
private string[] GetExistingPOWSNumbers()
{
var Results = new List<string>();
using (var OleConnection = new OleDbConnection(ConfigurationManager.ConnectionStrings["MPOleConnectionString"].ConnectionString))
using (
var OleCommand =
new OleDbCommand(
"SELECT DISTINCT con_num FROM `Items` WHERE con_num LIKE 'POWS%'",
OleConnection))
{
OleConnection.Open();
using (var OleReader = OleCommand.ExecuteReader(CommandBehavior.CloseConnection))
{
if (OleReader == null) return new string[0];
while (OleReader.Read())
{
Results.Add(OleReader.GetString(0));
}
}
}
return Results.ToArray();
}
Objects will be cleaned up when they are no longer being used and when the garbage collector sees fit. Sometimes, you may need to set an object to null in order to make it go out of scope (such as a static field whose value you no longer need), but overall there is usually no need to set to null.
Regarding disposing objects, I agree with #Andre. If the object is IDisposable it is a good idea to dispose it when you no longer need it, especially if the object uses unmanaged resources. Not disposing unmanaged resources will lead to memory leaks.
You can use the using statement to automatically dispose an object once your program leaves the scope of the using statement.
using (MyIDisposableObject obj = new MyIDisposableObject())
{
// use the object here
} // the object is disposed here
Which is functionally equivalent to:
MyIDisposableObject obj;
try
{
obj = new MyIDisposableObject();
}
finally
{
if (obj != null)
{
((IDisposable)obj).Dispose();
}
}
Got this from Zach Johnson here: Credit Due

Modify instance variable used by multiple Tasks

I need some help with handling Tasks. I have an XML String which is deserialized into a class. The class itself contains a property, e.g. rssProvider which is set to an unique value like MSN or YAHOO. However there can be multiple values delimited with an , in this field.
I am using this deserialized class instance in multiple Tasks. However the function which gets called in this task can only work with one rssProvider value, so I have a split on that string and a foreach loop which creates the task.
In the task itself the function I call needs the full object but with one rssProvider. However when I change the value of that property to the value in the foreach the other tasks will fail as they will get the same single value from the task which runs first.
Any ideas how I should restructure the logic? Thanks!
My code:
List<Task<ResponseOBJ>> tasks = new List<Task<ResponseOBJ>>();
// get List of rssProviders
string[] providers = request.requestBody.rssProvider.Split(',');
//go through each provider
foreach (string provider in providers)
{
Task<ResponseOBJ> task = Task.Factory.StartNew<ResponseOBJ>(() =>
{
request.requestBody.rssProvider = provider;
doStuff(request);
}
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
I would create a copy constructor in your Request object which copies the content of the original Request and creates a fresh one:
public class Request
{
public Request(Request oldRequest)
{
// initalize new request from the old
}
}
And then change my code to create a new request per task:
List<Task<ResponseOBJ>> tasks = new List<Task<ResponseOBJ>>();
// get List of rssProviders
string[] providers = request.requestBody.rssProvider.Split(',');
//go through each provider
foreach (string provider in providers)
{
Task<ResponseOBJ> task = Task.Factory.StartNew<ResponseOBJ>(() =>
{
request.requestBody.rssProvider = provider;
var newRequest = new Request(request);
doStuff(newRequest);
}
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
One option might be to change doStuff(request) to doStuff(request, provider) and remove the line request.requestBody.rssProvider = provider;, and then change your doStuff accordingly.
foreach (string provider in providers)
{
Task<ResponseOBJ> task = Task.Factory.StartNew<ResponseOBJ>(() =>
{
doStuff(request, provider);
}
tasks.Add(task);
}
Another option (as also mentioned in the above comments) is to create a new request object for each provider.

Cannot access a disposed object

i'm facing a issue while testing the DAL Library which uses LINQ to SQL
Method that is being Tested as below (a simple one):
public List<tblAccount> GetAccountsByCustomer(tblCustomer customer)
{
using (OnlineBankingDataClassesDataContext dbcntx = new OnlineBankingDataClassesDataContext())
{
var accounts = dbcntx.tblAccounts.Where(p => p.tblCustomer.ID.CompareTo(customer.ID)==0);
return accounts.ToList<tblAccount>();
}
}
Test code is as below:
static tblCustomer GetTopOneCustomer()
{
OnlineBankingDataClassesDataContext dbcntx = new OnlineBankingDataClassesDataContext();
var customers = dbcntx.tblCustomers.Take(1);
return customers.Single<tblCustomer>();
}
public static void Should_List_All_Account_By_Customer()
{
tblCustomer customer = GetTopOneCustomer();
DataController dc = new DataController();
List<tblAccount> accounts=dc.GetAccountsByCustomer(customer);
foreach (tblAccount account in accounts)
{
string accountdetails=string.Format("Account ID:{0} \n Account Type:{1} \n Balance:{2} \n BranchName:{3} \n AccountNumber:{4}",
account.ID.ToString(), account.tblAccountType.Name,
account.Balance.ToString(),
account.tblBranch.Name, account.Number);
Console.WriteLine(accountdetails);
}
}
I'm getting an error "Cannot access a disposed object." when accessing associated object like in this case, I'm using account.tblAccountType.Name. I know it has something to do with DataContext. How shall I get this code working.
dbcntx is a disposable object. The Garbage Collector can come along at any time after GetTopOneCustomer() has been called and dispose of it. Which is what looks like is happening.
Try changing GetTopOneCustomer() to:
static tblCustomer GetTopOneCustomer(OnlineBankingDataClassesDataContext dataContext)
{
//Stuff
}
Then inside Should_List_All_Account_By_Customer() change it like so:
using (OnlineBankingDataClassesDataContext dataContext = new OnlineBankingDataClassesDataContext())
{
tblCustomer customer = GetTopOneCustomer(dataContext);
//More Stuff
}
This way you control the lifetime of the dataContext.
Since the the DataContext is in a using statement, using (OnlineBankingDataClassesDataContext dbcntx = new OnlineBankingDataClassesDataContext())
Disposed will be called on it as soon as the context goes out of scope. This results in all entities beeing detached and all actions on the entity that requires a DataContext will fail.
This is what happens when account.Balance.ToString() is called.
One way to solve this is by creating a new context and use context.Attach(entity).

Thread and object by reference

I would like to ask is this code thread safe? There is a problem with attachment object. It is passed by reference to the new thread where MailHelper use it and sometimes the attachment object is mixed between threads.
public static void Start()
{
foreach (var message in messages)
{
//skip code
var fileName = httpWebResponse.GetResponseHeader("filename");
var fileStream = httpWebResponse.GetResponseStream();
var attachment = new Attachment(fileStream, fileName);
var thread = new Thread(() =>
{
var dictionary = new ListDictionary
{
{ "$Url$", message.Url }
};
MailHelper.SendMessage(dictionary,
message.Mail.Headers.From.Address,
"EmailConvertSuccess.txt",
attachment)
});
thread.Start();
}
}
No this will probably not be working - but it's not only the attachment (see Darins answer) but the message object you use as an iterator as well - you will have to copy it to a local instance before calling your Thread like this:
var messageCopy = message;
new Thread(a =>
MailHelper.SendMessage(
new ListDictionary { { "$Url$", messageCopy .Url } },
messageCopy.Mail.Headers.From.Address,
"EmailConvertSuccess.txt",
a as MailAttachment)
).Start(attachment);
If you really want to you could pass this as parameter - just like Darin did with it's variant but I don't think this is really needed)
I don't see a problem with attachment. True, it's captured in a closure, but as it's declared inside the loop, there should not be any problem with that.
However, there is a problem with message. Try var message1 = message; and then use message1 in the lambda.

Categories