//-----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
//
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop
{
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using Microsoft.Isam.Esent.Interop.Vista;
using Microsoft.Win32.SafeHandles;
///
/// A class that encapsulates a in a disposable object. The
/// instance must be closed last and closing the instance releases all other
/// resources for the instance.
///
public class Instance : SafeHandleZeroOrMinusOneIsInvalid
{
///
/// Parameters for the instance.
///
private readonly InstanceParameters parameters;
///
/// The name of the instance.
///
private readonly string name;
///
/// The display name of the instance.
///
private readonly string displayName;
///
/// The TermGrbit to be used at JetTerm time.
///
private TermGrbit termGrbit;
///
/// Initializes a new instance of the Instance class. The underlying
/// JET_INSTANCE is allocated, but not initialized.
///
///
/// The name of the instance. This string must be unique within a
/// given process hosting the database engine.
///
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
public Instance(string name) : this(name, name, TermGrbit.None)
{
}
///
/// Initializes a new instance of the Instance class. The underlying
/// JET_INSTANCE is allocated, but not initialized.
///
///
/// The name of the instance. This string must be unique within a
/// given process hosting the database engine.
///
///
/// A display name for the instance. This will be used in eventlog
/// entries.
///
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
public Instance(string name, string displayName) : this(name, displayName, TermGrbit.None)
{
}
///
/// Initializes a new instance of the Instance class. The underlying
/// JET_INSTANCE is allocated, but not initialized.
///
///
/// The name of the instance. This string must be unique within a
/// given process hosting the database engine.
///
///
/// A display name for the instance. This will be used in eventlog
/// entries.
///
///
/// The TermGrbit to be used at JetTerm time.
///
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
public Instance(string name, string displayName, TermGrbit termGrbit) : base(true)
{
this.name = name;
this.displayName = displayName;
this.termGrbit = termGrbit;
JET_INSTANCE instance;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
this.SetHandle(JET_INSTANCE.Nil.Value);
}
finally
{
// This is the code that we want in a constrained execution region.
// We need to avoid the situation where JetCreateInstance2 is called
// but the handle isn't set, so the instance is never terminated.
// This would happen, for example, if there was a ThreadAbortException
// between the call to JetCreateInstance2 and the call to SetHandle.
//
// If an Esent exception is generated we do not want to call SetHandle
// because the instance isn't valid. On the other hand if a different
// exception (out of memory or thread abort) is generated we still need
// to set the handle to avoid losing track of the instance. The call to
// JetCreateInstance2 is in the CER to make sure that the only exceptions
// which can be generated are from ESENT failures.
Api.JetCreateInstance2(out instance, this.name, this.displayName, CreateInstanceGrbit.None);
this.SetHandle(instance.Value);
}
this.parameters = new InstanceParameters(instance);
}
///
/// Gets the JET_INSTANCE that this instance contains.
///
public JET_INSTANCE JetInstance
{
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
get
{
this.CheckObjectIsNotDisposed();
return this.CreateInstanceFromHandle();
}
}
///
/// Gets the InstanceParameters for this instance.
///
public InstanceParameters Parameters
{
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
get
{
this.CheckObjectIsNotDisposed();
return this.parameters;
}
}
///
/// Gets or sets the TermGrbit for this instance.
///
public TermGrbit TermGrbit
{
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
get
{
this.CheckObjectIsNotDisposed();
return this.termGrbit;
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
set
{
this.CheckObjectIsNotDisposed();
this.termGrbit = value;
}
}
///
/// Provide implicit conversion of an Instance object to a JET_INSTANCE
/// structure. This is done so that an Instance can be used anywhere a
/// JET_INSTANCE is required.
///
/// The instance to convert.
/// The JET_INSTANCE wrapped by the instance.
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
public static implicit operator JET_INSTANCE(Instance instance)
{
return instance.JetInstance;
}
///
/// Returns a that represents the current .
///
///
/// A that represents the current .
///
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", this.displayName, this.name);
}
///
/// Initialize the JET_INSTANCE.
///
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
public void Init()
{
this.Init(InitGrbit.None);
}
///
/// Initialize the JET_INSTANCE.
///
///
/// Initialization options.
///
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
public void Init(InitGrbit grbit)
{
this.CheckObjectIsNotDisposed();
JET_INSTANCE instance = this.JetInstance;
// Use a constrained region so that the handle is
// always set after JetInit2 is called.
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// Remember that a failure in JetInit can zero the handle
// and that JetTerm should not be called in that case.
Api.JetInit2(ref instance, grbit);
}
finally
{
this.SetHandle(instance.Value);
}
}
///
/// Initialize the JET_INSTANCE. This API requires at least the
/// Vista version of ESENT.
///
///
/// Additional recovery parameters for remapping databases during
/// recovery, position where to stop recovery at, or recovery status.
///
///
/// Initialization options.
///
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
public void Init(JET_RSTINFO recoveryOptions, InitGrbit grbit)
{
this.CheckObjectIsNotDisposed();
JET_INSTANCE instance = this.JetInstance;
// Use a constrained region so that the handle is
// always set after JetInit3 is called.
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// Remember that a failure in JetInit can zero the handle
// and that JetTerm should not be called in that case.
VistaApi.JetInit3(ref instance, recoveryOptions, grbit);
}
finally
{
this.SetHandle(instance.Value);
}
}
///
/// Terminate the JET_INSTANCE.
///
[SuppressMessage(
"Microsoft.StyleCop.CSharp.MaintainabilityRules",
"SA1409:RemoveUnnecessaryCode",
Justification = "CER code belongs in the finally block, so the try clause is empty")]
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
public void Term()
{
// Use a constrained region so that the handle is
// always set as invalid after JetTerm is called.
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// This try block deliberately left blank.
}
finally
{
// This is the code that we want in a constrained execution region.
// We need to avoid the situation where JetTerm is called
// but the handle isn't invalidated, so the instance is terminated again.
// This would happen, for example, if there was a ThreadAbortException
// between the call to JetTerm and the call to SetHandle.
//
// If an Esent exception is generated we do not want to invalidate the handle
// because the instance isn't necessarily terminated. On the other hand if a
// different exception (out of memory or thread abort) is generated we still need
// to invalidate the handle.
try
{
Api.JetTerm2(this.JetInstance, this.termGrbit);
}
catch (EsentDirtyShutdownException)
{
this.SetHandleAsInvalid();
throw;
}
this.SetHandleAsInvalid();
}
}
///
/// Release the handle for this instance.
///
/// True if the handle could be released.
protected override bool ReleaseHandle()
{
// The object is already marked as invalid so don't check
var instance = this.CreateInstanceFromHandle();
return (int)JET_err.Success == Api.Impl.JetTerm2(instance, this.termGrbit);
}
///
/// Create a JET_INSTANCE from the internal handle value.
///
/// A JET_INSTANCE containing the internal handle.
private JET_INSTANCE CreateInstanceFromHandle()
{
return new JET_INSTANCE { Value = this.handle };
}
///
/// Check to see if this instance is invalid or closed.
///
[SecurityPermissionAttribute(SecurityAction.LinkDemand)]
private void CheckObjectIsNotDisposed()
{
if (this.IsInvalid || this.IsClosed)
{
throw new ObjectDisposedException("Instance");
}
}
}
}