/// <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));
}
#endregion
}
/// <summary>
/// The connection mode.
/// </summary>
public enum ConnectionMode
{ /// <summary>
/// Existing connection is used. If no such exists a new is created.
/// </summary>
UseExisting,
/// <summary>
/// A new connection is created.
/// </summary>
CreateNew
}
/// <summary>
/// Builds <see cref="DataLoadOptions"/> object.
/// </summary>
public class DataLoadOptionsBuilder : List<IDataLoadOptionsBuilderItem>
{ /// <summary>
/// Initializes a new instance of the <see cref="DataLoadOptionsBuilder"/> class.
/// </summary>
public DataLoadOptionsBuilder()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="DataLoadOptionsBuilder"/> class.
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new list.</param>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="collection"/> is null.</exception>
public DataLoadOptionsBuilder(IEnumerable<IDataLoadOptionsBuilderItem> collection) :
base(collection)
{ }
/// <summary>
/// Builds the data load options.
/// </summary>
/// <returns><see cref="DataLoadOptions"/> object.</returns>
public DataLoadOptions BuildDataLoadOptions()
{ var options = new DataLoadOptions();
for (int i = 0; i < Count; i++)
{ this[i].BuildDataLoadOptions(options);
}
return options;
}
}
/// <summary>
/// Interface for item that is able to partially prepare <see cref="DataLoadOptions"/> object.
/// </summary>
public interface IDataLoadOptionsBuilderItem
{ /// <summary>
/// Builds the data load options.
/// </summary>
/// <param name="options">The options.</param>
void BuildDataLoadOptions(DataLoadOptions options);
}
/// <summary>
/// Sets AssociateWith option within <see cref="DataLoadOptions"/>.
/// </summary>
public class AssociateWith : IDataLoadOptionsBuilderItem
{ private readonly LambdaExpression _expression;
/// <summary>
/// Initializes a new instance of the <see cref="AssociateWith"/> class.
/// </summary>
/// <param name="expression">The expression.</param>
public AssociateWith(LambdaExpression expression)
{ if (expression == null)
throw new ArgumentNullException("expression"); _expression = expression;
}
#region IDataLoadOptionsBuilderItem Members
/// <summary>
/// Builds the data load options.
/// </summary>
/// <param name="options">The options.</param>
public void BuildDataLoadOptions(DataLoadOptions options)
{ options.AssociateWith(_expression);
}
#endregion
}
/// <summary>
/// Sets AssociateWith option within <see cref="DataLoadOptions"/>.
/// </summary>
public class AssociateWith<T> : IDataLoadOptionsBuilderItem
{ private readonly Expression<Func<T, object>> _expression;
/// <summary>
/// Initializes a new instance of the <see cref="AssociateWith<T>"/> class.
/// </summary>
/// <param name="expression">The expression.</param>
public AssociateWith(Expression<Func<T, object>> expression)
{ if (expression == null)
throw new ArgumentNullException("expression"); _expression = expression;
}
#region IDataLoadOptionsBuilderItem Members
/// <summary>
/// Builds the data load options.
/// </summary>
/// <param name="options">The options.</param>
public void BuildDataLoadOptions(DataLoadOptions options)
{ options.AssociateWith(_expression);
}
#endregion
}
/// <summary>
/// Sets LoadWith option within <see cref="DataLoadOptions"/>.
/// </summary>
public class LoadWith : IDataLoadOptionsBuilderItem
{ private readonly LambdaExpression _expression;
/// <summary>
/// Initializes a new instance of the <see cref="LoadWith"/> class.
/// </summary>
/// <param name="expression">The expression.</param>
public LoadWith(LambdaExpression expression)
{ if (expression == null)
throw new ArgumentNullException("expression"); _expression = expression;
}
#region IDataLoadOptionsBuilderItem Members
/// <summary>
/// Builds the data load options.
/// </summary>
/// <param name="options">The options.</param>
public void BuildDataLoadOptions(DataLoadOptions options)
{ options.LoadWith(_expression);
}
#endregion
}
/// <summary>
/// Sets LoadWith option within <see cref="DataLoadOptions"/>.
/// </summary>
public class LoadWith<T> : IDataLoadOptionsBuilderItem
{ private readonly Expression<Func<T, object>> _expression;
/// <summary>
/// Initializes a new instance of the <see cref="LoadWith<T>"/> class.
/// </summary>
/// <param name="expression">The expression.</param>
public LoadWith(Expression<Func<T, object>> expression)
{ if (expression == null)
throw new ArgumentNullException("expression"); _expression = expression;
}
#region IDataLoadOptionsBuilderItem Members
/// <summary>
/// Builds the data load options.
/// </summary>
/// <param name="options">The options.</param>
public void BuildDataLoadOptions(DataLoadOptions options)
{ options.LoadWith(_expression);
}
#endregion
}