Problems trying to access/attach ESE database using ManagedEsent - c#

I'm trying to read an ESE database (.edb) using the ManagedEsent libraries. However, I'm getting an error at the time of setting up the database. The error appears to be coming up at the time of attaching the database (calling JetAttachDatabase()).
The error message I'm getting is "Soft recovery is intended on a backup database. Restore should be used instead".
Below is my code:
JET_INSTANCE instance;
JET_SESID sesid;
Microsoft.Isam.Esent.Interop.SystemParameters.DatabasePageSize = 8 * 1024;
Api.JetCreateInstance(out instance, Guid.NewGuid().ToString());
Api.JetInit(ref instance);
JET_DBID dbid;
JET_COLUMNID columnid;
JET_TABLEID tableid;
JET_COLUMNDEF columndef = new JET_COLUMNDEF();
Api.JetBeginSession(instance, out sesid, null, null);
Api.JetAttachDatabase(sesid, #"Blah.edb", AttachDatabaseGrbit.None);
Api.OpenDatabase(sesid, #"Blah.edb", out dbid, OpenDatabaseGrbit.None);
The exception I'm getting is:
Microsoft.Isam.Esent.Interop.EsentSoftRecoveryOnBackupDatabase
Soft recovery is intended on a backup database. Restore should be used instead
at Microsoft.Isam.Esent.Interop.Api.Check(Int32 err) in e:\src\codeplex_svn\codeplex\EsentInterop\Api.cs:line 2894
at Microsoft.Isam.Esent.Interop.Api.JetAttachDatabase(JET_SESID sesid, String database, AttachDatabaseGrbit grbit) in e:\src\codeplex_svn\codeplex\EsentInterop\Api.cs:line 372
Any ideas what could be causing this? Is the .edb file I'm attaching possible invalid?

I was able to access the database after running the EsentUtl utility to repair the database file:
esentutl /p Blah.edb
After running this, I can attach/open the database and read from the tables in the database.
I'm not sure what the repair has actually done to the file though (e.g. if it is possibly now missing some data or something).

Where did you get the database?
What you did should work for a Cleanly-shut-down database. You don't need the transaction log files for these databases.
But there are also Dirty databases (when JetTerm didn't complete gracefully). You'll need the transaction log files for those.
And also Backed-up databases, which are slightly different again. For these databases, you need to call JetRestore.
-martin

Related

How do I figure out the exact cause of error in EF update?

I have read the other threads on this, and none of them have answers that resolve my current scenario, nor are they similar. My scenario is reproducible on each run of my application, though I can't seem to produce a smaller piece of code that creates this error.
I'm getting the following error:
An exception has been raised that is likely due to a transient failure. If you are connecting to a SQL Azure database consider using SqlAzureExecutionStrategy.
The inner exception says:
A transport-level error has occurred when receiving results from the server. (provider: TCP Provider, error: 0 - The semaphore timeout period has expired.)
I am not connecting to a SQL Azure database. The connection is to a remote database through VPN, hosted on premises. To give some more context, I'm importing data from an external system, and every time it gets up to a specific record, it always fails when I try to update the entity after creating it. I've tried setting debug logging on in EF and copying the statement it generates into SSMS and running it with the same credentials with no errors. The only differentiating factor between this record and the previous records are the audit fields (time created/modified) and the name, which has changed from 1USD - Holding 99 to 1USD - Holding 100. I actually tested out changing the order which the records get imported, and it always fails at 100 when editing in EF after creation, so there's probably some other underlying issue at hand here. The field itself in the database is handling strings with a higher length than this, including this same process with no errors.
This obviously doesn't seem to actually be a transient failure, nor does it seem to be a connection issue, so how do I find the exact reason why this doesn't work?
Edit: Adding some code below. Also, I've noticed that if I change the name to 1USD - Holding 99 - Test 2, it works without any error despite the name being longer. Automatic ChangeDetection is not enabled for performance reasons.
security = new Security
{
Name = securityName,
IsActive = true,
CreatedAt = DateTime.Now,
CreatedBy = ADMIN_USER,
ModifiedAt = DateTime.Now,
ModifiedBy = ADMIN_USER
};
_repository.Save(security); //Ctx.Set<T>().Add(security); Ctx.SaveChanges();
//some attributes with a foreign key referencing this entity are saved, which is why we update audit fields below, but error occurs regardless if anything additional is saved
security.ModifiedBy = ADMIN_USER;
security.ModifiedAt = DateTime.Now;
_repository.Save(security); //Ctx.Set<T>().Attach(security); Ctx.Entry(security).State = EntityState.Modified; Ctx.SaveChanges();
Edit 2: It definitely seems to be something else other than a connection issue since it's happening for anything ending in a 3 character combination, such as A10, B10, or 10A. 1, 2, or 4 characters seem to be fine. Still have no idea what the actual issue is, however.

SQL Server CE two way sync with remote Access database

I'm working on a pretty special, legacy project where I need to build an app for PDA devices under Windows Mobile 6.5. The devices have a local database (SQL Server CE) which we are supposed to sync with a remote database (Microsoft Access) whenever they are docked and have network access.
So the local database using SQL Server CE works fine, but I can’t figure out a way to sync it to the Access database properly.
I read that ODBC and OLEDB are unsupported under Windows Mobile 6.5, most ressources I find are obsolete or have empty links, and the only way I found was to export the local database relevant tables in XML in the hope to build a VBA component for Access to import them properly. (and figure out backwards sync).
Update on the project and new questions
First of all, thanks to everyone who provided an useful answer, and to #josef who saved me a lot of time with the auto path on this thread.
So a remote SQL Server is a no go for security reasons (client is paranoid about security and won't provide me a server). So I'm tied to SQL Server CE on the PDA and Access on the computer.
As for the sync:
The exportation is fine: I'm using multiple dataAdapters and a WriteXML method to generate XML files transmitted by FTP when the device is plugged back in. Those files are then automatically imported into the Access database. (see code at the end).
My problem is on the importation: I can acquire data through XML readers from an Access-generated file. This data is then inserted in a dataset (In fact, I can even print the data on the PDA screen) but I can't figure out a way to do an "UPSERT" on the PDA's database. So I need a creative way to update/insert the data to the tables if they already contains data with the same id.
I tried two methods, with SQL errors (from what I understood it's SQL Server CE doesn't handle stored procedures or T-SQL). Example with a simple query that is supposed to update the "available" flag of some storage spots:
try
{
SqlCeDataAdapter dataAdapter = new SqlCeDataAdapter();
DataSet xmlDataSet = new DataSet();
xmlDataSet.ReadXml(localPath +#"\import.xml");
dataGrid1.DataSource = xmlDataSet.Tables[1];
_conn.Open();
int i = 0;
for (i = 0; i <= xmlDataSet.Tables[1].Rows.Count - 1; i++)
{
spot = xmlDataSet.Tables[1].Rows[i].ItemArray[0].ToString();
is_available = Convert.ToBoolean(xmlDataSet.Tables[1].Rows[i].ItemArray[1]);
SqlCeCommand importSpotCmd = new SqlCeCommand(#"
IF EXISTS (SELECT spot FROM spots WHERE spot=#spot)
BEGIN
UPDATE spots SET available=#available
END
ELSE
BEGIN
INSERT INTO spots(spot, available)
VALUES(#spot, #available)
END", _conn);
importSpotCmd.Parameters.Add("#spot", spot);
importSpotCmd.Parameters.Add("#available", is_available);
dataAdapter.InsertCommand = importSpotCmd;
dataAdapter.InsertCommand.ExecuteNonQuery();
}
_conn.Close();
}
catch (SqlCeException sql_ex)
{
MessageBox.Show("SQL database error: " + sql_ex.Message);
}
I also tried this query, same problem SQL server ce apparently don't handle ON DUPLICATE KEY (I think it's MySQL specific).
INSERT INTO spots (spot, available)
VALUES(#spot, #available)
ON DUPLICATE KEY UPDATE spots SET available=#available
The code of the export method, fixed so it works fine but still relevant for anybody who wants to know:
private void exportBtn_Click(object sender, EventArgs e)
{
const string sqlQuery = "SELECT * FROM storage";
const string sqlQuery2 = "SELECT * FROM spots";
string autoPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase); //get the current execution directory
using (SqlCeConnection _conn = new SqlCeConnection(_connString))
{
try
{
SqlCeDataAdapter dataAdapter1 = new SqlCeDataAdapter(sqlQuery, _conn);
SqlCeDataAdapter dataAdapter2 = new SqlCeDataAdapter(sqlQuery2, _conn);
_conn.Open();
DataSet ds = new DataSet("SQLExport");
dataAdapter1.Fill(ds, "stock");
dataAdapter2.Fill(ds, "spots");
ds.WriteXml(autoPath + #"\export.xml");
}
catch (SqlCeException sql_ex)
{
MessageBox.Show("SQL database error: " + sql_ex.Message);
}
}
}
As Access is more or less a stand-alone DB solution I strongly recommend to go with a full flavored SQL Server plus IIS to setup a Merge Replication synchronisation between the SQL CE data and the SQL Server data.
This is described with full sample code and setup in the book "Programming the .Net Compact Framework" by Paul Yao and David Durant (chapter 8, Synchronizing Mobile Data).
For a working sync, all changes to defined tables and data on the server and the CE device must be tracked (done via GUIDs, unique numbers) with there timestamps and a conflict handling has to be defined.
If the data is never changed by other means on the server, you may simply track Device side changes only and then push them to the Access database. This could be done by another app that does Buld Updates like described here.
If you do not want to go the expensive way to SQL Server, there are cheaper solutions with free SQLite (available for CE and Compact Framework too) and a commercial Sync tool for SQLite to MSAccess like DBSync.
If you are experienced, you may create your own SQLite to MS ACCESS sync tool.

Local ReportViewer Fails at Unnecessary Login to Database

I have inherited an application that runs small reports locally using Microsoft Web ReportViewer. Our application allows you to "Preview/Print" a report by clicking on a specific button that routes the user to a URL that allows them to download the report as a PDF. We have recently received the requirement to save these PDFs to the document table in our database. I have been able to get this to work successfully on localhost; however, when I publish the application to our IIS server, I get the following error:
System.Data.SqlClient.SqlException: Login failed for user 'Domain\Servername$'.
I've reviewed all of the sites that I could find involving this error (including this one) - most point to adding the server account to the SQL database; however, this shouldn't be an issue, since the button to preview/print the document is still functional and works as expected when the application is published and all of the data is held in a local object, which was previously pulled from the database (the model parameter below). The button and the auto-generation feature use the same two methods to create the PDF document(see below).
Here's some code:
public static byte[] CreatePDFDocument(DocumentTemplateType template, Request model)
{
Warning[] warnings;
string[] streamIds;
string mimeType = string.Empty;
string encoding = string.Empty;
string extension = string.Empty;
ReportViewer viewer = new ReportViewer();
viewer.ProcessingMode = ProcessingMode.Local;
viewer.LocalReport.ReportEmbeddedResource = "Xxx.Xxx.Bll.ReportViewerRDLCs." + template.RdlcFilename;
switch ((DocumentType)template.DocumentTypeId)
{
case eDocumentType.Report1:
viewer.LocalReport.SetParameters(GetForm1Parameters(model));
break;
/**
* Several other reports are in this switch. All reports have the
* same issue - all but one are removed for brevity.
*/
}
byte[] bytes = viewer.LocalReport.Render("PDF", null, out mimeType, out encoding, out extension, out streamIds, out warnings);
return bytes;
//return new byte[5] {5,6,7,8,9}; - used for troubleshooting.
}
public static List<ReportParameter> GetReport1Parameters(Request model)
{
List<ReportParameter> rptParams = new List<ReportParameter>();
//Start comment
rptParams.Add(new ReportParameter("EmployeeFullName", string.Format("{0:NN}", model.Employee)));
rptParams.Add(new ReportParameter("EmployeePhoneNumber", string.Format("{0:(###) ###-####}", Convert.ToInt64(model.Employee.PhoneNumber))));
rptParams.Add(new ReportParameter("HrchyShortDesc", model.Employee.HrchyShortDesc));
rptParams.Add(new ReportParameter("RequestDate", model.RequestDate.ToShortDateString()));
rptParams.Add(new ReportParameter("RequestRequested", model.RequestRequestType));
rptParams.Add(new ReportParameter("ReasonForRequest", model.RequestRequestReason));
rptParams.Add(new ReportParameter("LogNumber", model.CaseId));
if (!string.IsNullOrWhiteSpace(model.TimeSensitiveReason)) rptParams.Add(new ReportParameter("TimeSensitiveReason", model.TimeSensitiveReason));
var lastAction = model.LastActionOfType(WorkflowStateActionType.EmployeeConfirmation);
if (lastAction != null)
{
rptParams.Add(new ReportParameter("TodaysDate", lastAction.ActionDate.ToShortDateString()));
rptParams.Add(new ReportParameter("EmpConfirmed", "true"));
}
else rptParams.Add(new ReportParameter("TodaysDate", DateTime.Now.ToShortDateString()));
//end comment
return rptParams;
}
Through a lot of commenting in and out and pushes to our server, I've deduced the following:
From what I can tell, the error occurs on calling GetReport1Parameters. In the code above, I included a start and end comment - I've commented out everything in between, leaving only the list initialization and return statement (of an empty list) and still received the error.
I've commented out the call to GetReport1Parameters and returned a nonsensical byte array and didn't receive an Exception.
All functionality works fine on localhost and when I step through the functions, all of the variables seem to appear normal.
Things I've tried to do to remedy the situation:
1. Removed connection strings from the app.config, so that the application has to go to the web.config to get the correct strings (even though they were the same).
2. Commented in and out different sections of code to determine the problem area.
3. Tried calling the GetReport1Parameters method and returning null, leading to a null reference exception.
4. Tried calling the GetReport1Parameters with an empty parameter list, leading to the error mentioned above.
5. Tried running the report with no parameters (not even a blank list), got a ReportProcessingException for missing params.
Some additional information:
We use a service account for the application using impersonate identity in the web.config. That line is commented out on localhost, but is running on IIS.
All of other database interaction works correctly.
All of our database interaction is done using LINQ to SQL - model is an object based off of a database table, with some additional information that is calculated dynamically.
My desired outcome is that both the autogenerated documents and the preview/print documents both work. I have a feeling that this may be something simple that I'm overlooking, but I've already spent several hours today trying to fix this.
I can't think of any other pertinent information, but if you have questions I'll be more than happy to answer them.
Edit: Additional attempts to find solution:
Tried setting LINQ Deferred Loading equal to false. This caused more problems than it solved.
Implemented IReportServerCredentials and assigned the ReportViewer's ServerReport.ReportServerCredentials with the correct database credentials.
Assigned all pertinent report parameters to a Dictionary, and then called .ToString() on every object to ensure that it is pulled from the database. Then assigned those strings from the dictionary to the report parameters, so that ReportViewer should be receiving the data from the string pool, as opposed to pulling it from the database.
Even though you are using an ObjectDataSource to pass data to your report, Report Viewer will still invoke the Select method, which in turn could cause database access to occur. So even though it may seem that the login is unnecessary, you would need to dig into the data access methods you supplied with your ObjectDataSource to know for sure.
The error you are getting is being caused by a bug in Report Viewer 2010 that is describe in the following Microsoft Connect article:
ReportViewer.LocalReport.Render and ReportViewer.LocalReport.SetParameters changes ImpersonationLevel to None
Although the article mentions this problem should be fixed in Service Pack 1, it does not appear to be the case. I have not verified if this problem is fixed in Report Viewer 2012.
I worked around the problem by changing my data access layer to compare the current identity against the one in my HttpContext and restore it if necessary using the following code snippet:
System.Security.Principal.IIdentity id = System.Web.HttpContext.Current.User.Identity
if (id.Name != System.Security.Principal.WindowsIdentity.GetCurrent().Name)
{
context = (id as System.Security.Principal.WindowsIdentity).Impersonate()
}
I do this right before I connect to the database and undo it as soon as the connection is open.
I am not exactly thrilled with this workaround, mainly because now my data access layer is referencing the UI layer (System.Web).

C# Recovering Database stuck in 'Restoring...'

I am inserting a SQL Database Backup and Recovery section into my program. I have been using MSDN Examples (tryed another and it wouldnt work).
MSDN Page - http://msdn.microsoft.com/en-us/library/ms162133.aspx
I have got the Backup file working, and have tested the file in Management Studio.
But I am having trouble with the Recovery of the file.
The code seems to be working, but the database in SQL Server is stuck in "Restoring..."
if (openFile.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Server sqlServer = new Server();
Restore sqlRestore = new Restore();
sqlRestore.NoRecovery = true;
sqlRestore.Action = RestoreActionType.Database;
BackupDeviceItem deviceItem = new BackupDeviceItem(openFile.FileName, DeviceType.File);
sqlRestore.Devices.Add(deviceItem);
sqlRestore.Database = "firstRecoverTest";
sqlRestore.SqlRestore(sqlServer);
//Add the recovered database into the Entity Table
SqlCommand intoEntity = new SqlCommand("IF NOT EXISTS(SELECT entityName FROM Entitys WHERE entityName = 'firstRecoveryTest') INSERT INTO Entitys VALUES ('firstRecoveryTest');", sqlConnection);
sqlConnection.Open();
intoEntity.ExecuteNonQuery();
Database db = default(Database);
db = sqlServer.Databases["firstRecoverTest"];
db.RecoveryModel = (RecoveryModel)1;
db.AutoClose = true;
//db.Alter();
}
In the example there is a db.Alter(); function, but that throws an error that says "Alter failed for Database 'firstRecoverTest'".
Please let me know your thoughts
Thanks in advance
UPDATE
After inserting the "ReplaceDatabase = true;" there was no change in the end result.
Also stepping though the code line by line, shows that it is making it through.
The "db.Alter();" is just that placed at the end of the code (shown as comment). It is used in the creation of the backup and works without error.
InnerError shows this information when using db.Alter();
"ALTER DATABASE is not permitted while a database is in the Restoring state"
The interesting part is the SQL Log files. I am getting 3 Logs:
"Starting up database 'firstRecoverTest'."
"The database 'firstRecoverTest' is marked RESTORING and is in a state that does not allow recovery to be run."
"Database was restored: Database: firstRecoverTest, creation date(time): 2011/09/20(15:44:48), first LSN: 37:159:37, last LSN: 37:175:1, number of dump devices: 1, device information: (FILE=1, TYPE=DISK: {'C:\Users\Administrator\Desktop\installer_backup'}). Informational message. No user action required."
However, when I do a normal recover using SQL Management Studio there is 2 more log entrys saying
"Starting up database '[databaseName]'."
"Restore is complete on database '[databaseName]'. The database is now available"
I don't have enough reputation to post a small image of how it is in SQL Management Studio unfortunatly.
You should try either dropping the database or using sqlRestore.ReplaceDatabase = true;.
http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.restore.replacedatabase.aspx
If it looks like nothing is happening you can start the process in a seperate thread and wire up the events for notification of progress changes by using these.
sqlRestore.Complete += new ServerMessageEventHandler(completionEvent);
sqlRestore.PercentCompleteNotification = 10; // Call progress event every x percent change.
sqlRestore.PercentComplete += new PercentCompleteEventHandler(progressEvent);
If that doesn't work can you please post the Alter code that wasn't working. Also check SQL server logs and permissions.
UPDATED
Ok that update makes more sense. The reason is because you are setting sqlRestore.NoRecovery = true;. This has the effect that after the restore is done the DB is kept in a recovery state so you can restore an additional differential backup in addition to the full you just restored.
While it is in this state you will not be able to do anything more to the database. I would suggest that unless you require it you leave the default which is NoRecovery = false.
See http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.backuprestorebase.norecovery.aspx
Hope that helps.

SQL Server perform backup with C#

I've investigated the possibilities of creating database backups through SMO with C#.
The task is quite easy and code straightforward. I've got only one question: how can I check if the backup was really created?
SqlBackup.SqlBackup method returns no parameters and I don't even know if it throws any exceptions. (the only thing that I know is that it is blocking, because there's also SqlBackupAsync method)
I would appreciate any help.
you can and its very possible to do what you asked for,
but doing the backup it self using SMO its not very hard, but the hard part is managing the backup and the restore.
it would be hard to put all the code here, but its wont fit. so I will try my best to put the lines you need.
SqlBackup.SqlBackup doesn't return any value, its a void function.
but it takes one parameter which is "Server", try out the following code:
Server srvSql;
//Connect to Server using your authentication method and load the databases in srvSql
// THEN
Backup bkpDatabase = new Backup();
bkpDatabase.Action = BackupActionType.Database;
bkpDatabase.Incremental = true; // will take an incemental backup
bkpDatabase.Incremental = false; // will take a Full backup
bkpDatabase.Database = "your DB name";
BackupDeviceItem bDevice = new BackupDeviceItem("Backup.bak", DeviceType.File);
bkpDatabase.Devices.Add(bDevice );
bkpDatabase.PercentCompleteNotification = 1;// this for progress
bkpDatabase.SqlBackup(srvSql);
bkpDatabase.Devices.Clear();
I've investigated the problem using Reflector.NET (I suppose this is legal since RedGate is Ms Gold Certified Partner and Reflector.NET opens .NET libraries out of the box). As I found out the method throws two types of exceptions:
FailedOperationException - in most cases, other exceptions are "translated" (I suppose translating means creating new FailedOperationException and setting InnerException to what was actually thrown)
UnsupportedVersionException - in one case when log truncation is set to TruncateOnly and server major version is more or equal to 10 (which is sql server 2008?)
This solves my problem partially, because I'm not 100% sure that if something goes wrong those exceptions will actually be thrown.

Categories