private bool HasAssociationChanged(MetaAssociation assoc, TrackedObject item) { if (item.Original != null && item.Current != null) { if (assoc.ThisMember.StorageAccessor.HasAssignedValue(item.Current) || assoc.ThisMember.StorageAccessor.HasLoadedValue(item.Current) ) { return(this.GetOtherItem(assoc, item.Current) != this.GetOtherItem(assoc, item.Original)); } else { object[] currentFKs = CommonDataServices.GetForeignKeyValues(assoc, item.Current); object[] originaFKs = CommonDataServices.GetForeignKeyValues(assoc, item.Original); for (int i = 0, n = currentFKs.Length; i < n; i++) { if (!object.Equals(currentFKs[i], originaFKs[i])) { return(true); } } } } return(false); }
internal RelationComposer(ParameterExpression parameter, MetaAssociation association, Expression otherSouce, Expression parameterReplacement) { this.parameter = parameter ?? throw Error.ArgumentNull(nameof(parameter)); this.association = association ?? throw Error.ArgumentNull(nameof(association)); this.otherSouce = otherSouce ?? throw Error.ArgumentNull(nameof(otherSouce)); this.parameterReplacement = parameterReplacement ?? throw Error.ArgumentNull(nameof(parameterReplacement)); }
/// <summary> /// Returns a unique association name for the specified MetaAssociation /// </summary> /// <param name="metaAssociation">A <see cref="MetaAssociation"/>.</param> /// <returns>A <see cref="String"/> containing the association name.</returns> private string GetAssociationName(MetaAssociation metaAssociation) { lock (this._associationNameMap) { // We need a unique key for this association, so we use the MetaAssociation // itself. In the case of bi-directional associations, we use the FK side. if (!metaAssociation.IsForeignKey && metaAssociation.OtherMember != null) { metaAssociation = metaAssociation.OtherMember.Association; } string associationName = null; if (!this._associationNameMap.TryGetValue(metaAssociation, out associationName)) { // names are always formatted non-FK side type name followed by FK side type name // For example, the name for both ends of the PurchaseOrder/PurchaseOrderDetail // association will be PurchaseOrder_PurchaseOrderDetail if (metaAssociation.IsForeignKey) { associationName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", metaAssociation.OtherType.Name, metaAssociation.ThisMember.DeclaringType.Name); } else { associationName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", metaAssociation.ThisMember.DeclaringType.Name, metaAssociation.OtherType.Name); } associationName = TypeDescriptionContextBase.MakeUniqueName(associationName, this._associationNameMap.Values); this._associationNameMap[metaAssociation] = associationName; } return(associationName); } }
private static ResolvedJoinInfo CreateResolvedJoinInfo( SqlEntityExpression originatingEntity, MetaAssociation metaAssociation, IResolvedTableInfo joinedTableInfo) { var leftColumn = ResolveMember(originatingEntity, metaAssociation.ThisKey); // If needed, implement by using compounds (NewExpressions with named arguments, see NamedExpression.CreateNewExpressionWithNamedArguments.) if (metaAssociation.OtherKey.Count > 1) { throw new NotSupportedException( string.Format( "Associations with more than one column are currently not supported. ({0}.{1})", originatingEntity.Type, metaAssociation.OtherMember.Name)); } var otherKey = metaAssociation.OtherKey[0]; var rightColumn = new SqlColumnDefinitionExpression( otherKey.Type, joinedTableInfo.TableAlias, otherKey.MappedName, otherKey.IsPrimaryKey); var joinCondition = ConversionUtility.MakeBinaryWithOperandConversion(ExpressionType.Equal, leftColumn, rightColumn, false, null); return(new ResolvedJoinInfo(joinedTableInfo, joinCondition)); }
private IEnumerable <string> GetDropForeignKeyCommands(MetaTable metaTable) { var metaType = metaTable.RowType; foreach (var member in metaType.DataMembers) { if (member.IsDeclaredBy(metaType) && member.IsAssociation) { MetaAssociation association = member.Association; var stringBuilder = new StringBuilder(); var mappedName = member.MappedName; if (mappedName == member.Name) { mappedName = string.Format(CultureInfo.InvariantCulture, "FK_{0}_{1}", SqlIdentifier.UnquoteIdentifier(metaType.Table.TableName), SqlIdentifier.UnquoteIdentifier(member.Name)); } var command = string.Format("SELECT count(*) FROM RDB$RELATION_CONSTRAINTS WHERE UPPER(RDB$CONSTRAINT_NAME) = '{0}'", mappedName.ToUpper()); var result = sqlProvider.services.Context.ExecuteQuery <int>(command).Single(); if (result == 0) { continue; } command = "ALTER TABLE {0}" + Environment.NewLine + " DROP CONSTRAINT {1}"; var tableName = association.IsForeignKey ? metaType.Table.TableName : association.OtherType.Table.TableName; yield return(stringBuilder.AppendFormat(command, new object[] { tableName, mappedName, }).ToString()); } } }
// Ensure the the member and foreign keys are nulled so that after trackedInstance is deleted, // the object does not appear to be associated with the other side anymore. This prevents the deleted object // from referencing objects still in the cache, but also will prevent the related object from being implicitly loaded private static void ClearForeignKeysHelper(MetaAssociation assoc, object trackedInstance) { Debug.Assert(assoc.IsForeignKey, "Foreign key clearing should only happen on foreign key side of the association."); Debug.Assert(assoc.ThisMember.IsAssociation, "Expected ThisMember of an association to always be an association."); // If this member is one of our deferred loaders, and it does not already have a value, explicitly set the deferred source to // null so that when we set the association member itself to null later, it doesn't trigger an implicit load. // This is only necessary if the value has not already been assigned or set, because otherwise we won't implicitly load anyway when the member is accessed. MetaDataMember thisMember = assoc.ThisMember; if (thisMember.IsDeferred && !(thisMember.StorageAccessor.HasAssignedValue(trackedInstance) || thisMember.StorageAccessor.HasLoadedValue(trackedInstance))) { // If this is a deferred member, set the value directly in the deferred accessor instead of going // through the normal member accessor, so that we don't trigger an implicit load. thisMember.DeferredSourceAccessor.SetBoxedValue(ref trackedInstance, null); } // Notify the object that the relationship should be considered deleted. // This allows the object to do its own fixup even when we can't do it automatically. thisMember.MemberAccessor.SetBoxedValue(ref trackedInstance, null); // Also set the foreign key values to null if possible for (int i = 0, n = assoc.ThisKey.Count; i < n; i++) { MetaDataMember thisKey = assoc.ThisKey[i]; if (thisKey.CanBeNull) { thisKey.StorageAccessor.SetBoxedValue(ref trackedInstance, null); } } }
private void SetAssociationKeyInfo(MetaAssociation association) { DLinqColumnProvider column = (DLinqColumnProvider)FromColumn; List <string> foreignKeyNames = new List <string>(); int count = column.Member.Association.ThisKey.Count; for (int i = 0; i < count; i++) { MetaDataMember thisKeyMetaDataMember = column.Member.Association.ThisKey[i]; MetaDataMember otherKeyMetaDataMember = column.Member.Association.OtherKey[i]; DLinqColumnProvider thisEntityMemberComponent = FindColumn(column.Table, thisKeyMetaDataMember.Name); if (ShouldRemoveThisAssociation(association)) { column.ShouldRemove = true; return; } foreignKeyNames.Add(thisEntityMemberComponent.Name); if (thisEntityMemberComponent.IsPrimaryKey) { IsPrimaryKeyInThisTable = true; } if (association.IsForeignKey) { thisEntityMemberComponent.IsForeignKeyComponent = true; } } ForeignKeyNames = new ReadOnlyCollection <string>(foreignKeyNames); }
public Association(MetaAssociation metaAssociation, Guid associationId, Guid itemSourceId, Guid itemTargetId, string associationName) : base(metaAssociation, associationId) { this.itemSourceId = itemSourceId; this.itemTargetId = itemTargetId; this.associationName = associationName; }
internal void Add(MetaAssociation assoc, TrackedObject from, TrackedObject to) { if (!associations.TryGetValue(assoc, out var value)) { value = new Dictionary <TrackedObject, TrackedObject>(); associations.Add(assoc, value); } value.Add(from, to); }
internal TrackedObject this[MetaAssociation assoc, TrackedObject from] { get { if (associations.TryGetValue(assoc, out var value) && value.TryGetValue(from, out var value2)) { return(value2); } return(null); } }
internal static object[] GetForeignKeyValues(MetaAssociation association, object instance) { var list = new List <object>(); foreach (MetaDataMember member in association.ThisKey) { list.Add(member.MemberAccessor.GetBoxedValue(instance)); } return(list.ToArray()); }
internal static object[] GetForeignKeyValues(MetaAssociation association, object instance) { List <object> keyValues = new List <object>(); foreach (MetaDataMember mm in association.ThisKey) { keyValues.Add(mm.MemberAccessor.GetBoxedValue(instance)); } return(keyValues.ToArray()); }
internal void Add(MetaAssociation assoc, TrackedObject from, TrackedObject to) { Dictionary <TrackedObject, TrackedObject> dictionary; if (!this.associations.TryGetValue(assoc, out dictionary)) { dictionary = new Dictionary <TrackedObject, TrackedObject>(); this.associations.Add(assoc, dictionary); } dictionary.Add(from, to); }
internal void Add(MetaAssociation assoc, TrackedObject from, TrackedObject to) { Dictionary <TrackedObject, TrackedObject> pairs; if (!associations.TryGetValue(assoc, out pairs)) { pairs = new Dictionary <TrackedObject, TrackedObject>(); associations.Add(assoc, pairs); } pairs.Add(from, to); }
// Properties internal TrackedObject this[MetaAssociation assoc, TrackedObject from] { get { Dictionary <TrackedObject, TrackedObject> dictionary; TrackedObject obj2; if (this.associations.TryGetValue(assoc, out dictionary) && dictionary.TryGetValue(from, out obj2)) { return(obj2); } return(null); } }
/// <summary> /// Returns an AssociationAttribute for the specified association member /// </summary> /// <param name="member">The metadata member corresponding to the association member</param> /// <returns>The Association attribute</returns> public System.ComponentModel.DataAnnotations.AssociationAttribute CreateAssociationAttribute(MetaDataMember member) { MetaAssociation metaAssociation = member.Association; string associationName = this.GetAssociationName(metaAssociation); string thisKey = TypeDescriptionContextBase.FormatMemberList(metaAssociation.ThisKey.Select(p => p.Name)); string otherKey = TypeDescriptionContextBase.FormatMemberList(metaAssociation.OtherKey.Select(p => p.Name)); System.ComponentModel.DataAnnotations.AssociationAttribute assocAttrib = new System.ComponentModel.DataAnnotations.AssociationAttribute(associationName, thisKey, otherKey); assocAttrib.IsForeignKey = metaAssociation.IsForeignKey; return(assocAttrib); }
public DLinqAssociationProvider(DLinqColumnProvider column) { FromColumn = column; MetaAssociation association = column.Member.Association; SetOtherEndOfAssociation(association); SetDirection(association); Debug.Assert(Direction != AssociationDirection.ManyToMany, "Many to Many is not supported by Linq to SQL"); SetAssociationKeyInfo(association); }
private IEnumerable <string> GetCreateForeignKeyCommands(MetaType metaType) { foreach (var member in metaType.DataMembers) { if (member.IsDeclaredBy(metaType) && member.IsAssociation) { MetaAssociation association = member.Association; if (association.IsForeignKey) { var stringBuilder = new StringBuilder(); var thisKey = BuildKey(association.ThisKey); var otherKey = BuildKey(association.OtherKey); var otherTable = association.OtherType.Table.TableName; var mappedName = member.MappedName; if (mappedName == member.Name) { mappedName = string.Format(CultureInfo.InvariantCulture, "FK_{0}_{1}", new object[] { QuoteCompoundIdentifier(metaType.Table.TableName), QuoteIdentifier(member.Name) }); } var command = "ALTER TABLE {0}" + Environment.NewLine + " ADD CONSTRAINT {1} FOREIGN KEY ({2}) REFERENCES {3}({4})"; string deleteRule = " NO ACTION"; var otherMember = association.OtherMember; if (otherMember != null) { if (association.DeleteRule != null) { deleteRule = association.DeleteRule; } //if (deleteRule != null) //{ // command += Environment.NewLine + " ON DELETE " + deleteRule; //} } stringBuilder.AppendFormat(command, new object[] { QuoteCompoundIdentifier(metaType.Table.TableName), QuoteIdentifier(mappedName), thisKey, QuoteCompoundIdentifier(otherTable), otherKey }); //ON DELETE NO ACTION stringBuilder.AppendLine(); stringBuilder.AppendLine("ON DELETE" + deleteRule); stringBuilder.AppendLine("ON UPDATE NO ACTION"); yield return(stringBuilder.ToString()); } } } }
internal TrackedObject this[MetaAssociation assoc, TrackedObject from] { get { Dictionary <TrackedObject, TrackedObject> pairs; if (associations.TryGetValue(assoc, out pairs)) { TrackedObject to; if (pairs.TryGetValue(from, out to)) { return(to); } } return(null); } }
private void SetOtherEndOfAssociation(MetaAssociation association) { DLinqTableProvider entityMemberParentEntity = (DLinqTableProvider)FromColumn.Table; DLinqDataModelProvider parentEntityDataContext = (DLinqDataModelProvider)entityMemberParentEntity.DataModel; if (association.OtherMember != null) { ToColumn = parentEntityDataContext.ColumnLookup[(PropertyInfo)association.OtherMember.Member]; } else { ToTable = ((DLinqDataModelProvider)FromColumn.Table.DataModel).DLinqTables.Single(tp => tp.EntityType == association.OtherType.Type); } }
internal Expression GetDataMemberQuery(MetaDataMember member, Expression[] keyValues) { if (member == null) { throw Error.ArgumentNull("member"); } if (keyValues == null) { throw Error.ArgumentNull("keyValues"); } if (member.IsAssociation) { MetaAssociation association = member.Association; Type rootType = association.ThisMember.DeclaringType.InheritanceRoot.Type; Expression thisSource = Expression.Constant(context.GetTable(rootType)); if (rootType != association.ThisMember.DeclaringType.Type) { thisSource = Expression.Call(typeof(Enumerable), "Cast", new Type[] { association.ThisMember.DeclaringType.Type }, thisSource); } Expression thisInstance = Expression.Call(typeof(Enumerable), "FirstOrDefault", new Type[] { association.ThisMember.DeclaringType.Type }, System.Data.Linq.SqlClient.Translator.WhereClauseFromSourceAndKeys(thisSource, association.ThisKey.ToArray(), keyValues) ); Expression otherSource = Expression.Constant(context.GetTable(association.OtherType.InheritanceRoot.Type)); if (association.OtherType.Type != association.OtherType.InheritanceRoot.Type) { otherSource = Expression.Call(typeof(Enumerable), "Cast", new Type[] { association.OtherType.Type }, otherSource); } Expression expr = System.Data.Linq.SqlClient.Translator.TranslateAssociation( this.context, association, otherSource, keyValues, thisInstance ); return(expr); } else { Expression query = this.GetObjectQuery(member.DeclaringType, keyValues); Type elementType = System.Data.Linq.SqlClient.TypeSystem.GetElementType(query.Type); ParameterExpression p = Expression.Parameter(elementType, "p"); Expression e = p; if (elementType != member.DeclaringType.Type) { e = Expression.Convert(e, member.DeclaringType.Type); } Expression mem = (member.Member is PropertyInfo) ? Expression.Property(e, (PropertyInfo)member.Member) : Expression.Field(e, (FieldInfo)member.Member); LambdaExpression selector = Expression.Lambda(mem, p); return(Expression.Call(typeof(Queryable), "Select", new Type[] { elementType, selector.Body.Type }, query, selector)); } }
private void SetDirection(MetaAssociation association) { if (association.IsMany) { Direction = AssociationDirection.OneToMany; } else if (association.OtherMember == null || association.OtherMember.Association.IsMany) { // there might not be the other member if this is a one-sided association Direction = AssociationDirection.ManyToOne; } else { Direction = AssociationDirection.OneToOne; } }
private static IEnumerable <String> GetCreateForeignKeyCommands(MetaType type) { string tableName = type.Table.TableName; foreach (MetaDataMember mm in type.DataMembers) { if (mm.IsDeclaredBy(type) && mm.IsAssociation) { MetaAssociation assoc = mm.Association; if (assoc.IsForeignKey) { StringBuilder sb = new StringBuilder(); string thisKey = BuildKey(assoc.ThisKey); string otherKey = BuildKey(assoc.OtherKey); string otherTable = assoc.OtherType.Table.TableName; string name; name = mm.MappedName; if (name == mm.Name) { name = String.Format(Globalization.CultureInfo.InvariantCulture, "FK_{0}_{1}", tableName, mm.Name); } string cmd = "ALTER TABLE {0}" + Environment.NewLine + " ADD CONSTRAINT {1} FOREIGN KEY ({2}) REFERENCES {3}({4})"; //In DLinq we put the constraint on the child object (which triggers the behavior when deleted), //but in SQL it is part of the parent constraint (the parent row gets changed / deleted to satisfy the constraint) MetaDataMember otherMember = mm.Association.OtherMember; if (otherMember != null) { string delConstr = otherMember.Association.DeleteRule; if (delConstr != null) { cmd += Environment.NewLine + " ON DELETE " + delConstr; } } sb.AppendFormat(cmd, SqlIdentifier.QuoteCompoundIdentifier(tableName), SqlIdentifier.QuoteIdentifier(name), SqlIdentifier.QuoteCompoundIdentifier(thisKey), SqlIdentifier.QuoteCompoundIdentifier(otherTable), SqlIdentifier.QuoteCompoundIdentifier(otherKey)); yield return(sb.ToString()); } } } }
/// <summary> /// Creates the explicit join method call. /// </summary> /// <param name="member"> /// The member. /// </param> /// <param name="objectMetaType"> /// Type of the object meta. /// </param> /// <param name="selector"> /// The selector. /// </param> /// <returns> /// Explicit join method call expression /// </returns> private Expression CreateExplicitJoinMethodCall(MemberExpression member, MetaType objectMetaType, MethodInfo selector) { MetaAssociation association = objectMetaType.Associations.Where(a => a.ThisMember.Member == member.Member).SingleOrDefault(); Expression visitedMemberExpression = this.Visit(member.Expression); var thisSideKeyExpression = Expression.Convert( Expression.Property(visitedMemberExpression, association.ThisKey.Single().Member as PropertyInfo), typeof(object)); var thisPropertyExpression = Expression.Constant(association.OtherKey.Single().Member, typeof(PropertyInfo)); var repositoryConst = Expression.Constant(this.IndexesCache); var explicitJoinMethodCallExpression = Expression.Call(repositoryConst, selector, thisPropertyExpression, thisSideKeyExpression); return(explicitJoinMethodCallExpression); }
public void Many_To_One() { MetaType metaType = db.Configuration.Model.GetMetaType(typeof(Model.EmployeeTerritory)); Assert.AreEqual(2, metaType.Associations.Count); MetaAssociation assoc = metaType.Associations[1]; Assert.IsFalse(assoc.IsMany); Assert.IsTrue(assoc.OtherKeyIsPrimaryKey); Assert.AreEqual(typeof(Model.EmployeeTerritory), assoc.ThisMember.DeclaringType.Type); Assert.AreEqual(nameof(Model.EmployeeTerritory.Employee), assoc.ThisMember.Name); Assert.AreEqual(typeof(Model.Employee), assoc.ThisMember.Type); Assert.AreEqual(typeof(Model.Employee), assoc.OtherMember.DeclaringType.Type); Assert.AreEqual(nameof(Model.Employee.EmployeeTerritories), assoc.OtherMember.Name); Assert.AreEqual(typeof(Collection <Model.EmployeeTerritory>), assoc.OtherMember.Type); }
private static void ClearForeignKeysHelper(MetaAssociation assoc, object trackedInstance) { var thisMember = assoc.ThisMember; if (thisMember.IsDeferred && !thisMember.StorageAccessor.HasAssignedValue(trackedInstance) && !thisMember.StorageAccessor.HasLoadedValue(trackedInstance)) { thisMember.DeferredSourceAccessor.SetBoxedValue(ref trackedInstance, null); } thisMember.MemberAccessor.SetBoxedValue(ref trackedInstance, null); var i = 0; for (var count = assoc.ThisKey.Count; i < count; i++) { var metaDataMember = assoc.ThisKey[i]; if (metaDataMember.CanBeNull) { metaDataMember.StorageAccessor.SetBoxedValue(ref trackedInstance, null); } } }
internal Expression GetDataMemberQuery(MetaDataMember member, Expression[] keyValues) { if (member == null) { throw Error.ArgumentNull("member"); } if (keyValues == null) { throw Error.ArgumentNull("keyValues"); } if (member.IsAssociation) { MetaAssociation association = member.Association; Type type = association.ThisMember.DeclaringType.InheritanceRoot.Type; Expression source = Expression.Constant(Context.GetTable(type)); if (type != association.ThisMember.DeclaringType.Type) { source = Expression.Call(typeof(Enumerable), "Cast", new[] { association.ThisMember.DeclaringType.Type }, new[] { source }); } Expression thisInstance = Expression.Call(typeof(Enumerable), "FirstOrDefault", new[] { association.ThisMember.DeclaringType.Type }, new[] { Translator.WhereClauseFromSourceAndKeys(source, association.ThisKey.ToArray(), keyValues) }); Expression otherSource = Expression.Constant(Context.GetTable(association.OtherType.InheritanceRoot.Type)); if (association.OtherType.Type != association.OtherType.InheritanceRoot.Type) { otherSource = Expression.Call(typeof(Enumerable), "Cast", new[] { association.OtherType.Type }, new[] { otherSource }); } return(Translator.TranslateAssociation(Context, association, otherSource, keyValues, thisInstance)); } Expression objectQuery = GetObjectQuery(member.DeclaringType, keyValues); Type elementType = TypeSystem.GetElementType(objectQuery.Type); ParameterExpression expression6 = Expression.Parameter(elementType, "p"); Expression expression7 = expression6; if (elementType != member.DeclaringType.Type) { expression7 = Expression.Convert(expression7, member.DeclaringType.Type); } Expression body = (member.Member is PropertyInfo) ? Expression.Property(expression7, (PropertyInfo)member.Member) : Expression.Field(expression7, (FieldInfo)member.Member); LambdaExpression expression9 = Expression.Lambda(body, new[] { expression6 }); return(Expression.Call(typeof(Queryable), "Select", new[] { elementType, expression9.Body.Type }, new[] { objectQuery, expression9 })); }
private bool HasAssociationChanged(MetaAssociation assoc, TrackedObject item) { if (item.Original != null && item.Current != null) { if (assoc.ThisMember.StorageAccessor.HasAssignedValue(item.Current) || assoc.ThisMember.StorageAccessor.HasLoadedValue(item.Current)) { return(GetOtherItem(assoc, item.Current) != GetOtherItem(assoc, item.Original)); } var foreignKeyValues = CommonDataServices.GetForeignKeyValues(assoc, item.Current); var foreignKeyValues2 = CommonDataServices.GetForeignKeyValues(assoc, item.Original); var i = 0; for (var num = foreignKeyValues.Length; i < num; i++) { if (!object.Equals(foreignKeyValues[i], foreignKeyValues2[i])) { return(true); } } } return(false); }
internal static Expression TranslateAssociation(DataContext context, MetaAssociation association, Expression otherSource, Expression[] keyValues, Expression thisInstance) { if (association == null) { throw Error.ArgumentNull("association"); } if (keyValues == null) { throw Error.ArgumentNull("keyValues"); } if (context.LoadOptions != null) { LambdaExpression associationSubquery = context.LoadOptions.GetAssociationSubquery(association.ThisMember.Member); if (associationSubquery != null) { var composer = new RelationComposer(associationSubquery.Parameters[0], association, otherSource, thisInstance); return(composer.Visit(associationSubquery.Body)); } } return(WhereClauseFromSourceAndKeys(otherSource, association.OtherKey.ToArray <MetaDataMember>(), keyValues)); }
private TrackedObject GetOtherItem(MetaAssociation assoc, object instance) { if (instance == null) { return(null); } object obj = null; if (assoc.ThisMember.StorageAccessor.HasAssignedValue(instance) || assoc.ThisMember.StorageAccessor.HasLoadedValue(instance)) { obj = assoc.ThisMember.MemberAccessor.GetBoxedValue(instance); } else if (assoc.OtherKeyIsPrimaryKey) { var foreignKeyValues = CommonDataServices.GetForeignKeyValues(assoc, instance); obj = services.GetCachedObject(assoc.OtherType, foreignKeyValues); } if (obj == null) { return(null); } return(tracker.GetTrackedObject(obj)); }
internal RelationComposer(ParameterExpression parameter, MetaAssociation association, Expression otherSouce, Expression parameterReplacement) { if (parameter == null) { throw Error.ArgumentNull("parameter"); } if (association == null) { throw Error.ArgumentNull("association"); } if (otherSouce == null) { throw Error.ArgumentNull("otherSouce"); } if (parameterReplacement == null) { throw Error.ArgumentNull("parameterReplacement"); } this.parameter = parameter; this.association = association; this.otherSouce = otherSouce; this.parameterReplacement = parameterReplacement; }