Example #1
0
 protected virtual void UpdateForeignKeyFields(object child)
 {
     if (listType != GentleListType.StandAlone && mappings != null)
     {
         foreach (FieldMap cfm in mappings.Keys)
         {
             FieldMap pfm = mappings[cfm] as FieldMap;
             cfm.SetValue(child, pfm.GetValue(parent));
         }
     }
 }
Example #2
0
        /// <summary>
        /// Obtain a key for the specified object instance and property names. The returned
        /// key will contain the corresponding column names for the type, and thus foreign
        /// key columns must use identical naming for this to work.
        /// </summary>
        /// <param name="broker">The optional PersistenceBroker instance to use for obtaining the
        /// ObjectMap for the supplied object instance. If this parameter is null, Gentle will try
        /// to infer the broker from the object instance. If that fails, it will use the default
        /// provider.</param>
        /// <param name="key">An optional existing key to add the values to</param>
        /// <param name="isPropertyKeys">False is key indexers are column names, true for property names</param>
        /// <param name="instance">The object instance whose property values will be used</param>
        /// <param name="members">The names of the properties to include in the key</param>
        /// <returns>The key</returns>
        public static Key GetKey(PersistenceBroker broker, Key key, bool isPropertyKeys, object instance, params string[] members)
        {
            Check.VerifyNotNull(instance, Error.NullParameter, "instance");
            // try to infer broker from instance
            if (broker == null && instance is IBrokerLock)
            {
                broker = (instance as IBrokerLock).SessionBroker;
            }
            // WARNING/TODO if broker is null here and no ObjectMap yet exists for the type,
            // the DefaultProvider will be used to create the ObjectMap
            ObjectMap map = ObjectFactory.GetMap(broker, instance);

            // only set source type reference if this is a new key
            if (key == null)
            {
                key = new Key(map.GetTableName(instance), instance.GetType(), isPropertyKeys);
            }
            //else
            //	Check.Verify( ! key.isPropertyKeys, Error.DeveloperError,
            //		"Unable to combine keys containing property names due to possible name clashes." );
            Check.VerifyEquals(key.isPropertyKeys, isPropertyKeys, "Cannot combine property and " +
                               "column names in a single key - use one or the other.");
            Check.VerifyNotNull(members, Error.NullParameter, "members");
            // process the list of specified properties
            foreach (string memberName in members)
            {
                FieldMap fm = isPropertyKeys ? map.GetFieldMap(memberName) : map.GetFieldMapFromColumn(memberName);
                Check.VerifyNotNull(fm, Error.NoProperty, map.Type, memberName);                   // FIXME outdated error message
                object memberValue = fm.GetValue(instance);
                // translate foreign references to local names
                if (key.SourceType != map.Type)
                {
                    // WARNING/TODO if broker is null here and no ObjectMap yet exists for the type,
                    // the DefaultProvider will be used to create the ObjectMap
                    ObjectMap keyMap = ObjectFactory.GetMap(broker, key.SourceType);
                    fm = keyMap.GetForeignKeyFieldMap(map.TableName, fm.ColumnName);
                }
                key[isPropertyKeys ? fm.MemberName : fm.ColumnName] = memberValue;
            }
            // add concurrency value if enabled and instance has revision column
            if (GentleSettings.ConcurrencyControl && map.ConcurrencyMap != null)
            {
                long version = Convert.ToInt64(map.ConcurrencyMap.GetValue(instance));
                key[isPropertyKeys ? map.ConcurrencyMap.MemberName : map.ConcurrencyMap.ColumnName] = version;
            }
            return(key);
        }
