-#region MIT license
-//
-// MIT license
-//
-// Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-#endregion
-
-using System;
-using System.Collections;
-using System.Data;
-using System.Data.Common;
-using System.Data.Linq.Mapping;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-
-#if MONO_STRICT
-using System.Data.Linq.Implementation;
-using System.Data.Linq.Sugar;
-using System.Data.Linq.Identity;
-using DbLinq.Util;
-using AttributeMappingSource = System.Data.Linq.Mapping.AttributeMappingSource;
-using MappingContext = System.Data.Linq.Mapping.MappingContext;
-using DbLinq;
-#else
-using DbLinq.Data.Linq.Implementation;
-using DbLinq.Data.Linq.Sugar;
-using DbLinq.Data.Linq.Identity;
-using DbLinq.Util;
-using AttributeMappingSource = DbLinq.Data.Linq.Mapping.AttributeMappingSource;
-using MappingContext = DbLinq.Data.Linq.Mapping.MappingContext;
-using System.Data.Linq;
-#endif
-
-using DbLinq.Factory;
-using DbLinq.Vendor;
-using DbLinq.Data.Linq.Database;
-using DbLinq.Data.Linq.Database.Implementation;
-using System.Linq.Expressions;
-using System.Reflection.Emit;
-
-#if MONO_STRICT
-namespace System.Data.Linq
-#else
-namespace DbLinq.Data.Linq
-#endif
-{
- public partial class DataContext : IDisposable
- {
- //private readonly Dictionary<string, ITable> _tableMap = new Dictionary<string, ITable>();
- private readonly Dictionary<Type, ITable> _tableMap = new Dictionary<Type, ITable>();
-
- public MetaModel Mapping { get; private set; }
- // PC question: at ctor, we get a IDbConnection and the Connection property exposes a DbConnection
- // WTF?
- public DbConnection Connection { get { return DatabaseContext.Connection as DbConnection; } }
-
- // all properties below are set public to optionally be injected
- internal IVendor Vendor { get; set; }
- internal IQueryBuilder QueryBuilder { get; set; }
- internal IQueryRunner QueryRunner { get; set; }
- internal IMemberModificationHandler MemberModificationHandler { get; set; }
- internal IDatabaseContext DatabaseContext { get; private set; }
- // /all properties...
-
- private readonly EntityTracker entityTracker = new EntityTracker();
-
- private IIdentityReaderFactory identityReaderFactory;
- private readonly IDictionary<Type, IIdentityReader> identityReaders = new Dictionary<Type, IIdentityReader>();
-
- /// <summary>
- /// The default behavior creates one MappingContext.
- /// </summary>
- [DBLinqExtended]
- internal virtual MappingContext _MappingContext { get; set; }
-
- [DBLinqExtended]
- internal IVendorProvider _VendorProvider { get; set; }
-
- public DataContext(IDbConnection connection, MappingSource mapping)
- {
- Init(new DatabaseContext(connection), mapping, null);
- }
-
- public DataContext(IDbConnection connection)
- {
- Init(new DatabaseContext(connection), null, null);
- }
-
- [DbLinqToDo]
- public DataContext(string fileOrServerOrConnection, MappingSource mapping)
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Construct DataContext, given a connectionString.
- /// To determine which DB type to go against, we look for 'DbLinqProvider=xxx' substring.
- /// If not found, we assume that we are dealing with MS Sql Server.
- ///
- /// Valid values are names of provider DLLs (or any other DLL containing an IVendor implementation)
- /// DbLinqProvider=Mysql
- /// DbLinqProvider=Oracle etc.
- /// </summary>
- /// <param name="connectionString">specifies file or server connection</param>
- [DbLinqToDo]
- public DataContext(string connectionString)
- {
- #region DataContext connectionString ctor
- if (connectionString == null)
- throw new ArgumentNullException("connectionString");
-
- System.Text.RegularExpressions.Regex reProvider
- = new System.Text.RegularExpressions.Regex(@"DbLinqProvider=([\w\.]+)");
-
- int startPos = connectionString.IndexOf("DbLinqProvider=");
- string assemblyToLoad;
- string vendorClassToLoad;
- if (!reProvider.IsMatch(connectionString))
- {
- assemblyToLoad = "DbLinq.SqlServer.dll";
- vendorClassToLoad = "SqlServerVendor";
- }
- else
- {
- System.Text.RegularExpressions.Match match = reProvider.Match(connectionString);
-#if MONO_STRICT
- //Pascal says on the forum:
- //[in MONO] "all vendors are (will be) embedded in the System.Data.Linq assembly"
- assemblyToLoad = "System.Data.Linq.dll";
- vendorClassToLoad = match.Groups[1].Value; //eg. "MySql"
-#else
- //plain DbLinq - non MONO:
- //IVendor classes are in DLLs such as "DbLinq.MySql.dll"
- assemblyToLoad = match.Groups[1].Value; //eg. assemblyToLoad="DbLinq.MySql.dll"
- if (assemblyToLoad.Contains("."))
- {
- //already fully qualified DLL name?
- throw new ArgumentException("Please provide a short name, such as 'MySql', not '" + assemblyToLoad + "'");
- }
- else
- {
- //we were given short name, such as MySql
- vendorClassToLoad = assemblyToLoad + "Vendor"; //eg. MySqlVendor
- assemblyToLoad = "DbLinq." + assemblyToLoad + ".dll"; //eg. DbLinq.MySql.dll
- }
-#endif
- //shorten: "DbLinqProvider=X;Server=Y" -> ";Server=Y"
- string shortenedConnStr = reProvider.Replace(connectionString, "");
- connectionString = shortenedConnStr;
- }
-
- Assembly assy;
- try
- {
-#if MONO_STRICT
- assy = typeof (DataContext).Assembly; // System.Data.Linq.dll
-#else
- //TODO: check if DLL is already loaded?
- assy = Assembly.LoadFrom(assemblyToLoad);
-#endif
- }
- catch (Exception ex)
- {
- //TODO: add proper logging here
- Console.WriteLine("DataContext ctor: Assembly load failed for " + assemblyToLoad + ": " + ex);
- throw ex;
- }
-
- //find IDbProvider class in this assembly:
- var ctors = (from mod in assy.GetModules()
- from cls in mod.GetTypes()
- where cls.GetInterfaces().Contains(typeof(IVendor))
- && cls.Name.ToLower() == vendorClassToLoad.ToLower()
- let ctorInfo = cls.GetConstructor(Type.EmptyTypes)
- where ctorInfo != null
- select ctorInfo).ToList();
- if (ctors.Count == 0)
- {
- string msg = "Found no IVendor class in assembly " + assemblyToLoad + " having a string ctor";
- throw new ArgumentException(msg);
- }
- else if (ctors.Count > 1)
- {
- string msg = "Found more than one IVendor class in assembly " + assemblyToLoad + " having a string ctor";
- throw new ArgumentException(msg);
- }
- ConstructorInfo ctorInfo2 = ctors[0];
-
- object ivendorObject;
- try
- {
- ivendorObject = ctorInfo2.Invoke(new object[]{});
- }
- catch (Exception ex)
- {
- //TODO: add proper logging here
- Console.WriteLine("DataContext ctor: Failed to invoke IVendor ctor " + ctorInfo2.Name + ": " + ex);
- throw ex;
- }
- IVendor ivendor = (IVendor)ivendorObject;
- IDbConnection dbConnection = ivendor.CreateDbConnection(connectionString);
- Init(new DatabaseContext(dbConnection), null, ivendor);
- #endregion
- }
-
- private void Init(IDatabaseContext databaseContext, MappingSource mappingSource, IVendor vendor)
- {
- if (databaseContext == null)
- throw new ArgumentNullException("databaseContext");
-
- _VendorProvider = ObjectFactory.Get<IVendorProvider>();
- if (vendor == null)
- Vendor = _VendorProvider.FindVendorByProviderType(typeof(SqlClient.Sql2005Provider));
- else
- Vendor = vendor;
-
- DatabaseContext = databaseContext;
-
- MemberModificationHandler = ObjectFactory.Create<IMemberModificationHandler>(); // not a singleton: object is stateful
- QueryBuilder = ObjectFactory.Get<IQueryBuilder>();
- QueryRunner = ObjectFactory.Get<IQueryRunner>();
-
- //EntityMap = ObjectFactory.Create<IEntityMap>();
- identityReaderFactory = ObjectFactory.Get<IIdentityReaderFactory>();
-
- _MappingContext = new MappingContext();
-
- // initialize the mapping information
- if (mappingSource == null)
- mappingSource = new AttributeMappingSource();
- Mapping = mappingSource.GetModel(GetType());
- }
-
- /// <summary>
- /// Checks if the table is allready mapped or maps it if not.
- /// </summary>
- /// <param name="tableType">Type of the table.</param>
- /// <exception cref="InvalidOperationException">Thrown if the table is not mappable.</exception>
- private void CheckTableMapping(Type tableType)
- {
- //This will throw an exception if the table is not found
- if(Mapping.GetTable(tableType) == null)
- {
- throw new InvalidOperationException("The type '" + tableType.Name + "' is not mapped as a Table.");
- }
- }
-
- /// <summary>
- /// Returns a Table for the type TEntity.
- /// </summary>
- /// <exception cref="InvalidOperationException">If the type TEntity is not mappable as a Table.</exception>
- /// <typeparam name="TEntity">The table type.</typeparam>
- public Table<TEntity> GetTable<TEntity>() where TEntity : class
- {
- return (Table<TEntity>)GetTable(typeof(TEntity));
- }
-
- /// <summary>
- /// Returns a Table for the given type.
- /// </summary>
- /// <param name="type">The table type.</param>
- /// <exception cref="InvalidOperationException">If the type is not mappable as a Table.</exception>
- public ITable GetTable(Type type)
- {
- lock (_tableMap)
- {
- ITable tableExisting;
- if (_tableMap.TryGetValue(type, out tableExisting))
- return tableExisting;
-
- //Check for table mapping
- CheckTableMapping(type);
-
- var tableNew = Activator.CreateInstance(
- typeof(Table<>).MakeGenericType(type)
- , BindingFlags.NonPublic | BindingFlags.Instance
- , null
- , new object[] { this }
- , System.Globalization.CultureInfo.CurrentCulture) as ITable;
-
- _tableMap[type] = tableNew;
- return tableNew;
- }
- }
-
- public void SubmitChanges()
- {
- SubmitChanges(ConflictMode.FailOnFirstConflict);
- }
-
- /// <summary>
- /// Pings database
- /// </summary>
- /// <returns></returns>
- public bool DatabaseExists()
- {
- try
- {
- return Vendor.Ping(this);
- }
- catch (Exception)
- {
- return false;
- }
- }
-
- /// <summary>
- /// Commits all pending changes to database
- /// </summary>
- /// <param name="failureMode"></param>
- public virtual void SubmitChanges(ConflictMode failureMode)
- {
- using (DatabaseContext.OpenConnection()) //ConnMgr will close connection for us
- using (IDatabaseTransaction transaction = DatabaseContext.Transaction())
- {
- var queryContext = new QueryContext(this);
- var entityTracks = entityTracker.EnumerateAll().ToList();
- foreach (var entityTrack in entityTracks)
- {
- switch (entityTrack.EntityState)
- {
- case EntityState.ToInsert:
- var insertQuery = QueryBuilder.GetInsertQuery(entityTrack.Entity, queryContext);
- QueryRunner.Insert(entityTrack.Entity, insertQuery);
- Register(entityTrack.Entity);
- break;
- case EntityState.ToWatch:
- if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))
- {
- var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entityTrack.Entity, Mapping);
- var updateQuery = QueryBuilder.GetUpdateQuery(entityTrack.Entity, modifiedMembers, queryContext);
- QueryRunner.Update(entityTrack.Entity, updateQuery, modifiedMembers);
-
- RegisterUpdateAgain(entityTrack.Entity);
- }
- break;
- case EntityState.ToDelete:
- var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext);
- QueryRunner.Delete(entityTrack.Entity, deleteQuery);
-
- UnregisterDelete(entityTrack.Entity);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- // TODO: handle conflicts (which can only occur when concurrency mode is implemented)
- transaction.Commit();
- }
- }
-
- /// <summary>
- /// TODO - allow generated methods to call into stored procedures
- /// </summary>
- [DBLinqExtended]
- internal IExecuteResult _ExecuteMethodCall(DataContext context, System.Reflection.MethodInfo method, params object[] sqlParams)
- {
- using (DatabaseContext.OpenConnection())
- {
- System.Data.Linq.IExecuteResult result = Vendor.ExecuteMethodCall(context, method, sqlParams);
- return result;
- }
- }
-
- [DbLinqToDo]
- protected IExecuteResult ExecuteMethodCall(object instance, System.Reflection.MethodInfo methodInfo, params object[] parameters)
- {
- throw new NotImplementedException();
- }
-
- #region Identity management
-
- [DBLinqExtended]
- internal IIdentityReader _GetIdentityReader(Type t)
- {
- IIdentityReader identityReader;
- lock (identityReaders)
- {
- if (!identityReaders.TryGetValue(t, out identityReader))
- {
- identityReader = identityReaderFactory.GetReader(t, this);
- identityReaders[t] = identityReader;
- }
- }
- return identityReader;
- }
-
- [DBLinqExtended]
- internal object _GetRegisteredEntity(object entity)
- {
- // TODO: check what is faster: by identity or by ref
- var identityReader = _GetIdentityReader(entity.GetType());
- var identityKey = identityReader.GetIdentityKey(entity);
- if (identityKey == null) // if we don't have an entitykey here, it means that the entity has no PK
- return entity;
- // even
- var registeredEntityTrack = entityTracker.FindByIdentity(identityKey);
- if (registeredEntityTrack != null)
- return registeredEntityTrack.Entity;
- return null;
- }
-
- //internal object GetRegisteredEntityByKey(IdentityKey identityKey)
- //{
- // return EntityMap[identityKey];
- //}
-
- /// <summary>
- /// Registers an entity in a watch state
- /// </summary>
- /// <param name="entity"></param>
- /// <returns></returns>
- [DBLinqExtended]
- internal object _GetOrRegisterEntity(object entity)
- {
- var identityReader = _GetIdentityReader(entity.GetType());
- var identityKey = identityReader.GetIdentityKey(entity);
- SetEntitySetsQueries(entity);
- SetEntityRefQueries(entity);
-
- // if we have no identity, we can't track it
- if (identityKey == null)
- return entity;
-
- // try to find an already registered entity and return it
- var registeredEntityTrack = entityTracker.FindByIdentity(identityKey);
- if (registeredEntityTrack != null)
- return registeredEntityTrack.Entity;
-
- // otherwise, register and return
- entityTracker.RegisterToWatch(entity, identityKey);
- return entity;
- }
-
- readonly IDataMapper DataMapper = ObjectFactory.Get<IDataMapper>();
- private void SetEntityRefQueries(object entity)
- {
- Type thisType = entity.GetType();
- IList<MemberInfo> properties = DataMapper.GetEntityRefAssociations(thisType);
-
-
- foreach (PropertyInfo prop in properties)
- {
- //example of entityRef:Order.Employee
- AssociationAttribute associationInfo = prop.GetAttribute<AssociationAttribute>();
- Type otherTableType = prop.PropertyType;
- IList<MemberInfo> otherPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(otherTableType));
-
- if (otherPKs.Count > 1)
- throw new NotSupportedException("Multiple keys object not supported yet.");
-
- var otherTable = GetTable(otherTableType);
-
- //ie:EmployeeTerritories.EmployeeID
-
- var thisForeignKeyProperty = thisType.GetProperty(associationInfo.ThisKey);
- object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null);
-
- IEnumerable query = null;
- if (thisForeignKeyValue != null)
- {
- ParameterExpression p = Expression.Parameter(otherTableType, "other");
- Expression predicate;
- if (!(thisForeignKeyProperty.PropertyType.IsNullable()))
- {
- predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()),
- Expression.Constant(thisForeignKeyValue));
- }
- else
- {
- var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value");
- predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()),
- Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null)));
- }
-
- query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable;
- //it would be interesting surround the above query with a .Take(1) expression for performance.
- }
-
-
- FieldInfo entityRefField = entity.GetType().GetField(associationInfo.Storage, BindingFlags.NonPublic | BindingFlags.Instance);
- object entityRefValue = null;
- if (query != null)
- entityRefValue = Activator.CreateInstance(entityRefField.FieldType, query);
- else
- entityRefValue = Activator.CreateInstance(entityRefField.FieldType);
- entityRefField.SetValue(entity, entityRefValue);
- }
- }
-
- /// <summary>
- /// This method is executed when the entity is being registered. Each EntitySet property has a internal query that can be set using the EntitySet.SetSource method.
- /// Here we set the query source of each EntitySetProperty
- /// </summary>
- /// <param name="entity"></param>
- private void SetEntitySetsQueries(object entity)
- {
- IList<MemberInfo> properties = DataMapper.GetEntitySetAssociations(entity.GetType());
-
- if (properties.Any()) {
- IList<MemberInfo> thisPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(entity.GetType()));
-
- if (thisPKs.Count > 1)
- throw new NotSupportedException("Multiple keys object not supported yet.");
-
- object primaryKeyValue = (thisPKs.First() as PropertyInfo).GetValue(entity, null);
-
-
- foreach (PropertyInfo prop in properties)
- {
- //example of entitySet: Employee.EmployeeTerritories
- var associationInfo = prop.GetAttribute<AssociationAttribute>();
- Type otherTableType = prop.PropertyType.GetGenericArguments().First();
-
- //other table:EmployeeTerritories
- var otherTable = GetTable(otherTableType);
- //other table member:EmployeeTerritories.EmployeeID
- var otherTableMember = otherTableType.GetProperty(associationInfo.OtherKey);
-
-
- ParameterExpression p = Expression.Parameter(otherTableType, "other");
- Expression predicate;
- if (!(otherTableMember.PropertyType.IsNullable()))
- {
- predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember),
- Expression.Constant(primaryKeyValue));
- }
- else
- {
- var ValueProperty = otherTableMember.PropertyType.GetProperty("Value");
- predicate = Expression.Equal(Expression.MakeMemberAccess(
- Expression.MakeMemberAccess(p, otherTableMember),
- ValueProperty),
- Expression.Constant(primaryKeyValue));
- }
-
- var query = GetOtherTableQuery(predicate, p, otherTableType, otherTable);
-
- var entitySetValue = prop.GetValue(entity, null);
-
- if (entitySetValue == null)
- {
- entitySetValue = Activator.CreateInstance(prop.PropertyType);
- prop.SetValue(entity, entitySetValue, null);
- }
-
- var setSourceMethod = entitySetValue.GetType().GetMethod("SetSource");
- setSourceMethod.Invoke(entitySetValue, new[] { query });
- //employee.EmployeeTerritories.SetSource(Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH"))
- }
- }
- }
-
- private object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable)
- {
- //predicate: other.EmployeeID== "WARTH"
- Expression lambdaPredicate = Expression.Lambda(predicate, parameter);
- //lambdaPredicate: other=>other.EmployeeID== "WARTH"
-
- var whereMethod = typeof(Queryable)
- .GetMethods().First(m => m.Name == "Where")
- .MakeGenericMethod(otherTableType);
-
-
- Expression call = Expression.Call(whereMethod, otherTable.Expression, lambdaPredicate);
- //Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH")
-
- return otherTable.Provider.CreateQuery(call);
- }
-
- #endregion
-
- #region Insert/Update/Delete management
-
- /// <summary>
- /// Registers an entity for insert
- /// </summary>
- /// <param name="entity"></param>
- internal void RegisterInsert(object entity)
- {
- entityTracker.RegisterToInsert(entity);
- }
-
- /// <summary>
- /// Registers an entity for update
- /// The entity will be updated only if some of its members have changed after the registration
- /// </summary>
- /// <param name="entity"></param>
- internal void RegisterUpdate(object entity)
- {
- var identityReader = _GetIdentityReader(entity.GetType());
- var identityKey = identityReader.GetIdentityKey(entity);
- // if we have no key, we can not watch
- if (identityKey == null)
- return;
- // register entity
- entityTracker.RegisterToWatch(entity, identityKey);
- }
-
- /// <summary>
- /// Registers or re-registers an entity and clears its state
- /// </summary>
- /// <param name="entity"></param>
- /// <returns></returns>
- internal object Register(object entity)
- {
- var registeredEntity = _GetOrRegisterEntity(entity);
- // the fact of registering again clears the modified state, so we're... clear with that
- MemberModificationHandler.Register(registeredEntity, Mapping);
- return registeredEntity;
- }
-
- /// <summary>
- /// Registers an entity for update
- /// The entity will be updated only if some of its members have changed after the registration
- /// </summary>
- /// <param name="entity"></param>
- /// <param name="entityOriginalState"></param>
- internal void RegisterUpdate(object entity, object entityOriginalState)
- {
- RegisterUpdate(entity);
- MemberModificationHandler.Register(entity, entityOriginalState, Mapping);
- }
-
- /// <summary>
- /// Clears the current state, and marks the object as clean
- /// </summary>
- /// <param name="entity"></param>
- internal void RegisterUpdateAgain(object entity)
- {
- MemberModificationHandler.ClearModified(entity, Mapping);
- }
-
- /// <summary>
- /// Registers an entity for delete
- /// </summary>
- /// <param name="entity"></param>
- internal void RegisterDelete(object entity)
- {
- entityTracker.RegisterToDelete(entity);
- }
-
- /// <summary>
- /// Unregisters entity after deletion
- /// </summary>
- /// <param name="entity"></param>
- internal void UnregisterDelete(object entity)
- {
- entityTracker.RegisterDeleted(entity);
- }
-
- #endregion
-
- /// <summary>
- /// Changed object determine
- /// </summary>
- /// <returns>Lists of inserted, updated, deleted objects</returns>
- public ChangeSet GetChangeSet()
- {
- var inserts = new List<object>();
- var updates = new List<object>();
- var deletes = new List<object>();
- foreach (var entityTrack in entityTracker.EnumerateAll())
- {
- switch (entityTrack.EntityState)
- {
- case EntityState.ToInsert:
- inserts.Add(entityTrack.Entity);
- break;
- case EntityState.ToWatch:
- if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))
- updates.Add(entityTrack.Entity);
- break;
- case EntityState.ToDelete:
- deletes.Add(entityTrack.Entity);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- return new ChangeSet(inserts, updates, deletes);
- }
-
- /// <summary>
- /// use ExecuteCommand to call raw SQL
- /// </summary>
- public int ExecuteCommand(string command, params object[] parameters)
- {
- var directQuery = QueryBuilder.GetDirectQuery(command, new QueryContext(this));
- return QueryRunner.Execute(directQuery, parameters);
- }
-
- /// <summary>
- /// Execute raw SQL query and return object
- /// </summary>
- public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters) where TResult : class, new()
- {
- //GetTable<TResult>();
- foreach (TResult result in ExecuteQuery(typeof(TResult), query, parameters))
- yield return result;
- }
-
- public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters)
- {
- var queryContext = new QueryContext(this);
- var directQuery = QueryBuilder.GetDirectQuery(query, queryContext);
- return QueryRunner.ExecuteSelect(elementType, directQuery, parameters);
- }
-
- /// <summary>
- /// Gets or sets the load options
- /// </summary>
- [DbLinqToDo]
- public DataLoadOptions LoadOptions { get; set; }
-
- public DbTransaction Transaction { get; set; }
-
- /// <summary>
- /// Runs the given reader and returns columns.
- /// </summary>
- /// <typeparam name="TResult">The type of the result.</typeparam>
- /// <param name="reader">The reader.</param>
- /// <returns></returns>
- public IEnumerable<TResult> Translate<TResult>(DbDataReader reader)
- {
- foreach (TResult result in Translate(typeof(TResult), reader))
- yield return result;
- }
-
- public IMultipleResults Translate(DbDataReader reader)
- {
- throw new NotImplementedException();
- }
-
- public IEnumerable Translate(Type elementType, DbDataReader reader)
- {
- return QueryRunner.EnumerateResult(elementType, reader, this);
- }
-
- public void Dispose()
- {
- //connection closing should not be done here.
- //read: http://msdn2.microsoft.com/en-us/library/bb292288.aspx
- }
-
- [DbLinqToDo]
- protected virtual void Dispose(bool disposing)
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Creates a IDbDataAdapter. Used internally by Vendors
- /// </summary>
- /// <returns></returns>
- internal IDbDataAdapter CreateDataAdapter()
- {
- return DatabaseContext.CreateDataAdapter();
- }
-
- /// <summary>
- /// Sets a TextWriter where generated SQL commands are written
- /// </summary>
- public TextWriter Log { get; set; }
-
- /// <summary>
- /// Writes text on Log (if not null)
- /// Internal helper
- /// </summary>
- /// <param name="text"></param>
- internal void WriteLog(string text)
- {
- if (Log != null)
- Log.WriteLine(text);
- }
-
- /// <summary>
- /// Write an IDbCommand to Log (if non null)
- /// </summary>
- /// <param name="command"></param>
- internal void WriteLog(IDbCommand command)
- {
- if (Log != null)
- {
- Log.WriteLine(command.CommandText);
- foreach (IDbDataParameter parameter in command.Parameters)
- WriteLog(parameter);
- Log.Write("--");
- Log.Write(" Context: {0}", Vendor.VendorName);
- Log.Write(" Model: {0}", Mapping.GetType().Name);
- Log.Write(" Build: {0}", Assembly.GetExecutingAssembly().GetName().Version);
- Log.WriteLine();
- }
- }
-
- /// <summary>
- /// Writes and IDbDataParameter to Log (if non null)
- /// </summary>
- /// <param name="parameter"></param>
- internal void WriteLog(IDbDataParameter parameter)
- {
- if (Log != null)
- {
- // -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
- // -- <name>: <direction> <type> (...) [<value>]
- Log.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]",
- parameter.ParameterName, parameter.Direction, parameter.DbType,
- parameter.Size, parameter.Precision, parameter.Scale, parameter.Value);
- }
- }
-
-
- [DbLinqToDo]
- public bool ObjectTrackingEnabled
- {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
- }
-
- [DbLinqToDo]
- public int CommandTimeout
- {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
- }
-
- [DbLinqToDo]
- public bool DeferredLoadingEnabled
- {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
- }
-
- [DbLinqToDo]
- public ChangeConflictCollection ChangeConflicts
- {
- get { throw new NotImplementedException(); }
- }
-
- [DbLinqToDo]
- public DbCommand GetCommand(IQueryable query)
- {
- var qp = query.Provider as QueryProvider;
- if (qp == null)
- throw new InvalidOperationException();
-
- IDbCommand dbCommand = qp.GetQuery(null).GetCommand().Command;
- if (!(dbCommand is DbCommand))
- throw new InvalidOperationException();
-
- return (DbCommand)dbCommand;
- }
-
- [DbLinqToDo]
- public void Refresh(RefreshMode mode, IEnumerable entities)
- {
- throw new NotImplementedException();
- }
-
- [DbLinqToDo]
- public void Refresh(RefreshMode mode, params object[] entities)
- {
- throw new NotImplementedException();
- }
-
- [DbLinqToDo]
- public void Refresh(RefreshMode mode, object entity)
- {
- throw new NotImplementedException();
- }
-
- [DbLinqToDo]
- public void DeleteDatabase()
- {
- throw new NotImplementedException();
- }
-
- [DbLinqToDo]
- public void CreateDatabase()
- {
- throw new NotImplementedException();
- }
-
- [DbLinqToDo]
- protected internal IQueryable<TResult> CreateMethodCallQuery<TResult>(object instance, MethodInfo methodInfo, params object[] parameters)
- {
- throw new NotImplementedException();
- }
-
- [DbLinqToDo]
- protected internal void ExecuteDynamicDelete(object entity)
- {
- throw new NotImplementedException();
- }
-
- [DbLinqToDo]
- protected internal void ExecuteDynamicInsert(object entity)
- {
- throw new NotImplementedException();
- }
-
- [DbLinqToDo]
- protected internal void ExecuteDynamicUpdate(object entity)
- {
- throw new NotImplementedException();
- }
- }
-}
+#region MIT license\r
+// \r
+// MIT license\r
+//\r
+// Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
+// \r
+// Permission is hereby granted, free of charge, to any person obtaining a copy\r
+// of this software and associated documentation files (the "Software"), to deal\r
+// in the Software without restriction, including without limitation the rights\r
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+// copies of the Software, and to permit persons to whom the Software is\r
+// furnished to do so, subject to the following conditions:\r
+// \r
+// The above copyright notice and this permission notice shall be included in\r
+// all copies or substantial portions of the Software.\r
+// \r
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+// THE SOFTWARE.\r
+// \r
+#endregion\r
+\r
+using System;\r
+using System.Collections;\r
+using System.Data;\r
+using System.Data.Common;\r
+using System.Data.Linq.Mapping;\r
+using System.Collections.Generic;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Reflection;\r
+\r
+#if MONO_STRICT\r
+using System.Data.Linq.Implementation;\r
+using System.Data.Linq.Sugar;\r
+using System.Data.Linq.Identity;\r
+using DbLinq.Util;\r
+using AttributeMappingSource = System.Data.Linq.Mapping.AttributeMappingSource;\r
+using MappingContext = System.Data.Linq.Mapping.MappingContext;\r
+using DbLinq;\r
+#else\r
+using DbLinq.Data.Linq.Implementation;\r
+using DbLinq.Data.Linq.Sugar;\r
+using DbLinq.Data.Linq.Identity;\r
+using DbLinq.Util;\r
+using AttributeMappingSource = DbLinq.Data.Linq.Mapping.AttributeMappingSource;\r
+using MappingContext = DbLinq.Data.Linq.Mapping.MappingContext;\r
+using System.Data.Linq;\r
+#endif\r
+\r
+using DbLinq.Factory;\r
+using DbLinq.Vendor;\r
+using DbLinq.Data.Linq.Database;\r
+using DbLinq.Data.Linq.Database.Implementation;\r
+using System.Linq.Expressions;\r
+using System.Reflection.Emit;\r
+\r
+#if MONO_STRICT\r
+namespace System.Data.Linq\r
+#else\r
+namespace DbLinq.Data.Linq\r
+#endif\r
+{\r
+ public partial class DataContext : IDisposable\r
+ {\r
+ //private readonly Dictionary<string, ITable> _tableMap = new Dictionary<string, ITable>();\r
+ private readonly Dictionary<Type, ITable> _tableMap = new Dictionary<Type, ITable>();\r
+\r
+ public MetaModel Mapping { get; private set; }\r
+ // PC question: at ctor, we get a IDbConnection and the Connection property exposes a DbConnection\r
+ // WTF?\r
+ public DbConnection Connection { get { return DatabaseContext.Connection as DbConnection; } }\r
+\r
+ // all properties below are set public to optionally be injected\r
+ internal IVendor Vendor { get; set; }\r
+ internal IQueryBuilder QueryBuilder { get; set; }\r
+ internal IQueryRunner QueryRunner { get; set; }\r
+ internal IMemberModificationHandler MemberModificationHandler { get; set; }\r
+ internal IDatabaseContext DatabaseContext { get; private set; }\r
+ // /all properties...\r
+\r
+ private readonly EntityTracker entityTracker = new EntityTracker();\r
+\r
+ private IIdentityReaderFactory identityReaderFactory;\r
+ private readonly IDictionary<Type, IIdentityReader> identityReaders = new Dictionary<Type, IIdentityReader>();\r
+\r
+ /// <summary>\r
+ /// The default behavior creates one MappingContext.\r
+ /// </summary>\r
+ [DBLinqExtended]\r
+ internal virtual MappingContext _MappingContext { get; set; }\r
+\r
+ [DBLinqExtended]\r
+ internal IVendorProvider _VendorProvider { get; set; }\r
+\r
+ public DataContext(IDbConnection connection, MappingSource mapping)\r
+ {\r
+ Init(new DatabaseContext(connection), mapping, null);\r
+ }\r
+\r
+ public DataContext(IDbConnection connection)\r
+ {\r
+ Init(new DatabaseContext(connection), null, null);\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public DataContext(string fileOrServerOrConnection, MappingSource mapping)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Construct DataContext, given a connectionString.\r
+ /// To determine which DB type to go against, we look for 'DbLinqProvider=xxx' substring.\r
+ /// If not found, we assume that we are dealing with MS Sql Server.\r
+ /// \r
+ /// Valid values are names of provider DLLs (or any other DLL containing an IVendor implementation)\r
+ /// DbLinqProvider=Mysql\r
+ /// DbLinqProvider=Oracle etc.\r
+ /// </summary>\r
+ /// <param name="connectionString">specifies file or server connection</param>\r
+ [DbLinqToDo]\r
+ public DataContext(string connectionString)\r
+ {\r
+ #region DataContext connectionString ctor\r
+ if (connectionString == null)\r
+ throw new ArgumentNullException("connectionString");\r
+\r
+ System.Text.RegularExpressions.Regex reProvider\r
+ = new System.Text.RegularExpressions.Regex(@"DbLinqProvider=([\w\.]+)");\r
+\r
+ int startPos = connectionString.IndexOf("DbLinqProvider=");\r
+ string assemblyToLoad;\r
+ string vendorClassToLoad;\r
+ if (!reProvider.IsMatch(connectionString))\r
+ {\r
+ assemblyToLoad = "DbLinq.SqlServer.dll";\r
+ vendorClassToLoad = "SqlServerVendor";\r
+ }\r
+ else\r
+ {\r
+ System.Text.RegularExpressions.Match match = reProvider.Match(connectionString);\r
+#if MONO_STRICT\r
+ //Pascal says on the forum: \r
+ //[in MONO] "all vendors are (will be) embedded in the System.Data.Linq assembly"\r
+ assemblyToLoad = "System.Data.Linq.dll";\r
+ vendorClassToLoad = match.Groups[1].Value; //eg. "MySql"\r
+#else\r
+ //plain DbLinq - non MONO: \r
+ //IVendor classes are in DLLs such as "DbLinq.MySql.dll"\r
+ assemblyToLoad = match.Groups[1].Value; //eg. assemblyToLoad="DbLinq.MySql.dll"\r
+ if (assemblyToLoad.Contains("."))\r
+ {\r
+ //already fully qualified DLL name?\r
+ throw new ArgumentException("Please provide a short name, such as 'MySql', not '" + assemblyToLoad + "'");\r
+ }\r
+ else\r
+ {\r
+ //we were given short name, such as MySql\r
+ vendorClassToLoad = assemblyToLoad + "Vendor"; //eg. MySqlVendor\r
+ assemblyToLoad = "DbLinq." + assemblyToLoad + ".dll"; //eg. DbLinq.MySql.dll\r
+ }\r
+#endif\r
+ //shorten: "DbLinqProvider=X;Server=Y" -> ";Server=Y"\r
+ string shortenedConnStr = reProvider.Replace(connectionString, "");\r
+ connectionString = shortenedConnStr;\r
+ }\r
+\r
+ Assembly assy;\r
+ try\r
+ {\r
+#if MONO_STRICT\r
+ assy = typeof (DataContext).Assembly; // System.Data.Linq.dll\r
+#else\r
+ //TODO: check if DLL is already loaded?\r
+ assy = Assembly.LoadFrom(assemblyToLoad);\r
+#endif\r
+ }\r
+ catch (Exception ex)\r
+ {\r
+ //TODO: add proper logging here\r
+ Console.WriteLine("DataContext ctor: Assembly load failed for " + assemblyToLoad + ": " + ex);\r
+ throw ex;\r
+ }\r
+\r
+ //find IDbProvider class in this assembly:\r
+ var ctors = (from mod in assy.GetModules()\r
+ from cls in mod.GetTypes()\r
+ where cls.GetInterfaces().Contains(typeof(IVendor))\r
+ && cls.Name.ToLower() == vendorClassToLoad.ToLower()\r
+ let ctorInfo = cls.GetConstructor(Type.EmptyTypes)\r
+ where ctorInfo != null\r
+ select ctorInfo).ToList();\r
+ if (ctors.Count == 0)\r
+ {\r
+ string msg = "Found no IVendor class in assembly " + assemblyToLoad + " having a string ctor";\r
+ throw new ArgumentException(msg);\r
+ }\r
+ else if (ctors.Count > 1)\r
+ {\r
+ string msg = "Found more than one IVendor class in assembly " + assemblyToLoad + " having a string ctor";\r
+ throw new ArgumentException(msg);\r
+ }\r
+ ConstructorInfo ctorInfo2 = ctors[0];\r
+\r
+ object ivendorObject;\r
+ try\r
+ {\r
+ ivendorObject = ctorInfo2.Invoke(new object[]{});\r
+ }\r
+ catch (Exception ex)\r
+ {\r
+ //TODO: add proper logging here\r
+ Console.WriteLine("DataContext ctor: Failed to invoke IVendor ctor " + ctorInfo2.Name + ": " + ex);\r
+ throw ex;\r
+ }\r
+ IVendor ivendor = (IVendor)ivendorObject;\r
+ IDbConnection dbConnection = ivendor.CreateDbConnection(connectionString);\r
+ Init(new DatabaseContext(dbConnection), null, ivendor);\r
+ #endregion\r
+ }\r
+\r
+ private void Init(IDatabaseContext databaseContext, MappingSource mappingSource, IVendor vendor)\r
+ {\r
+ if (databaseContext == null)\r
+ throw new ArgumentNullException("databaseContext");\r
+\r
+ _VendorProvider = ObjectFactory.Get<IVendorProvider>();\r
+ if (vendor == null)\r
+ Vendor = _VendorProvider.FindVendorByProviderType(typeof(SqlClient.Sql2005Provider));\r
+ else\r
+ Vendor = vendor;\r
+\r
+ DatabaseContext = databaseContext;\r
+\r
+ MemberModificationHandler = ObjectFactory.Create<IMemberModificationHandler>(); // not a singleton: object is stateful\r
+ QueryBuilder = ObjectFactory.Get<IQueryBuilder>();\r
+ QueryRunner = ObjectFactory.Get<IQueryRunner>();\r
+\r
+ //EntityMap = ObjectFactory.Create<IEntityMap>();\r
+ identityReaderFactory = ObjectFactory.Get<IIdentityReaderFactory>();\r
+\r
+ _MappingContext = new MappingContext();\r
+\r
+ // initialize the mapping information\r
+ if (mappingSource == null)\r
+ mappingSource = new AttributeMappingSource();\r
+ Mapping = mappingSource.GetModel(GetType());\r
+ }\r
+\r
+ /// <summary>\r
+ /// Checks if the table is allready mapped or maps it if not.\r
+ /// </summary>\r
+ /// <param name="tableType">Type of the table.</param>\r
+ /// <exception cref="InvalidOperationException">Thrown if the table is not mappable.</exception>\r
+ private void CheckTableMapping(Type tableType)\r
+ {\r
+ //This will throw an exception if the table is not found\r
+ if(Mapping.GetTable(tableType) == null)\r
+ {\r
+ throw new InvalidOperationException("The type '" + tableType.Name + "' is not mapped as a Table.");\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Returns a Table for the type TEntity.\r
+ /// </summary>\r
+ /// <exception cref="InvalidOperationException">If the type TEntity is not mappable as a Table.</exception>\r
+ /// <typeparam name="TEntity">The table type.</typeparam>\r
+ public Table<TEntity> GetTable<TEntity>() where TEntity : class\r
+ {\r
+ return (Table<TEntity>)GetTable(typeof(TEntity));\r
+ }\r
+\r
+ /// <summary>\r
+ /// Returns a Table for the given type.\r
+ /// </summary>\r
+ /// <param name="type">The table type.</param>\r
+ /// <exception cref="InvalidOperationException">If the type is not mappable as a Table.</exception>\r
+ public ITable GetTable(Type type)\r
+ {\r
+ lock (_tableMap)\r
+ {\r
+ ITable tableExisting;\r
+ if (_tableMap.TryGetValue(type, out tableExisting))\r
+ return tableExisting;\r
+\r
+ //Check for table mapping\r
+ CheckTableMapping(type);\r
+\r
+ var tableNew = Activator.CreateInstance(\r
+ typeof(Table<>).MakeGenericType(type)\r
+ , BindingFlags.NonPublic | BindingFlags.Instance\r
+ , null\r
+ , new object[] { this }\r
+ , System.Globalization.CultureInfo.CurrentCulture) as ITable;\r
+\r
+ _tableMap[type] = tableNew;\r
+ return tableNew;\r
+ }\r
+ }\r
+\r
+ public void SubmitChanges()\r
+ {\r
+ SubmitChanges(ConflictMode.FailOnFirstConflict);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Pings database\r
+ /// </summary>\r
+ /// <returns></returns>\r
+ public bool DatabaseExists()\r
+ {\r
+ try\r
+ {\r
+ return Vendor.Ping(this);\r
+ }\r
+ catch (Exception)\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Commits all pending changes to database \r
+ /// </summary>\r
+ /// <param name="failureMode"></param>\r
+ public virtual void SubmitChanges(ConflictMode failureMode)\r
+ {\r
+ using (DatabaseContext.OpenConnection()) //ConnMgr will close connection for us\r
+ using (IDatabaseTransaction transaction = DatabaseContext.Transaction())\r
+ {\r
+ var queryContext = new QueryContext(this);\r
+ var entityTracks = entityTracker.EnumerateAll().ToList();\r
+ foreach (var entityTrack in entityTracks)\r
+ {\r
+ switch (entityTrack.EntityState)\r
+ {\r
+ case EntityState.ToInsert:\r
+ var insertQuery = QueryBuilder.GetInsertQuery(entityTrack.Entity, queryContext);\r
+ QueryRunner.Insert(entityTrack.Entity, insertQuery);\r
+ Register(entityTrack.Entity);\r
+ break;\r
+ case EntityState.ToWatch:\r
+ if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))\r
+ {\r
+ var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entityTrack.Entity, Mapping);\r
+ var updateQuery = QueryBuilder.GetUpdateQuery(entityTrack.Entity, modifiedMembers, queryContext);\r
+ QueryRunner.Update(entityTrack.Entity, updateQuery, modifiedMembers);\r
+\r
+ RegisterUpdateAgain(entityTrack.Entity);\r
+ }\r
+ break;\r
+ case EntityState.ToDelete:\r
+ var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext);\r
+ QueryRunner.Delete(entityTrack.Entity, deleteQuery);\r
+\r
+ UnregisterDelete(entityTrack.Entity);\r
+ break;\r
+ default:\r
+ throw new ArgumentOutOfRangeException();\r
+ }\r
+ }\r
+ // TODO: handle conflicts (which can only occur when concurrency mode is implemented)\r
+ transaction.Commit();\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// TODO - allow generated methods to call into stored procedures\r
+ /// </summary>\r
+ [DBLinqExtended]\r
+ internal IExecuteResult _ExecuteMethodCall(DataContext context, System.Reflection.MethodInfo method, params object[] sqlParams)\r
+ {\r
+ using (DatabaseContext.OpenConnection())\r
+ {\r
+ System.Data.Linq.IExecuteResult result = Vendor.ExecuteMethodCall(context, method, sqlParams);\r
+ return result;\r
+ }\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ protected IExecuteResult ExecuteMethodCall(object instance, System.Reflection.MethodInfo methodInfo, params object[] parameters)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ #region Identity management\r
+\r
+ [DBLinqExtended]\r
+ internal IIdentityReader _GetIdentityReader(Type t)\r
+ {\r
+ IIdentityReader identityReader;\r
+ lock (identityReaders)\r
+ {\r
+ if (!identityReaders.TryGetValue(t, out identityReader))\r
+ {\r
+ identityReader = identityReaderFactory.GetReader(t, this);\r
+ identityReaders[t] = identityReader;\r
+ }\r
+ }\r
+ return identityReader;\r
+ }\r
+\r
+ [DBLinqExtended]\r
+ internal object _GetRegisteredEntity(object entity)\r
+ {\r
+ // TODO: check what is faster: by identity or by ref\r
+ var identityReader = _GetIdentityReader(entity.GetType());\r
+ var identityKey = identityReader.GetIdentityKey(entity);\r
+ if (identityKey == null) // if we don't have an entitykey here, it means that the entity has no PK\r
+ return entity;\r
+ // even \r
+ var registeredEntityTrack = entityTracker.FindByIdentity(identityKey);\r
+ if (registeredEntityTrack != null)\r
+ return registeredEntityTrack.Entity;\r
+ return null;\r
+ }\r
+\r
+ //internal object GetRegisteredEntityByKey(IdentityKey identityKey)\r
+ //{\r
+ // return EntityMap[identityKey];\r
+ //}\r
+\r
+ /// <summary>\r
+ /// Registers an entity in a watch state\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ /// <returns></returns>\r
+ [DBLinqExtended]\r
+ internal object _GetOrRegisterEntity(object entity)\r
+ {\r
+ var identityReader = _GetIdentityReader(entity.GetType());\r
+ var identityKey = identityReader.GetIdentityKey(entity);\r
+ SetEntitySetsQueries(entity);\r
+ SetEntityRefQueries(entity);\r
+\r
+ // if we have no identity, we can't track it\r
+ if (identityKey == null)\r
+ return entity;\r
+\r
+ // try to find an already registered entity and return it\r
+ var registeredEntityTrack = entityTracker.FindByIdentity(identityKey);\r
+ if (registeredEntityTrack != null)\r
+ return registeredEntityTrack.Entity;\r
+\r
+ // otherwise, register and return\r
+ entityTracker.RegisterToWatch(entity, identityKey);\r
+ return entity;\r
+ }\r
+\r
+ readonly IDataMapper DataMapper = ObjectFactory.Get<IDataMapper>();\r
+ private void SetEntityRefQueries(object entity)\r
+ {\r
+ Type thisType = entity.GetType();\r
+ IList<MemberInfo> properties = DataMapper.GetEntityRefAssociations(thisType);\r
+\r
+\r
+ foreach (PropertyInfo prop in properties)\r
+ {\r
+ //example of entityRef:Order.Employee\r
+ AssociationAttribute associationInfo = prop.GetAttribute<AssociationAttribute>();\r
+ Type otherTableType = prop.PropertyType;\r
+ IList<MemberInfo> otherPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(otherTableType));\r
+\r
+ if (otherPKs.Count > 1)\r
+ throw new NotSupportedException("Multiple keys object not supported yet.");\r
+\r
+ var otherTable = GetTable(otherTableType);\r
+\r
+ //ie:EmployeeTerritories.EmployeeID\r
+\r
+ var thisForeignKeyProperty = thisType.GetProperty(associationInfo.ThisKey);\r
+ object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null);\r
+\r
+ IEnumerable query = null;\r
+ if (thisForeignKeyValue != null)\r
+ {\r
+ ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
+ Expression predicate;\r
+ if (!(thisForeignKeyProperty.PropertyType.IsNullable()))\r
+ {\r
+ predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()),\r
+ Expression.Constant(thisForeignKeyValue));\r
+ }\r
+ else\r
+ {\r
+ var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value");\r
+ predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()),\r
+ Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null)));\r
+ }\r
+\r
+ query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable;\r
+ //it would be interesting surround the above query with a .Take(1) expression for performance.\r
+ }\r
+\r
+\r
+ FieldInfo entityRefField = entity.GetType().GetField(associationInfo.Storage, BindingFlags.NonPublic | BindingFlags.Instance);\r
+ object entityRefValue = null;\r
+ if (query != null)\r
+ entityRefValue = Activator.CreateInstance(entityRefField.FieldType, query);\r
+ else\r
+ entityRefValue = Activator.CreateInstance(entityRefField.FieldType);\r
+ entityRefField.SetValue(entity, entityRefValue);\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// This method is executed when the entity is being registered. Each EntitySet property has a internal query that can be set using the EntitySet.SetSource method.\r
+ /// Here we set the query source of each EntitySetProperty\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ private void SetEntitySetsQueries(object entity)\r
+ {\r
+ IList<MemberInfo> properties = DataMapper.GetEntitySetAssociations(entity.GetType());\r
+ \r
+ if (properties.Any()) {\r
+ IList<MemberInfo> thisPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(entity.GetType()));\r
+\r
+ if (thisPKs.Count > 1)\r
+ throw new NotSupportedException("Multiple keys object not supported yet.");\r
+\r
+ object primaryKeyValue = (thisPKs.First() as PropertyInfo).GetValue(entity, null);\r
+\r
+\r
+ foreach (PropertyInfo prop in properties)\r
+ {\r
+ //example of entitySet: Employee.EmployeeTerritories\r
+ var associationInfo = prop.GetAttribute<AssociationAttribute>();\r
+ Type otherTableType = prop.PropertyType.GetGenericArguments().First();\r
+\r
+ //other table:EmployeeTerritories\r
+ var otherTable = GetTable(otherTableType);\r
+ //other table member:EmployeeTerritories.EmployeeID\r
+ var otherTableMember = otherTableType.GetProperty(associationInfo.OtherKey);\r
+\r
+\r
+ ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
+ Expression predicate;\r
+ if (!(otherTableMember.PropertyType.IsNullable()))\r
+ {\r
+ predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember),\r
+ Expression.Constant(primaryKeyValue));\r
+ }\r
+ else\r
+ {\r
+ var ValueProperty = otherTableMember.PropertyType.GetProperty("Value");\r
+ predicate = Expression.Equal(Expression.MakeMemberAccess(\r
+ Expression.MakeMemberAccess(p, otherTableMember),\r
+ ValueProperty),\r
+ Expression.Constant(primaryKeyValue));\r
+ }\r
+\r
+ var query = GetOtherTableQuery(predicate, p, otherTableType, otherTable);\r
+\r
+ var entitySetValue = prop.GetValue(entity, null);\r
+\r
+ if (entitySetValue == null)\r
+ {\r
+ entitySetValue = Activator.CreateInstance(prop.PropertyType);\r
+ prop.SetValue(entity, entitySetValue, null);\r
+ }\r
+\r
+ var setSourceMethod = entitySetValue.GetType().GetMethod("SetSource");\r
+ setSourceMethod.Invoke(entitySetValue, new[] { query });\r
+ //employee.EmployeeTerritories.SetSource(Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH"))\r
+ }\r
+ }\r
+ }\r
+\r
+ private object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable)\r
+ {\r
+ //predicate: other.EmployeeID== "WARTH"\r
+ Expression lambdaPredicate = Expression.Lambda(predicate, parameter);\r
+ //lambdaPredicate: other=>other.EmployeeID== "WARTH"\r
+\r
+ var whereMethod = typeof(Queryable)\r
+ .GetMethods().First(m => m.Name == "Where")\r
+ .MakeGenericMethod(otherTableType);\r
+\r
+\r
+ Expression call = Expression.Call(whereMethod, otherTable.Expression, lambdaPredicate);\r
+ //Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH")\r
+\r
+ return otherTable.Provider.CreateQuery(call);\r
+ }\r
+\r
+ #endregion\r
+\r
+ #region Insert/Update/Delete management\r
+\r
+ /// <summary>\r
+ /// Registers an entity for insert\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ internal void RegisterInsert(object entity)\r
+ {\r
+ entityTracker.RegisterToInsert(entity);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Registers an entity for update\r
+ /// The entity will be updated only if some of its members have changed after the registration\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ internal void RegisterUpdate(object entity)\r
+ {\r
+ var identityReader = _GetIdentityReader(entity.GetType());\r
+ var identityKey = identityReader.GetIdentityKey(entity);\r
+ // if we have no key, we can not watch\r
+ if (identityKey == null)\r
+ return;\r
+ // register entity\r
+ entityTracker.RegisterToWatch(entity, identityKey);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Registers or re-registers an entity and clears its state\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ /// <returns></returns>\r
+ internal object Register(object entity)\r
+ {\r
+ var registeredEntity = _GetOrRegisterEntity(entity);\r
+ // the fact of registering again clears the modified state, so we're... clear with that\r
+ MemberModificationHandler.Register(registeredEntity, Mapping);\r
+ return registeredEntity;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Registers an entity for update\r
+ /// The entity will be updated only if some of its members have changed after the registration\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ /// <param name="entityOriginalState"></param>\r
+ internal void RegisterUpdate(object entity, object entityOriginalState)\r
+ {\r
+ RegisterUpdate(entity);\r
+ MemberModificationHandler.Register(entity, entityOriginalState, Mapping);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Clears the current state, and marks the object as clean\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ internal void RegisterUpdateAgain(object entity)\r
+ {\r
+ MemberModificationHandler.ClearModified(entity, Mapping);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Registers an entity for delete\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ internal void RegisterDelete(object entity)\r
+ {\r
+ entityTracker.RegisterToDelete(entity);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Unregisters entity after deletion\r
+ /// </summary>\r
+ /// <param name="entity"></param>\r
+ internal void UnregisterDelete(object entity)\r
+ {\r
+ entityTracker.RegisterDeleted(entity);\r
+ }\r
+\r
+ #endregion\r
+\r
+ /// <summary>\r
+ /// Changed object determine \r
+ /// </summary>\r
+ /// <returns>Lists of inserted, updated, deleted objects</returns>\r
+ public ChangeSet GetChangeSet()\r
+ {\r
+ var inserts = new List<object>();\r
+ var updates = new List<object>();\r
+ var deletes = new List<object>();\r
+ foreach (var entityTrack in entityTracker.EnumerateAll())\r
+ {\r
+ switch (entityTrack.EntityState)\r
+ {\r
+ case EntityState.ToInsert:\r
+ inserts.Add(entityTrack.Entity);\r
+ break;\r
+ case EntityState.ToWatch:\r
+ if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))\r
+ updates.Add(entityTrack.Entity);\r
+ break;\r
+ case EntityState.ToDelete:\r
+ deletes.Add(entityTrack.Entity);\r
+ break;\r
+ default:\r
+ throw new ArgumentOutOfRangeException();\r
+ }\r
+ }\r
+ return new ChangeSet(inserts, updates, deletes);\r
+ }\r
+\r
+ /// <summary>\r
+ /// use ExecuteCommand to call raw SQL\r
+ /// </summary>\r
+ public int ExecuteCommand(string command, params object[] parameters)\r
+ {\r
+ var directQuery = QueryBuilder.GetDirectQuery(command, new QueryContext(this));\r
+ return QueryRunner.Execute(directQuery, parameters);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Execute raw SQL query and return object\r
+ /// </summary>\r
+ public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters) where TResult : class, new()\r
+ {\r
+ //GetTable<TResult>();\r
+ foreach (TResult result in ExecuteQuery(typeof(TResult), query, parameters))\r
+ yield return result;\r
+ }\r
+\r
+ public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters)\r
+ {\r
+ var queryContext = new QueryContext(this);\r
+ var directQuery = QueryBuilder.GetDirectQuery(query, queryContext);\r
+ return QueryRunner.ExecuteSelect(elementType, directQuery, parameters);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the load options\r
+ /// </summary>\r
+ [DbLinqToDo]\r
+ public DataLoadOptions LoadOptions { get; set; }\r
+\r
+ public DbTransaction Transaction { get; set; }\r
+\r
+ /// <summary>\r
+ /// Runs the given reader and returns columns.\r
+ /// </summary>\r
+ /// <typeparam name="TResult">The type of the result.</typeparam>\r
+ /// <param name="reader">The reader.</param>\r
+ /// <returns></returns>\r
+ public IEnumerable<TResult> Translate<TResult>(DbDataReader reader)\r
+ {\r
+ foreach (TResult result in Translate(typeof(TResult), reader))\r
+ yield return result;\r
+ }\r
+\r
+ public IMultipleResults Translate(DbDataReader reader)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ public IEnumerable Translate(Type elementType, DbDataReader reader)\r
+ {\r
+ return QueryRunner.EnumerateResult(elementType, reader, this);\r
+ }\r
+\r
+ public void Dispose()\r
+ {\r
+ //connection closing should not be done here.\r
+ //read: http://msdn2.microsoft.com/en-us/library/bb292288.aspx\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ protected virtual void Dispose(bool disposing)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Creates a IDbDataAdapter. Used internally by Vendors\r
+ /// </summary>\r
+ /// <returns></returns>\r
+ internal IDbDataAdapter CreateDataAdapter()\r
+ {\r
+ return DatabaseContext.CreateDataAdapter();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Sets a TextWriter where generated SQL commands are written\r
+ /// </summary>\r
+ public TextWriter Log { get; set; }\r
+\r
+ /// <summary>\r
+ /// Writes text on Log (if not null)\r
+ /// Internal helper\r
+ /// </summary>\r
+ /// <param name="text"></param>\r
+ internal void WriteLog(string text)\r
+ {\r
+ if (Log != null)\r
+ Log.WriteLine(text);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Write an IDbCommand to Log (if non null)\r
+ /// </summary>\r
+ /// <param name="command"></param>\r
+ internal void WriteLog(IDbCommand command)\r
+ {\r
+ if (Log != null)\r
+ {\r
+ Log.WriteLine(command.CommandText);\r
+ foreach (IDbDataParameter parameter in command.Parameters)\r
+ WriteLog(parameter);\r
+ Log.Write("--");\r
+ Log.Write(" Context: {0}", Vendor.VendorName);\r
+ Log.Write(" Model: {0}", Mapping.GetType().Name);\r
+ Log.Write(" Build: {0}", Assembly.GetExecutingAssembly().GetName().Version);\r
+ Log.WriteLine();\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Writes and IDbDataParameter to Log (if non null)\r
+ /// </summary>\r
+ /// <param name="parameter"></param>\r
+ internal void WriteLog(IDbDataParameter parameter)\r
+ {\r
+ if (Log != null)\r
+ {\r
+ // -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2]\r
+ // -- <name>: <direction> <type> (...) [<value>]\r
+ Log.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]",\r
+ parameter.ParameterName, parameter.Direction, parameter.DbType,\r
+ parameter.Size, parameter.Precision, parameter.Scale, parameter.Value);\r
+ }\r
+ }\r
+\r
+\r
+ [DbLinqToDo]\r
+ public bool ObjectTrackingEnabled\r
+ {\r
+ get { throw new NotImplementedException(); }\r
+ set { throw new NotImplementedException(); }\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public int CommandTimeout\r
+ {\r
+ get { throw new NotImplementedException(); }\r
+ set { throw new NotImplementedException(); }\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public bool DeferredLoadingEnabled\r
+ {\r
+ get { throw new NotImplementedException(); }\r
+ set { throw new NotImplementedException(); }\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public ChangeConflictCollection ChangeConflicts\r
+ {\r
+ get { throw new NotImplementedException(); }\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public DbCommand GetCommand(IQueryable query)\r
+ {\r
+ var qp = query.Provider as QueryProvider;\r
+ if (qp == null)\r
+ throw new InvalidOperationException();\r
+\r
+ IDbCommand dbCommand = qp.GetQuery(null).GetCommand().Command;\r
+ if (!(dbCommand is DbCommand))\r
+ throw new InvalidOperationException();\r
+\r
+ return (DbCommand)dbCommand;\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public void Refresh(RefreshMode mode, IEnumerable entities)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public void Refresh(RefreshMode mode, params object[] entities)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public void Refresh(RefreshMode mode, object entity)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public void DeleteDatabase()\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ public void CreateDatabase()\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ protected internal IQueryable<TResult> CreateMethodCallQuery<TResult>(object instance, MethodInfo methodInfo, params object[] parameters)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ protected internal void ExecuteDynamicDelete(object entity)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ protected internal void ExecuteDynamicInsert(object entity)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ protected internal void ExecuteDynamicUpdate(object entity)\r
+ {\r
+ throw new NotImplementedException();\r
+ }\r
+ }\r
+}\r
-#region MIT license
-//
-// MIT license
-//
-// Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-#endregion
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Text;
-using System.Linq;
-using System.Linq.Expressions;
-using System.ComponentModel;
-using DbLinq;
-
-#if MONO_STRICT
-namespace System.Data.Linq
-#else
-namespace DbLinq.Data.Linq
-#endif
-{
- public sealed class EntitySet<TEntity> : ICollection, ICollection<TEntity>, IEnumerable, IEnumerable<TEntity>, IList, IList<TEntity>, IListSource
- where TEntity : class
- {
- private readonly Action<TEntity> onAdd;
- private readonly Action<TEntity> onRemove;
-
- private IEnumerable<TEntity> Source;
- private List<TEntity> SourceInUse;
- private List<TEntity> sourceAsList
- {
- get
- {
- if (SourceInUse == null)
- {
- SourceInUse = Source.ToList();
- }
- return SourceInUse;
- }
- }
-
-
- /// <summary>
- /// Initializes a new instance of the <see cref="EntitySet<TEntity>"/> class.
- /// </summary>
- /// <param name="onAdd">The on add.</param>
- /// <param name="onRemove">The on remove.</param>
- [DbLinqToDo]
- public EntitySet(Action<TEntity> onAdd, Action<TEntity> onRemove)
- : this()
- {
- this.onAdd = onAdd;
- this.onRemove = onRemove;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="EntitySet<TEntity>"/> class.
- /// </summary>
- public EntitySet()
- {
- Source = System.Linq.Enumerable.Empty<TEntity>();
- }
-
- /// <summary>
- /// entry point for 'foreach' statement.
- /// </summary>
- public IEnumerator<TEntity> GetEnumerator()
- {
- return Source.GetEnumerator();
- //vars = GetVars();
- //IEnumerator<T> enumerator = new RowEnumerator<T>(vars);
- //return (IEnumerator<T>)enumerator;
- }
-
- /// <summary>
- /// Enumerates all entities
- /// </summary>
- /// <returns></returns>
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- /// <summary>
- /// Gets the source expression (used to nest queries)
- /// </summary>
- /// <value>The expression.</value>
- internal Expression Expression
- {
- get
- {
- if (this.Source is IQueryable<TEntity>)
- return (Source as IQueryable<TEntity>).Expression;
- else
- return Expression.Constant(this);
- }
- }
-
- /// <summary>
- /// TODO: Add(row)
- /// </summary>
- public void Add(TEntity entity)
- {
- if (Contains (entity))
- return;
- sourceAsList.Add(entity);
- OnAdd(entity);
- }
-
- [DbLinqToDo]
- bool IListSource.ContainsListCollection
- {
- get { throw new NotImplementedException(); }
- }
-
- IList IListSource.GetList()
- {
- return sourceAsList;
- }
-
- #region IList<TEntity> Members
-
- /// <summary>
- /// Returns entity's index
- /// </summary>
- /// <param name="entity">The entity.</param>
- /// <returns></returns>
- public int IndexOf(TEntity entity)
- {
- return sourceAsList.IndexOf(entity);
- }
-
- /// <summary>
- /// Inserts entity at specified index.
- /// </summary>
- /// <param name="index">The index.</param>
- /// <param name="entity">The entity.</param>
- public void Insert(int index, TEntity entity)
- {
- if (Contains(entity))
- throw new ArgumentOutOfRangeException();
- OnAdd(entity);
- sourceAsList.Insert(index, entity);
- }
-
- /// <summary>
- /// Removes entity as specified index
- /// </summary>
- /// <param name="index"></param>
- public void RemoveAt(int index)
- {
- OnRemove(sourceAsList[index]);
- sourceAsList.RemoveAt(index);
- }
-
- /// <summary>
- /// Gets or sets the <see cref="TEntity"/> at the specified index.
- /// </summary>
- /// <value></value>
- public TEntity this[int index]
- {
- get
- {
- return Source.ElementAt(index);
- }
- set
- {
- OnRemove(sourceAsList[index]);
- OnAdd(value);
- sourceAsList[index] = value;
- }
- }
-
- #endregion
-
- #region ICollection<TEntity> Members
-
- /// <summary>
- /// Removes all items in collection
- /// </summary>
- public void Clear()
- {
- while (sourceAsList.Count > 0)
- OnRemove(sourceAsList [0]);
- Source = Enumerable.Empty<TEntity>();
- }
-
- /// <summary>
- /// Determines whether [contains] [the specified entity].
- /// </summary>
- /// <param name="entity">The entity.</param>
- /// <returns>
- /// <c>true</c> if [contains] [the specified entity]; otherwise, <c>false</c>.
- /// </returns>
- public bool Contains(TEntity entity)
- {
- return Source.Contains(entity);
- }
-
- /// <summary>
- /// Copies items to target array
- /// </summary>
- /// <param name="array"></param>
- /// <param name="arrayIndex"></param>
- public void CopyTo(TEntity[] array, int arrayIndex)
- {
- array = this.Source.Skip(arrayIndex).ToArray();
- }
-
- /// <summary>
- /// Returns entities count
- /// </summary>
- public int Count
- {
- get
- {
- return sourceAsList.Count;
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
- /// </summary>
- /// <value></value>
- /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
- /// </returns>
- bool ICollection<TEntity>.IsReadOnly
- {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Removes the specified entity.
- /// </summary>
- /// <param name="entity">The entity.</param>
- /// <returns></returns>
- public bool Remove(TEntity entity)
- {
- OnRemove(entity);
- return sourceAsList.Remove(entity);
- }
-
- #endregion
-
- #region IList Members
-
- int IList.Add(object value)
- {
- if (value is TEntity)
- {
- var v = value as TEntity;
- if (this.IndexOf(v) >= 0)
- throw new ArgumentOutOfRangeException();
- this.Add(v);
- return this.Count - 1;
- }
- else
- throw new NotSupportedException();
- }
-
- void IList.Clear()
- {
- this.Clear();
- }
-
- bool IList.Contains(object value)
- {
- if (value is TEntity)
- return this.Contains(value as TEntity);
- else
- return false;
- }
-
- int IList.IndexOf(object value)
- {
- return this.IndexOf(value as TEntity);
- }
-
- void IList.Insert(int index, object value)
- {
- this.Insert(index, value as TEntity);
- }
-
- bool IList.IsFixedSize
- {
- get { return false; }
- }
-
- bool IList.IsReadOnly
- {
- get { return false; }
- }
-
- void IList.Remove(object value)
- {
- this.Remove(value as TEntity);
- }
-
- void IList.RemoveAt(int index)
- {
- this.RemoveAt(index);
- }
-
- object IList.this[int index]
- {
- get
- {
- return this[index];
- }
- set
- {
- this[index] = value as TEntity;
- }
- }
-
- #endregion
-
- #region ICollection Members
-
- void ICollection.CopyTo(Array array, int index)
- {
- this.CopyTo(array as TEntity[], index);
- }
-
- int ICollection.Count
- {
- get { return this.Count; }
- }
-
- [DbLinqToDo]
- bool ICollection.IsSynchronized
- {
- get { throw new NotImplementedException(); }
- }
-
- [DbLinqToDo]
- object ICollection.SyncRoot
- {
- get { throw new NotImplementedException(); }
- }
-
- #endregion
-
- /// <summary>
- /// Gets a value indicating whether this instance is deferred.
- /// </summary>
- /// <value>
- /// <c>true</c> if this instance is deferred; otherwise, <c>false</c>.
- /// </value>
- public bool IsDeferred
- {
- get { return Source is IQueryable; }
- }
-
- /// <summary>
- /// Adds the range.
- /// </summary>
- /// <param name="collection">The collection.</param>
- public void AddRange(IEnumerable<TEntity> collection)
- {
- if (onAdd != null)
- {
- foreach (var entity in collection)
- {
- Add(entity);
- }
- }
- }
-
- /// <summary>
- /// Assigns the specified entity source.
- /// </summary>
- /// <param name="entitySource">The entity source.</param>
- public void Assign(IEnumerable<TEntity> entitySource)
- {
- // notifies removals and adds
- Clear();
- foreach (var entity in entitySource)
- OnAdd(entity);
- this.Source = entitySource;
- this.SourceInUse = sourceAsList;
- }
-
- /// <summary>
- /// Sets the entity source.
- /// </summary>
- /// <param name="entitySource">The entity source.</param>
- public void SetSource(IEnumerable<TEntity> entitySource)
- {
- if(HasLoadedOrAssignedValues)
- throw new InvalidOperationException("The EntitySet is already loaded and the source cannot be changed.");
- this.Source = entitySource;
- }
-
- /// <summary>
- /// Loads all entities.
- /// </summary>
- public void Load()
- {
- this.sourceAsList.Count();
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has loaded or assigned values.
- /// </summary>
- /// <value>
- /// <c>true</c> if this instance has loaded or assigned values; otherwise, <c>false</c>.
- /// </value>
- public bool HasLoadedOrAssignedValues
- {
- get { return SourceInUse != null; }
- }
-
- /// <summary>
- /// Gets a new binding list.
- /// </summary>
- /// <returns></returns>
- public IBindingList GetNewBindingList()
- {
- return new BindingList<TEntity>(Source.ToList());
- }
-
- // TODO: implement handler call
- public event ListChangedEventHandler ListChanged;
-
- /// <summary>
- /// Called when entity is added.
- /// </summary>
- /// <param name="entity">The entity.</param>
- private void OnAdd(TEntity entity)
- {
- if (onAdd != null)
- onAdd(entity);
- }
- /// <summary>
- /// Called when entity is removed
- /// </summary>
- /// <param name="entity">The entity.</param>
- private void OnRemove(TEntity entity)
- {
- if (onRemove != null)
- onRemove(entity);
- }
- }
-}
+#region MIT license\r
+// \r
+// MIT license\r
+//\r
+// Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
+// \r
+// Permission is hereby granted, free of charge, to any person obtaining a copy\r
+// of this software and associated documentation files (the "Software"), to deal\r
+// in the Software without restriction, including without limitation the rights\r
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+// copies of the Software, and to permit persons to whom the Software is\r
+// furnished to do so, subject to the following conditions:\r
+// \r
+// The above copyright notice and this permission notice shall be included in\r
+// all copies or substantial portions of the Software.\r
+// \r
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+// THE SOFTWARE.\r
+// \r
+#endregion\r
+\r
+using System;\r
+using System.Collections;\r
+using System.Collections.Generic;\r
+using System.Text;\r
+using System.Linq;\r
+using System.Linq.Expressions;\r
+using System.ComponentModel;\r
+using DbLinq;\r
+\r
+#if MONO_STRICT\r
+namespace System.Data.Linq\r
+#else\r
+namespace DbLinq.Data.Linq\r
+#endif\r
+{\r
+ public sealed class EntitySet<TEntity> : ICollection, ICollection<TEntity>, IEnumerable, IEnumerable<TEntity>, IList, IList<TEntity>, IListSource\r
+ where TEntity : class\r
+ {\r
+ private readonly Action<TEntity> onAdd;\r
+ private readonly Action<TEntity> onRemove;\r
+\r
+ private IEnumerable<TEntity> Source;\r
+ private List<TEntity> SourceInUse;\r
+ private List<TEntity> sourceAsList\r
+ {\r
+ get\r
+ {\r
+ if (SourceInUse == null)\r
+ {\r
+ SourceInUse = Source.ToList();\r
+ }\r
+ return SourceInUse;\r
+ }\r
+ }\r
+\r
+\r
+ /// <summary>\r
+ /// Initializes a new instance of the <see cref="EntitySet<TEntity>"/> class.\r
+ /// </summary>\r
+ /// <param name="onAdd">The on add.</param>\r
+ /// <param name="onRemove">The on remove.</param>\r
+ [DbLinqToDo]\r
+ public EntitySet(Action<TEntity> onAdd, Action<TEntity> onRemove)\r
+ : this()\r
+ {\r
+ this.onAdd = onAdd;\r
+ this.onRemove = onRemove;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Initializes a new instance of the <see cref="EntitySet<TEntity>"/> class.\r
+ /// </summary>\r
+ public EntitySet()\r
+ {\r
+ Source = System.Linq.Enumerable.Empty<TEntity>();\r
+ }\r
+\r
+ /// <summary>\r
+ /// entry point for 'foreach' statement.\r
+ /// </summary>\r
+ public IEnumerator<TEntity> GetEnumerator()\r
+ {\r
+ return Source.GetEnumerator();\r
+ //vars = GetVars();\r
+ //IEnumerator<T> enumerator = new RowEnumerator<T>(vars);\r
+ //return (IEnumerator<T>)enumerator;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Enumerates all entities\r
+ /// </summary>\r
+ /// <returns></returns>\r
+ IEnumerator IEnumerable.GetEnumerator()\r
+ {\r
+ return GetEnumerator();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Gets the source expression (used to nest queries)\r
+ /// </summary>\r
+ /// <value>The expression.</value>\r
+ internal Expression Expression\r
+ {\r
+ get\r
+ {\r
+ if (this.Source is IQueryable<TEntity>)\r
+ return (Source as IQueryable<TEntity>).Expression;\r
+ else\r
+ return Expression.Constant(this);\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// TODO: Add(row)\r
+ /// </summary>\r
+ public void Add(TEntity entity)\r
+ {\r
+ if (Contains (entity))\r
+ return;\r
+ sourceAsList.Add(entity);\r
+ OnAdd(entity);\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ bool IListSource.ContainsListCollection\r
+ {\r
+ get { throw new NotImplementedException(); }\r
+ }\r
+\r
+ IList IListSource.GetList()\r
+ {\r
+ return sourceAsList;\r
+ }\r
+\r
+ #region IList<TEntity> Members\r
+\r
+ /// <summary>\r
+ /// Returns entity's index\r
+ /// </summary>\r
+ /// <param name="entity">The entity.</param>\r
+ /// <returns></returns>\r
+ public int IndexOf(TEntity entity)\r
+ {\r
+ return sourceAsList.IndexOf(entity);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Inserts entity at specified index.\r
+ /// </summary>\r
+ /// <param name="index">The index.</param>\r
+ /// <param name="entity">The entity.</param>\r
+ public void Insert(int index, TEntity entity)\r
+ {\r
+ if (Contains(entity))\r
+ throw new ArgumentOutOfRangeException();\r
+ OnAdd(entity);\r
+ sourceAsList.Insert(index, entity);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Removes entity as specified index\r
+ /// </summary>\r
+ /// <param name="index"></param>\r
+ public void RemoveAt(int index)\r
+ {\r
+ OnRemove(sourceAsList[index]);\r
+ sourceAsList.RemoveAt(index);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the <see cref="TEntity"/> at the specified index.\r
+ /// </summary>\r
+ /// <value></value>\r
+ public TEntity this[int index]\r
+ {\r
+ get\r
+ {\r
+ return Source.ElementAt(index);\r
+ }\r
+ set\r
+ {\r
+ OnRemove(sourceAsList[index]);\r
+ OnAdd(value);\r
+ sourceAsList[index] = value;\r
+ }\r
+ }\r
+\r
+ #endregion\r
+\r
+ #region ICollection<TEntity> Members\r
+\r
+ /// <summary>\r
+ /// Removes all items in collection\r
+ /// </summary>\r
+ public void Clear()\r
+ {\r
+ while (sourceAsList.Count > 0)\r
+ Remove(sourceAsList [0]);\r
+ Source = Enumerable.Empty<TEntity>();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Determines whether [contains] [the specified entity].\r
+ /// </summary>\r
+ /// <param name="entity">The entity.</param>\r
+ /// <returns>\r
+ /// <c>true</c> if [contains] [the specified entity]; otherwise, <c>false</c>.\r
+ /// </returns>\r
+ public bool Contains(TEntity entity)\r
+ {\r
+ return Source.Contains(entity);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Copies items to target array\r
+ /// </summary>\r
+ /// <param name="array"></param>\r
+ /// <param name="arrayIndex"></param>\r
+ public void CopyTo(TEntity[] array, int arrayIndex)\r
+ {\r
+ array = this.Source.Skip(arrayIndex).ToArray();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Returns entities count\r
+ /// </summary>\r
+ public int Count\r
+ {\r
+ get\r
+ {\r
+ return sourceAsList.Count;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.\r
+ /// </summary>\r
+ /// <value></value>\r
+ /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.\r
+ /// </returns>\r
+ bool ICollection<TEntity>.IsReadOnly\r
+ {\r
+ get { throw new NotImplementedException(); }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Removes the specified entity.\r
+ /// </summary>\r
+ /// <param name="entity">The entity.</param>\r
+ /// <returns></returns>\r
+ public bool Remove(TEntity entity)\r
+ {\r
+ OnRemove(entity);\r
+ return sourceAsList.Remove(entity);\r
+ }\r
+\r
+ #endregion\r
+\r
+ #region IList Members\r
+\r
+ int IList.Add(object value)\r
+ {\r
+ if (value is TEntity)\r
+ {\r
+ var v = value as TEntity;\r
+ if (this.IndexOf(v) >= 0)\r
+ throw new ArgumentOutOfRangeException();\r
+ this.Add(v);\r
+ return this.Count - 1;\r
+ }\r
+ else\r
+ throw new NotSupportedException();\r
+ }\r
+\r
+ void IList.Clear()\r
+ {\r
+ this.Clear();\r
+ }\r
+\r
+ bool IList.Contains(object value)\r
+ {\r
+ if (value is TEntity)\r
+ return this.Contains(value as TEntity);\r
+ else\r
+ return false;\r
+ }\r
+\r
+ int IList.IndexOf(object value)\r
+ {\r
+ return this.IndexOf(value as TEntity);\r
+ }\r
+\r
+ void IList.Insert(int index, object value)\r
+ {\r
+ this.Insert(index, value as TEntity);\r
+ }\r
+\r
+ bool IList.IsFixedSize\r
+ {\r
+ get { return false; }\r
+ }\r
+\r
+ bool IList.IsReadOnly\r
+ {\r
+ get { return false; }\r
+ }\r
+\r
+ void IList.Remove(object value)\r
+ {\r
+ this.Remove(value as TEntity);\r
+ }\r
+\r
+ void IList.RemoveAt(int index)\r
+ {\r
+ this.RemoveAt(index);\r
+ }\r
+\r
+ object IList.this[int index]\r
+ {\r
+ get\r
+ {\r
+ return this[index];\r
+ }\r
+ set\r
+ {\r
+ this[index] = value as TEntity;\r
+ }\r
+ }\r
+\r
+ #endregion\r
+\r
+ #region ICollection Members\r
+\r
+ void ICollection.CopyTo(Array array, int index)\r
+ {\r
+ this.CopyTo(array as TEntity[], index);\r
+ }\r
+\r
+ int ICollection.Count\r
+ {\r
+ get { return this.Count; }\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ bool ICollection.IsSynchronized\r
+ {\r
+ get { throw new NotImplementedException(); }\r
+ }\r
+\r
+ [DbLinqToDo]\r
+ object ICollection.SyncRoot\r
+ {\r
+ get { throw new NotImplementedException(); }\r
+ }\r
+\r
+ #endregion\r
+\r
+ /// <summary>\r
+ /// Gets a value indicating whether this instance is deferred.\r
+ /// </summary>\r
+ /// <value>\r
+ /// <c>true</c> if this instance is deferred; otherwise, <c>false</c>.\r
+ /// </value>\r
+ public bool IsDeferred\r
+ {\r
+ get { return Source is IQueryable; }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Adds the range.\r
+ /// </summary>\r
+ /// <param name="collection">The collection.</param>\r
+ public void AddRange(IEnumerable<TEntity> collection)\r
+ {\r
+ if (onAdd != null)\r
+ {\r
+ foreach (var entity in collection)\r
+ {\r
+ Add(entity);\r
+ }\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Assigns the specified entity source.\r
+ /// </summary>\r
+ /// <param name="entitySource">The entity source.</param>\r
+ public void Assign(IEnumerable<TEntity> entitySource)\r
+ {\r
+ // notifies removals and adds\r
+ Clear();\r
+ foreach (var entity in entitySource)\r
+ OnAdd(entity);\r
+ this.Source = entitySource;\r
+ this.SourceInUse = sourceAsList;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Sets the entity source.\r
+ /// </summary>\r
+ /// <param name="entitySource">The entity source.</param>\r
+ public void SetSource(IEnumerable<TEntity> entitySource)\r
+ {\r
+ if(HasLoadedOrAssignedValues)\r
+ throw new InvalidOperationException("The EntitySet is already loaded and the source cannot be changed.");\r
+ this.Source = entitySource;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Loads all entities.\r
+ /// </summary>\r
+ public void Load()\r
+ {\r
+ this.sourceAsList.Count();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Gets or sets a value indicating whether this instance has loaded or assigned values.\r
+ /// </summary>\r
+ /// <value>\r
+ /// <c>true</c> if this instance has loaded or assigned values; otherwise, <c>false</c>.\r
+ /// </value>\r
+ public bool HasLoadedOrAssignedValues\r
+ {\r
+ get { return SourceInUse != null; }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Gets a new binding list.\r
+ /// </summary>\r
+ /// <returns></returns>\r
+ public IBindingList GetNewBindingList()\r
+ {\r
+ return new BindingList<TEntity>(Source.ToList());\r
+ }\r
+\r
+ // TODO: implement handler call\r
+ public event ListChangedEventHandler ListChanged;\r
+\r
+ /// <summary>\r
+ /// Called when entity is added.\r
+ /// </summary>\r
+ /// <param name="entity">The entity.</param>\r
+ private void OnAdd(TEntity entity)\r
+ {\r
+ if (onAdd != null)\r
+ onAdd(entity);\r
+ }\r
+ /// <summary>\r
+ /// Called when entity is removed\r
+ /// </summary>\r
+ /// <param name="entity">The entity.</param>\r
+ private void OnRemove(TEntity entity)\r
+ {\r
+ if (onRemove != null)\r
+ onRemove(entity);\r
+ }\r
+ }\r
+}\r