Prevent object dispose inside using block - c#

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);
}

Related

DataReader Error with Lazy Singleton Pattern in C# when Manager Called More Than Once

I'm using the lazy singleton pattern in C# for my data managers. The code in my manager looks like this:
private static readonly Lazy<ConditionManager> singleton =
new Lazy<ConditionManager>(() => new ConditionManager());
public static ConditionManager Instance { get { return singleton.Value; } }
Then in my code, I am trying to see if the item exists before I create it:
//see if condition exists. If it doesn't then create it.
List<Condition> conditions = ConditionManager.Instance.Select
(question.Id, ConditionType.ProjectQuestion);
Condition c;
if (conditions.Count == 0)
{
c = new Condition(question.Id, ConditionType.ProjectQuestion, ReleaseId);
c.Id = ConditionManager.Instance.Insert(c);
}
The problem is that, when I call the Insert, I get the dreaded error: "There is already an open DataReader associated with this Command which must be closed first." I have found a workaround (I have one call to the ConditionManager that both checks to see if it exists and then returns either the existing one of the newly created one), but what is the proper way to handle this? How do I close the first instance?

Convert System.Data.Entity.DynamicProxies to (non proxy) class in C#

I am getting some data from the database and storing this in a global variable as shown:
//Global Variable
public static List<stuff> Stuff;
using (var context = new StuffContext())
{
stuff = new List<stuff>();
stuff = (from r in context.Stuff
select r).ToList();
}
The problem I am having is that the context closes and when I wish to access some of the data stored in the global variable, I cannot.
The data is of System.Data.Entity.DynamicProxies.Stuff instead of Application.Model.Stuff which means I then receive this error when I try to do something with the data:
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
My question is how can I, using the above code as an example, convert / cast to the type that I want so that I can use the data else where in my application?
Edit: Quick screen grab of the error:
The Solution was due to lazy loading after all.
I had to tell the query to grab everything so that when the context closes I still had access to the data.
This is the change I had to make:
public static List<stuff> Stuff;
using (var context = new StuffContext())
{
stuff = new List<stuff>();
stuff = (from r in context.Stuff
.Include(s => s.MoreStuff).Include(s => s.EvenMoreStuff)
select r).ToList();
}
Try to Disable ProxyCreationEnabled In Your Project BbContext constructor As Follow:
Configuration.ProxyCreationEnabled = false;

RavenDB is not tracking entity changes

I have the following code that opens a session with RavenDB, gets the relevant IDs, uses those ideas to load the entities, change them, and finally save them.
List<EventDescriptor> events;
using (var session = raven.OpenSession())
{
session.Store(aggregate);
session.SaveChanges();
events = (from descriptor in session.Query<EventDescriptor>() where descriptor.AggregateId == aggregate.Id select descriptor).ToList();
}
using (var session = raven.OpenSession())
{
foreach (var #event in events)
{
var e = session.Load<EventDescriptor>("EventDescriptors/" + #event.Id.ToString());
e.Saved = true;
}
session.SaveChanges();
}
The problem however is that the changes in the entities don't seem to be tracked, and I can't delete the entities either (gives me unknown entity error), even though the object is loaded. I already tried calling SaveChanges inside the loop, but that didn't help either. I looked at the Raven documentation but I don't see what I'm doing wrong here.
Yes, we can't track changes on structs, because every time that you change them, you create a new copy
The problem was that EventDescriptor was a struct, and not a class. Changing this solved the problem. I assume it's because a struct is a valuetype and not a referencetype.

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

Error when trying to loop through entities from an Azure Table

i keep getting this error, "The current value 'String.Empty' type is not compatible with the expected 'System.Boolean' type", when i try to loop through a bunch of entities from an Azure table, i am only new to using Azure so this could be something very easy, the error that i am getting.
my code :
private void registerButton_Click(object sender, RoutedEventArgs e)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString"));
// Create the table client
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
// Get the data service context
TableServiceContext serviceContext = tableClient.GetDataServiceContext();
// Create a new customer entity
user = new UserDetailsEntity();
//Setting the fields of the new userEntity
user.username = usernameText.Text;
user.password = passwordText.Text;
user.subscriptionID = subText.Text;
user.subscriptionName = subscriptionNameText.Text;
user.thumbprint = thumbprintText.Text;
user.email = emailText.Text;
user.phoneNumber = "3530" + numberText.Text;
int rowCount = 1;
CloudTableQuery<UserDetailsEntity> Query = (from en in serviceContext.CreateQuery<UserDetailsEntity>("userdetails")
select en).AsTableServiceQuery<UserDetailsEntity>();
//error occurs in the next line
foreach (UserDetailsEntity ent in Query)
{
rowCount++;
}
user.RowKey = rowCount.ToString();
// Add the new customer to the people table
serviceContext.AddObject("userdetails", user);
// Submit the operation to the table service
serviceContext.SaveChangesWithRetries();
//Set the variables so they can be retrieved when the next screen loads
Application.Current.Properties["username"] = usernameText.Text;
Application.Current.Properties["password"] = passwordText.Text;
Window1 userHome = new Window1();
this.Close(); //to close Password window
userHome.Show(); //to show Main form
}
Without more code, I cannot tell you exactly where the problem is, however the exception is fairly explanatory. You are trying to set a boolean property to a value of a string.
If the error is occurring in your foreach as you noted in the code comment, then I would check how your UserDetailsEntity object is set up. There is probably a property that is set up as a boolean, yet your data is coming back as a String.Empty. The reason you are getting this in your foreach is because your LINQ query is of type IQueryable, so it will not actually execute and fill your objects until you actually access the data (by your foreach)*. So, you could put breakpoints in your UserDetailsEntity properties to see which one it is if this is not blatent from looking at the code.
*Keep in mind that this is the N+1 problem, where you are making a call to the database on each iteration of your loop. You can resolve this by calling .ToList() to eager load all of your data at once into your query...if this is a problem for you, that is.

Categories

Resources