/// <summary>
/// Creates a "DbConnection scope", DataContext and manages its LoadOptions.
/// </summary>
public class MyDbDatabase : IDisposable
{ #region Private fields
[ThreadStatic]
private static MyDbDatabase CurrentContext;
private readonly DataLoadOptionsBuilder loadOptionsBuilder = new DataLoadOptionsBuilder();
private readonly ConnectionMode mode;
private readonly MyDbDatabase parent;
private DbConnection connection;
private MyDbDataContext context;
private bool createdConnection;
#endregion
#region Properties
/// <summary>
/// Gets the current connection.
/// </summary>
/// <value>The connection.</value>
public DbConnection Connection
{ get
{ if (connection == null)
{ if (mode == ConnectionMode.UseExisting)
{ if (parent != null)
{ connection = parent.Connection;
}
else
{ connection = CreateConnection();
connection.Open();
createdConnection = true;
}
}
else
{ connection = CreateConnection();
createdConnection = true;
}
}
return connection;
}
}
/// <summary>
/// Gets the current DataContext.
/// </summary>
/// <value>The DataContext.</value>
public MyDbDataContext DataContext
{ get
{ if (context == null)
{ context = new MyDbDataContext(Connection)
{ LoadOptions = loadOptionsBuilder.BuildDataLoadOptions()
};
}
return context;
}
}
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="MyDbDatabase"/> class.
/// </summary>
/// <param name="mode">The mode.</param>
public MyDbDatabase(ConnectionMode mode)
{ this.mode = mode;
parent = CurrentContext;
CurrentContext = this;
CreateDefaultDataLoadOptions();
}
/// <summary>
/// Initializes a new instance of the <see cref="MyDbDatabase"/> class.
/// </summary>
public MyDbDatabase()
: this(ConnectionMode.UseExisting)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="MyDbDatabase"/> class.
/// </summary>
/// <param name="connection">The connection to use.</param>
/// <remarks>This method uses ConnectionMode.CreateNew mode.</remarks>
public MyDbDatabase(DbConnection connection)
: this(ConnectionMode.CreateNew)
{ if (connection == null)
throw new ArgumentNullException("connection");
this.connection = connection;
createdConnection = false;
}
#endregion Constructors
#region LoadOptions Methods
/// <summary>
/// Adds LoadWith option to DataLoadOptions.
/// </summary>
/// <param name="expr">The expression.</param>
public void LoadWith(LambdaExpression expr)
{ if (context != null)
throw new InvalidOperationException(
"LoadOptions for DataContext are frozen and cannot be modified. Methods LoadWith, LoadWith<T>, AssociateWith, AssociateWith<T>, ClearLoadOptions cannot be used after DataContext property of EFSDatabase class has been accessed for the first time.");
loadOptionsBuilder.Add(new LoadWith(expr));
}
/// <summary>
/// Adds LoadWith option to DataLoadOptions.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expr">The expression.</param>
public void LoadWith<T>(Expression<Func<T, object>> expr)
{ if (context != null)
throw new InvalidOperationException(
"LoadOptions for DataContext are frozen and cannot be modified. Methods LoadWith, LoadWith<T>, AssociateWith, AssociateWith<T>, ClearLoadOptions cannot be used after DataContext property of EFSDatabase class has been accessed for the first time.");
loadOptionsBuilder.Add(new LoadWith<T>(expr));
}
/// <summary>
/// Adds AssociateWith option to DataLoadOptions.
/// </summary>
/// <param name="expr">The expression.</param>
public void AssociateWith(LambdaExpression expr)
{ if (context != null)
throw new InvalidOperationException(
"LoadOptions for DataContext are frozen and cannot be modified. Methods LoadWith, LoadWith<T>, AssociateWith, AssociateWith<T>, ClearLoadOptions cannot be used after DataContext property of EFSDatabase class has been accessed for the first time.");
loadOptionsBuilder.Add(new AssociateWith(expr));
}
/// <summary>
/// Adds AssociateWith option to DataLoadOptions.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expr">The expression.</param>
public void AssociateWith<T>(Expression<Func<T, object>> expr)
{ if (context != null)
throw new InvalidOperationException(
"LoadOptions for DataContext are frozen and cannot be modified. Methods LoadWith, LoadWith<T>, AssociateWith, AssociateWith<T>, ClearLoadOptions cannot be used after DataContext property of EFSDatabase class has been accessed for the first time.");
loadOptionsBuilder.Add(new AssociateWith<T>(expr));
}
/// <summary>
/// Clears all currently set load options.
/// </summary>
public void ClearLoadOptions()
{ if (context != null)
throw new InvalidOperationException(
"LoadOptions for DataContext are frozen and cannot be modified. Methods LoadWith, LoadWith<T>, AssociateWith, AssociateWith<T>, ClearLoadOptions cannot be used after DataContext property of EFSDatabase class has been accessed for the first time.");
loadOptionsBuilder.Clear();
}
#endregion
#region IDisposable Members
public void Dispose()
{ if (context != null)
context.Dispose();
if (CurrentContext != this)
throw new InvalidOperationException(
"MyDbDatabase object is disposed before dependant MyDbDatabase object(s). This indicates a programming error.");
CurrentContext = parent;
if (createdConnection && connection != null)
connection.Dispose();
}
#endregion
#region Custom methods
private const string DefaultConnectionStringName = "ConsoleApplication1.Properties.Settings.DbConnectionString";
/// <summary>
/// Creates the connection.
/// </summary>
/// <returns>New connection.</returns>
public DbConnection CreateConnection()
{ //connection is created here
return new SqlConnection(
ConfigurationManager.ConnectionStrings[
DefaultConnectionStringName].ConnectionString);
}
/// <summary>
/// Creates the default data load options.
/// </summary>
public void CreateDefaultDataLoadOptions()
{ //default DataLoadOptions go here
loadOptionsBuilder.Add(new LoadWith<Dog>(d => d.Breed));
}