2008-12-02 Atsushi Enomoto <atsushi@ximian.com>
authorAtsushi Eno <atsushieno@gmail.com>
Tue, 2 Dec 2008 02:53:50 +0000 (02:53 -0000)
committerAtsushi Eno <atsushieno@gmail.com>
Tue, 2 Dec 2008 02:53:50 +0000 (02:53 -0000)
* updated to DbLinq r966.

svn path=/branches/atsushi-200810/mcs/; revision=120451

mcs/class/System.Data.Linq/ChangeLog
mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs
mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataLoadOptions.cs
mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/EntityRef.cs
mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/EntitySet.cs

index 54632f79eba34cedaf40bec35f6d25369f6fde7f..2a83b45752247a7e62524624c9cc16dbe23da08d 100755 (executable)
@@ -1,3 +1,7 @@
+2008-12-02  Atsushi Enomoto  <atsushi@ximian.com>
+
+       * updated to DbLinq r966.
+
 2008-11-25  Atsushi Enomoto  <atsushi@ximian.com>
 
        * updated to DbLinq r960.
index 568dc358bde775e9415daa62f5c77fdd1042a624..de96cc50e3202f0efa1d34d226b0f761409e8e71 100644 (file)
-#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
index a4befd36ac83f1aa7117b201fe253e2d801fed4d..ae13027db72cef2a70a4b2f0598dbf939d394918 100644 (file)
@@ -100,8 +100,37 @@ using DbLinq.Util;
         {\r
             // TODO: ensure we have an EntitySet<>\r
             var memberInfo = ReflectionUtility.GetMemberInfo(expression);\r
+            if (memberInfo == null)\r
+                throw new InvalidOperationException("The argument expression must be a property access or a field access where the target object is the parameter");\r
             if (!eagerLoading.Contains(memberInfo))\r
+            {\r
+                VerifyMemberAccessCycles(memberInfo);\r
                 eagerLoading.Add(memberInfo);\r
+            }\r
+        }\r
+\r
+        private void VerifyMemberAccessCycles(MemberInfo member)\r
+        {\r
+            var mt = GetMemberEntityType (member);\r
+            var d = member.DeclaringType;\r
+            foreach (var m in eagerLoading)\r
+            {\r
+                if (m.DeclaringType == mt && GetMemberEntityType (m) == d)\r
+                    throw new InvalidOperationException("Illegal cycles are detected in the argument expression among other eager-loading expressions");\r
+            }\r
+        }\r
+\r
+        private Type GetMemberEntityType(MemberInfo member)\r
+        {\r
+            var mt = member.GetMemberType();\r
+            if (mt.IsGenericType)\r
+            {\r
+                if (mt.GetGenericTypeDefinition() == typeof(System.Data.Linq.EntitySet<>))\r
+                    mt = mt.GetGenericArguments()[0];\r
+                else if (mt.GetGenericTypeDefinition() == typeof(System.Data.Linq.EntityRef<>))\r
+                    mt = mt.GetGenericArguments()[0];\r
+            }\r
+            return mt;\r
         }\r
 \r
         /// <summary>\r
index ff90a6b1f7d66b6cc4ba7204966d41cff80d37c5..a380e60f3c223affbb6bb2cc916e8d74ea2e13e7 100644 (file)
@@ -54,7 +54,6 @@ namespace DbLinq.Data.Linq
             hasLoadedOrAssignedValue = true;\r
         }\r
 \r
-        [DbLinqToDo]\r
         public EntityRef(IEnumerable<TEntity> source)\r
         {\r
             this.source = source;\r
@@ -64,9 +63,14 @@ namespace DbLinq.Data.Linq
 \r
         public EntityRef(EntityRef<TEntity> entityRef)\r
         {\r
-            this.source = null;\r
-            this.entity = entityRef.Entity;\r
-            hasLoadedOrAssignedValue = true;\r
+            this.entity = entityRef.entity;\r
+            if (entityRef.entity == null && entityRef.source is ICloneable)\r
+            {\r
+                source = (IEnumerable<TEntity>)((ICloneable)entityRef.source).Clone();\r
+            }\r
+            else\r
+                source = null;\r
+            hasLoadedOrAssignedValue = entityRef.hasLoadedOrAssignedValue;\r
         }\r
 \r
         public TEntity Entity\r
index ab11b49b873ac8b3d87ee7376643d7210256a2e1..18d36d3e8f7c898fe97d2743e6cdae5ebda25c42 100644 (file)
-#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&lt;TEntity&gt;"/> 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&lt;TEntity&gt;"/> 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&lt;TEntity&gt;"/> 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&lt;TEntity&gt;"/> 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