Example #3
0
        /// <summary>
        /// Internal helper method used for standard CRUD operations on known types.
        /// </summary>
        /// <param name="obj">The object instance being operated on</param>
        /// <param name="st">The statement type</param>
        /// <param name="conn">An existing database connection to reuse. This is useful
        /// when you need to execute statements in the same session as a previous statement.</param>
        /// <param name="tr">The database transaction for when participating in transactions.</param>
        /// <returns>The result of the operation</returns>
        internal SqlResult Execute(object obj, StatementType st, IDbConnection conn, IDbTransaction tr)
        {
            ObjectMap map = ObjectFactory.GetMap(this, obj);

            // perform validations first
            if (StatementType.Insert == st || StatementType.Update == st)
            {
                ValidationBroker.Validate(obj);
            }

            if (map.IsSoftDelete && st == StatementType.Delete)
            {
                st = StatementType.SoftDelete;
            }
            SqlStatement stmt = GetStatement(obj, st);

            stmt.SetParameters(obj, true);
            // connections are supplied from outside when in a transaction or executing batch queries
            conn = tr != null ? tr.Connection : conn ?? stmt.SessionBroker.Provider.GetConnection();
            SqlResult sr = stmt.Execute(conn, tr);

            // throw an error if execution failed
            Check.Verify(sr.ErrorCode == 0, Error.StatementError, sr.Error, stmt.Sql);
            // check that statement affected a row
            if (st == StatementType.Insert || st == StatementType.Update ||
                st == StatementType.Delete || st == StatementType.SoftDelete)
            {
                Check.Verify(sr.RowsAffected >= 1, Error.UnexpectedRowCount, sr.RowsAffected, "1+");
            }
            // update identity values for inserts
            if (st == StatementType.Insert)
            {
                if (sr.LastRowId != 0 && map.IdentityMap != null)
                {
                    map.SetIdentity(obj, sr.LastRowId);
                }
                if (obj is IEntity)
                {
                    (obj as IEntity).IsPersisted = true;
                }
                // this is not really necessary but nice for error checking (also used in test cases)
                if (map.InheritanceMap != null)
                {
                    map.InheritanceMap.SetValue(obj, obj.GetType().AssemblyQualifiedName);
                }
            }
            // update/invalidate the cache
            if (GentleSettings.CacheObjects && map.CacheStrategy != CacheStrategy.Never)
            {
                if (st == StatementType.Insert)
                {
                    // insert new object into cache
                    CacheManager.Insert(map.GetInstanceHashKey(obj), obj, map.CacheStrategy);
                    // invalidate query results for select statements for this type, reducing the
                    // effectiveness of the cache (but ensuring correct results)
                    if (GentleSettings.CacheStatements)
                    {
                        CacheManager.ClearQueryResultsByType(map.Type);
                    }
                }
                else if (st == StatementType.Delete || st == StatementType.SoftDelete)
                {
                    // remove invalidated object from the cache
                    CacheManager.Remove(obj);
                    // invalidate query results for select statements for this type, reducing the
                    // effectiveness of the cache (but ensuring correct results)
                    if (GentleSettings.CacheStatements)
                    {
                        CacheManager.ClearQueryResultsByType(map.Type);
                    }
                }
            }
            // update the in-memory version/revision counter for objects under concurrency control
            if (st == StatementType.Update && GentleSettings.ConcurrencyControl)
            {
                if (map.ConcurrencyMap != null)
                {
                    FieldMap fm      = map.ConcurrencyMap;
                    long     version = Convert.ToInt64(fm.GetValue(obj));
                    // handle wrap-around of the version counter
                    if ((fm.Type.Equals(typeof(int)) && version == int.MaxValue) ||
                        (fm.Type.Equals(typeof(long)) && version == long.MaxValue))
                    {
                        version = 1;
                    }
                    else
                    {
                        version += 1;
                    }
                    map.ConcurrencyMap.SetValue(obj, version);
                }
            }
            // update object with database-created values if UpdateAfterWrite is set to true
            if (map.IsUpdateAfterWrite && (st == StatementType.Insert || st == StatementType.Update))
            {
                if (tr != null)
                {
                    Refresh(obj, tr);
                }
                else
                {
                    Refresh(obj);
                }
            }
            return(sr);
        }
Example #4
0
 protected virtual void InitList(Type viaType, params Type[] relationTypes)
 {
     if (listType == GentleListType.StandAlone)
     {
         broker.RetrieveList(containedMap.Type, this);
     }
     else if (listType == GentleListType.OneToMany)
     {
         // no relation objects for 1:n relationships
         viaInstances = null;
         mappings     = containedMap.GetForeignKeyMappings(parentMap, true);
         if (mappings.Count == 0)
         {
             mappings = viaMap.GetForeignKeyMappings(parentMap, false);
         }
         Check.Verify(mappings.Count > 0, Error.DeveloperError,
                      "The type {0} does not contain a foreign key reference to type {1}.",
                      parentMap.Type, containedMap.Type);
         Check.Verify(mappings.Count == 1, Error.NotImplemented,
                      "GentleList for 1:n relations can not be used with composite keys.");
         // populate self with any existing entries matching the current parent
         Key key = new Key(parentMap.Type, true);
         IDictionaryEnumerator iterator = mappings.GetEnumerator();
         while (iterator.MoveNext())
         {
             // construct a key to read the data; first obtain the referenced value from
             // the parent object (this is the constraint value used in the select)
             FieldMap fm = iterator.Value as FieldMap;
             object   referencedValue = fm.GetValue(parent);
             // if class references self make sure to pick the outgoing column
             if (containedMap.Type == parentMap.Type && !fm.IsForeignKey)
             {
                 fm = iterator.Key as FieldMap;
             }
             key[fm.MemberName] = referencedValue;
         }
         broker.RetrieveList(containedMap.Type, key, this);
     }
     else if (listType == GentleListType.ManyToMany)
     {
         // create relation for n:m management
         Type[] relatedTypes = Merge(containedMap.Type, relationTypes);
         viaInstances = new GentleRelation(broker, viaType, parent, relatedTypes);
         // populate the list with any existing entries matching the current relation entries
         ObjectMap  viaMap = ObjectFactory.GetMap(broker, viaType);
         SqlBuilder sb     = new SqlBuilder(broker, StatementType.Select, containedMap.Type);
         // assume the relation object is the child, i.e. refers to the contained type
         mappings = viaMap.GetForeignKeyMappings(containedMap, true);
         if (mappings.Count == 0)
         {
             mappings = viaMap.GetForeignKeyMappings(containedMap, false);
         }
         Check.Verify(mappings.Count > 0, Error.DeveloperError,
                      "The type {0} does not contain a foreign key reference to type {1}.",
                      viaMap.Type, containedMap.Type);
         Check.Verify(mappings.Count == 1, Error.NotImplemented,
                      "GentleList for n:m relations can not be used with composite keys.");
         // verify that references point to unique instance
         //Check.Verify( mappings.Count == parentMap.PrimaryKeyCount, Error.DeveloperError,
         //	"The number of fields ({0}) referencing {1} from {2} must equal the primary key count ({3}).",
         //	mappings.Count, parentMap.Type, containedMap.Type, parentMap.PrimaryKeyCount );
         if (viaInstances.Count > 0)
         {
             foreach (FieldMap remote in mappings.Keys)
             {
                 FieldMap local = (FieldMap)mappings[remote];
                 // viaMap.GetForeignKeyFieldMap( containedMap.Type, local.PropertyName );
                 sb.AddConstraint(Operator.In, local.MemberName, viaInstances, remote.MemberName);
             }
             ObjectFactory.GetCollection(containedMap.Type, sb.GetStatement(true).Execute(), this);
         }
     }
 }
Example #5
0
		/// <summary>
		/// This method is for adding set membership constraints using either Operator.In or
		/// Operator.NotIn. If an invalid operator is used this method will throw an exception.
		/// </summary>
		/// <param name="op">The operator used for this constraint.</param>
		/// <param name="fieldMap">The FieldMap instance for the field to constrain. The framework 
		/// assumes property names but
		/// also checks column names if no property could be found.</param>
		/// <param name="data">The set of data to constrain on.</param>
		/// <param name="constraintMap">If the constraint data set holds objects this is the property
		/// name to gather from all objects. If the data set is an SqlResult this is the column name
		/// to use from all rows. String-type fields are quoted, all other types are not. This method
		/// performs no additional type checking and is likely to fail (when the statement is executed)
		/// for esoteric types such as decimals or dates.</param>
		public void AddConstraint( Operator op, FieldMap fieldMap, ICollection data, FieldMap constraintMap )
		{
			Check.VerifyNotNull( fieldMap, Error.NullParameter, "field" );
			Check.VerifyNotNull( data, Error.NullParameter, "data" );
			Check.IsTrue( op == Operator.In || op == Operator.NotIn, Error.DeveloperError,
			              "This AddConstraint method must be used only with the In and NotIn operators." );
			// TODO make this a config option (e.g. "strict" behavior)
			// Check.Verify( data.Count > 0, Error.EmptyListParameter, "data" );
			if( data.Count > 0 )
			{
				// convert supplied collection into comma-delimited list of values 
				string[] list = new string[data.Count];
				int count = 0;
				foreach( object obj in data )
				{
					if( constraintMap == null )
					{
						list[ count++ ] = Convert.ToString( obj );
					}
					else
					{
						list[ count++ ] = Convert.ToString( constraintMap.GetValue( obj ) );
					}
				}
				// determine whether field type needs quoting
				bool needsQuoting = fieldMap.Type.Equals( typeof(string) );
				needsQuoting |= fieldMap.Type.Equals( typeof(DateTime) );
				// quote GUIDs (only when they are not in binary form)
				needsQuoting |= fieldMap.Type.Equals( typeof(Guid) ) && (fieldMap.Size == 0 || fieldMap.Size > 16);
				// add the constraint
				AddConstraint( String.Format( "{0} {1} {2} {3}", fieldMap.QuotedColumnName,
				                              AsOperatorBegin( op, false ),
				                              AsCommaList( list, needsQuoting ),
				                              AsOperatorEnd( op ) ) );
			}
		}