public void ValueConflictThrows() { var entity = new TestEntity { Property = "prop" }; using (var context = InMemoryContextBuilder.Build <TestDataContext>()) { context.Add(entity); context.SaveChanges(); } using (var context = InMemoryContextBuilder.Build <TestDataContext>()) { var update = new TestEntity { Id = entity.Id, Property = "Something new", Timestamp = RowVersion.New() }; context.Attach(update); context.Entry(update).Property("Property").IsModified = true; var exception = Assert.Throws <DbUpdateConcurrencyException>(() => context.SaveChanges()); Approvals.Verify(exception.Message); } }
private int IncrementNextRecordVersion <TModel>(IDbConnection connection, TKey identity) { var table = _integrator.GetTableInfo(typeof(TModel)); var versionTbl = GetVersionTableInfo(table, changesSchema); var param = new { id = identity }; string tableName = versionTbl.ToString(); int result = connection.QuerySingleOrDefault <int?>(SqlSelectNextVersion(tableName), param) ?? 0; if (result == 0) { RowVersion <TKey> initialVersion = new RowVersion <TKey>() { RecordId = identity, NextVersion = 1 }; PlainInsert(connection, initialVersion, tableName: tableName); result = 1; } connection.Execute(SqlUpdateNextVersion(tableName), param); return(result); }
/// <inheritdoc /> protected override string FieldToString(FieldId field, RowVersion version) { return(field switch { FieldId.ProvinceState when version == RowVersion.JHopkinsV1 || version == RowVersion.JHopkinsV2 => "Province/State", FieldId.ProvinceState => "Province_State", FieldId.CountryRegion when version == RowVersion.JHopkinsV1 || version == RowVersion.JHopkinsV2 => "Country/Region", FieldId.CountryRegion => "Country_Region", FieldId.LastUpdate when version == RowVersion.JHopkinsV1 || version == RowVersion.JHopkinsV2 => "Last Update", FieldId.LastUpdate => "Last_Update", FieldId.Latitude when version == RowVersion.JHopkinsV1 || version == RowVersion.JHopkinsV2 => field.ToString(), FieldId.Latitude => "Lat", FieldId.Longitude when version == RowVersion.JHopkinsV1 || version == RowVersion.JHopkinsV2 => field.ToString(), FieldId.Longitude => "Long_", FieldId.CombinedKey => "Combined_Key", FieldId.IncidenceRate when version == RowVersion.JHopkinsV4 => "Incidence_Rate", FieldId.IncidenceRate when version == RowVersion.JHopkinsV5 => "Incident_Rate", FieldId.CaseFatalityRatio when version == RowVersion.JHopkinsV4 => "Case-Fatality_Ratio", FieldId.CaseFatalityRatio when version == RowVersion.JHopkinsV5 => "Case_Fatality_Ratio", _ => field.ToString() });
/// <inheritdoc /> protected override string FieldToString(FieldId field, RowVersion version) { return(field switch { FieldId.Iso2 when version == RowVersion.StatsBase => "iso2", FieldId.Iso3 when version == RowVersion.StatsBase => "iso3", FieldId.Code3 when version == RowVersion.StatsBase => "code3", FieldId.CountryRegion when version == RowVersion.StatsBase => "Country_Region", FieldId.Iso2 when version == RowVersion.StatsEx => "Two_Letter_Country_Code", FieldId.Iso3 when version == RowVersion.StatsEx => "Three_Letter_Country_Code", FieldId.Code3 when version == RowVersion.StatsEx => "Country_Number", FieldId.CountryRegion when version == RowVersion.StatsEx => "Country_Name", FieldId.ProvinceState => "Province_State", FieldId.Latitude => "Lat", FieldId.Longitude => "Long_", FieldId.CombinedKey => "Combined_Key", FieldId.ContinentName => "Continent_Name", FieldId.ContinentCode => "Continent_Code", FieldId.English => "English_JHopkins", FieldId.Russian => "Russian_Yandex", _ => field.ToString() });
/// <summary> /// Escreve os dados para XML. /// </summary> /// <param name="writer"></param> void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { writer.WriteAttributeString("xmlns", "i", null, Namespaces.SchemaInstance); writer.WriteAttributeString("xmlns", "q", null, Namespaces.Query); writer.WriteAttributeString("ActionId", ActionId.ToString()); writer.WriteAttributeString("Type", Type.ToString()); writer.WriteAttributeString("EntityFullName", EntityFullName); writer.WriteAttributeString("ProviderName", ProviderName ?? ""); writer.WriteAttributeString("RowVersion", (RowVersion.HasValue) ? RowVersion.ToString() : ""); writer.WriteAttributeString("CommandTimeout", CommandTimeout.ToString()); writer.WriteStartElement("Parameters", Namespaces.Data); if (Parameters != null) { foreach (System.Xml.Serialization.IXmlSerializable parameter in Parameters) { writer.WriteStartElement("Parameter", Namespaces.Data); parameter.WriteXml(writer); writer.WriteEndElement(); } } writer.WriteEndElement(); writer.WriteStartElement("Conditional"); if (Conditional != null) { ((System.Xml.Serialization.IXmlSerializable)Conditional).WriteXml(writer); } writer.WriteEndElement(); writer.WriteStartElement("AlternativeActions"); foreach (System.Xml.Serialization.IXmlSerializable action in AlternativeActions) { writer.WriteStartElement("PersistenceAction", Namespaces.Data); action.WriteXml(writer); writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteStartElement("BeforeActions"); foreach (System.Xml.Serialization.IXmlSerializable action in BeforeActions) { writer.WriteStartElement("PersistenceAction", Namespaces.Data); action.WriteXml(writer); writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteStartElement("AfterActions"); foreach (System.Xml.Serialization.IXmlSerializable action in AfterActions) { writer.WriteStartElement("PersistenceAction", Namespaces.Data); action.WriteXml(writer); writer.WriteEndElement(); } writer.WriteEndElement(); WriteItem <Colosoft.Query.StoredProcedureName>(writer, "StoredProcedureName", _storedProcedureName ?? Colosoft.Query.StoredProcedureName.Empty); writer.WriteStartElement("Query"); if (Query != null) { ((System.Xml.Serialization.IXmlSerializable)Query).WriteXml(writer); } writer.WriteEndElement(); }
/// <inheritdoc /> protected override string FieldToString(FieldId field, RowVersion version) { return(field switch { FieldId.LastUpdate => "Day", FieldId.CountryRegion => "Country", FieldId.ProvinceState => "Province", FieldId.Admin2 => "County", FieldId.ContinentName => "Continent", FieldId.Population => "Population", FieldId.CombinedKey => "StatsName", _ => field.ToString() });
private bool ValidateColumnsOrder(RowVersion version, IEnumerable <string> headerColumns) { using var headerEnumerator = headerColumns.GetEnumerator(); using var fieldsEnumerator = VersionFieldsDictionary[version].GetEnumerator(); while (headerEnumerator.MoveNext() && fieldsEnumerator.MoveNext()) { if (headerEnumerator.Current != FieldToString(fieldsEnumerator.Current, version)) { return(false); } } return(true); }
/// <inheritdoc /> protected override string FieldToString(FieldId field, RowVersion version) { return(field switch { FieldId.Confirmed => "Заражений", FieldId.LastUpdate => "Дата", FieldId.ProvinceState => "Регион", FieldId.Recovered => "Выздоровлений", FieldId.Deaths => "Смертей", FieldId.DeathsByDay => "Смертей за день", FieldId.ConfirmedByDay => "Заражений за день", FieldId.RecoveredByDay => "Выздоровлений за день", _ => field.ToString() });
void Validate(Func <object, byte[]> getter, Action <object, byte[]> setter, IProperty primaryKey, object primaryKeyValue, List <object> objects) { byte[] rowVersion; var first = objects.First(); var exceptionSuffix = $" Type: {first.GetType().FullName}. {primaryKey.Name}: {primaryKeyValue}."; //If seen var version = getter(first); if (seen.Any(x => ReferenceEquals(x, first))) { rowVersion = version; if (rowVersion == null) { throw new Exception($"Row version has been incorrectly set to null. {exceptionSuffix}"); } } //If not seen else { if (version != null) { throw new Exception($"The first save must have a null RowVersion. {exceptionSuffix}"); } rowVersion = RowVersion.New(); setter(first, rowVersion); seen.Add(first); } foreach (var o in objects.Skip(1)) { var bytes = getter(o); if (bytes != null && bytes.SequenceEqual(rowVersion)) { rowVersion = RowVersion.New(); setter(o, rowVersion); } } }
void Validate(Func <object, byte[]> getter, Action <object, byte[]> setter, List <object> objects) { byte[] rowVersion; var first = objects.First(); var version = getter(first); if (seen.Any(x => ReferenceEquals(x, first))) { rowVersion = version; if (rowVersion == null) { throw new Exception("Row version has been incorrectly set to null"); } } //If not seen else { if (version != null) { throw new Exception("The first save must have a null RowVersion"); } rowVersion = RowVersion.New(); setter(first, rowVersion); seen.Add(first); } foreach (var o in objects.Skip(1)) { var bytes = getter(o); if (bytes != null && bytes.SequenceEqual(rowVersion)) { rowVersion = RowVersion.New(); setter(o, rowVersion); } } }
/// <summary> /// Initializes the Globa Data Model. /// </summary> static ServerDataModel() { // IMPORTANT CONCEPT: The RowVersion object keeps track of the 'age' of the server dataset. When rows are added, // updated and deleted, the row version is incremented. There is a single, monotonically incrementing value used for // the entire DataSet. We initialize the database with a value of 1, that allows the client to use a value of zero // when initializing. A requested row version of zero will guarantee that all the contents of the database are // returned to the client. ServerDataModel.rowVersion = new RowVersion(); // The persistent storage device used by this server is specified in this custom configuration section. PersistentStoreSection persistentStoreSection = (PersistentStoreSection)ConfigurationManager.GetSection("persistentStoreSection"); PersistentStoreInfo persistentStoreInfo = persistentStoreSection[PersistentStore]; if (persistentStoreInfo == null) { throw new Exception("There is no persistent storage device defined for this server."); } SqlConnection sqlConnection = new SqlConnection(persistentStoreInfo.ConnectionString); try { // Make sure all the tables are locked before populating them. foreach (TableLock tableLock in ServerDataModel.TableLocks) { tableLock.AcquireWriterLock(CommonTimeout.LockWait); } // IMPORTANT CONCEPT: These filters are used for security and performance. They remove elements that a user has // no right to view on the local client. They must also guarantee referential integrity if a record is removed. // That is, if you have a filter to remove an element, there must be another filter to guarantee the children of // that element are not returned to the client. ServerDataModel.Object.UserFilter = new RowFilterDelegate(FilterObject); ServerDataModel.Security.UserFilter = new RowFilterDelegate(FilterSecurity); ServerDataModel.Price.UserFilter = new RowFilterDelegate(FilterPrice); ServerDataModel.Currency.UserFilter = new RowFilterDelegate(FilterCurrency); ServerDataModel.Debt.UserFilter = new RowFilterDelegate(FilterDebt); ServerDataModel.Equity.UserFilter = new RowFilterDelegate(FilterEquity); // A view is needed for all the tables so we can search the records according to 'age'. The 'RowVersion' is an // indication of the relative age of an record. When a client requests a refresh of it's data model, we will // return any records that are younger than the oldest record on the client. To find these records efficiently, we // need this view on the table. foreach (Table table in ServerDataModel.Tables) { table.DefaultView.Sort = "RowVersion DESC"; table.DefaultView.RowStateFilter = DataViewRowState.OriginalRows; } // Open a connection to the server and read all the tables into the data model. sqlConnection.Open(); // Constraints are disabled so the data can be loaded in table-by-table from the persistent store. ServerDataModel.EnforceConstraints = false; // This will keep track of the largest row number in the persistent data store. This maximum row version will // become the seed value for the system-wide row version that is assigned to every row that is added, updated or // deleted. long maxRowVersion = 0; // Read each of the persistent tables from the relational database store. foreach (Table table in ServerDataModel.Tables) { if (table.IsPersistent) { // Every record has an 'Archived' bit which indicates whether the record has been deleted from the active // database. This bit is used here to discard records that shouldn't be included in the active database. // While it would be possible to simply select all the records where the 'Archived' bit is clear, they are // added to the table and then deleted in order to keep the identity columns of the ADO tables synchronized // with the indices of the persistent SQL tables. This section of code will construct a statement that // will select all the persistent columns from the relational database. string columnList = "IsArchived, IsDeleted"; foreach (Column column in table.Columns) { if (column.IsPersistent) { columnList += (columnList == string.Empty ? string.Empty : ", ") + "\"" + column.ColumnName + "\""; } } string selectCommand = String.Format("select {0} from \"{1}\"", columnList, table.TableName); // Execute the command to read the table. SqlCommand sqlCommand = new SqlCommand(selectCommand); sqlCommand.Connection = sqlConnection; SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(); // IMPORTANT CONCEPT: The persistent database can store many deleted and archived rows. To minimize the // amount of memory needed to read these tables, the rows are reused as they are read when the row has been // deleted or archived. Active rows will be added to the data model and a new row will be created the next // time a row from that table is read. Row row = null; // Read each record from the table into the ADO database. while (sqlDataReader.Read()) { // Create a new record (unless the row is being reused because it was deleted or archived). if (row == null) { row = (Row)table.NewRow(); } // Read all records from the database, throw away the ones that are archived. This will keep the // identity columns in the ADO database from repeating any of the key elements in the SQL database. bool isArchived = false; bool isDeleted = false; for (int column = 0; column < sqlDataReader.FieldCount; column++) { string columnName = sqlDataReader.GetName(column); if (columnName == "IsArchived") { isArchived = (bool)sqlDataReader.GetValue(column); } if (columnName == "IsDeleted") { isDeleted = (bool)sqlDataReader.GetValue(column); } DataColumn destinationColumn = table.Columns[columnName]; if (destinationColumn != null) { row[destinationColumn] = sqlDataReader.GetValue(column); } } // IMPORTANT CONCEPT: The initial system-wide row version used by the in-memory data model to keep // track of the age of a record will be the maximum row version read from the persistent store // (including deleted and archived rows). maxRowVersion = row.RowVersion > maxRowVersion ? row.RowVersion : maxRowVersion; // Add active rows to the ADO table, reuse deleted and archived rows for the next record read. if (!isArchived && !isDeleted) { table.Rows.Add(row); row = null; } } // This is the end of reading a table. Close out the reader, accept the changes and move on to the next // table in the DataSet. sqlDataReader.Close(); table.AcceptChanges(); } } // This is the row version that will be used for all inserted, modified and deleted records. rowVersion.Set(maxRowVersion); // Once all the tables have been read, the constraints can be enforced again. This is where any Relational // Integrity problems will kick out. ServerDataModel.EnforceConstraints = true; // These masks are used to dynamically filter the data that is returned to the client. ServerDataModel.masks = new Hashtable(); ServerDataModel.maskLocks = new Hashtable(); ServerDataModel.maskLock = new TableLock("Mask.Master"); // Run through each of the users and create a mask for their pricing data. foreach (ServerDataModel.UserRow userRow in ServerDataModel.User.Rows) { // UserRow.UserName must match MarkThree.Quasar.Server.Environment.UserName which is always lower case. string maskName = string.Format("Mask.{0}", userRow.UserName.ToLower()); DataSet maskSet = new DataSet(maskName); ServerDataModel.masks[userRow.UserName.ToLower()] = maskSet; ServerDataModel.maskLocks[userRow.UserName.ToLower()] = new TableLock(maskName); // The mask has a single table with the security identifier in it. Any price that matches the security // identifier is returned to the client. DataTable priceTable = maskSet.Tables.Add("Price"); DataColumn securityIdColumn = priceTable.Columns.Add("SecurityId", typeof(int)); priceTable.PrimaryKey = new DataColumn[] { securityIdColumn }; } } catch (ConstraintException constraintException) { // Write out the exact location of the error. foreach (DataTable dataTable in ServerDataModel.Tables) { foreach (DataRow dataRow in dataTable.Rows) { if (dataRow.HasErrors) { EventLog.Error("Error in '{0}': {1}", dataRow.Table.TableName, dataRow.RowError); } } } // Rethrow the exception. throw constraintException; } catch (SqlException sqlException) { // Write out the exact location of the error. foreach (SqlError sqlError in sqlException.Errors) { EventLog.Error(sqlError.Message); } // Rethrow the exception. throw sqlException; } finally { // Release all of the write locks. foreach (TableLock tableLock in ServerDataModel.TableLocks) { if (tableLock.IsWriterLockHeld) { tableLock.ReleaseWriterLock(); } } // Make sure the sqlConnection is closed, even when an exception happens. sqlConnection.Close(); } }
/// <summary> /// Serializa o objeto. /// </summary> /// <param name="writer"></param> public void Serialize(Serialization.IO.CompactWriter writer) { writer.Write(ActionId); writer.Write((int)Type); writer.Write(EntityFullName); writer.Write(ProviderName); writer.Write((RowVersion.HasValue) ? RowVersion.ToString() : ""); if (Parameters != null) { writer.Write(Parameters.Count); foreach (ICompactSerializable parameter in Parameters) { parameter.Serialize(writer); } } else { writer.Write(0); } if (Conditional != null) { writer.Write(true); ((ICompactSerializable)Conditional).Serialize(writer); } else { writer.Write(false); } writer.Write(AlternativeActions.Count); foreach (ICompactSerializable action in AlternativeActions) { action.Serialize(writer); } writer.Write(BeforeActions.Count); foreach (ICompactSerializable action in BeforeActions) { action.Serialize(writer); } writer.Write(AfterActions.Count); foreach (ICompactSerializable action in AfterActions) { action.Serialize(writer); } if (_storedProcedureName != null) { writer.Write(true); ((ICompactSerializable)_storedProcedureName).Serialize(writer); } else { writer.Write(false); } if (Query != null) { writer.Write(true); ((ICompactSerializable)Query).Serialize(writer); } else { writer.Write(false); } }
public void ValidateJHopkinsVersionStrings(string header, RowVersion version) { var provider = new JHopkinsDataProvider(); Assert.That(provider.GetVersion(header.SplitCsvRowString().ToArray()), Is.EqualTo(version)); }
/// <inheritdoc /> public IEnumerable <FieldId> GetFields(RowVersion version) { return(VersionFieldsDictionary.ContainsKey(version) ? VersionFieldsDictionary[version] : Enumerable.Empty <FieldId>()); }
public static EventStreamRow FromRaw(string rawDataString) { var parsed = JsonConvert.DeserializeObject <List <dynamic> >(rawDataString); // Synapse 0.99.4+ sends ["ev", [...]] - the 'ev' represents the kind of event. RowKind kind = RowKind.Unknown; if (parsed[0] == "ev") { kind = RowKind.Event; } if (parsed[0] == "state") { kind = RowKind.State; } var secondArgType = (parsed[1] as object).GetType().FullName; RowVersion version = secondArgType == typeof(JArray).FullName ? RowVersion.Post0994 : RowVersion.Pre0994; // We have to convert the JArray to something useful var rowData = version == RowVersion.Post0994 ? ((JArray)parsed[1]).ToObject <List <dynamic> >() : new List <dynamic>(); string relatesToEventId = null; if (rowData.Count >= 6) { version = RowVersion.Post0995; relatesToEventId = rowData[5]; } switch (kind) { case RowKind.Event: return(new EventStreamRow { Kind = kind, SynapseVersion = version, EventId = rowData[0], RoomId = rowData[1], EventType = rowData[2], StateKey = rowData[3], RedactsEventId = rowData[4], RelatesToEventId = relatesToEventId, }); case RowKind.State: return(new EventStreamRow { Kind = kind, SynapseVersion = version, RoomId = rowData[0], EventType = rowData[1], StateKey = rowData[2], EventId = rowData[3], }); default: if (version == RowVersion.Pre0994) { return(new EventStreamRow { Kind = kind, SynapseVersion = version, EventId = parsed[0], RoomId = parsed[1], EventType = parsed[2], StateKey = parsed[3], RedactsEventId = parsed[4], RelatesToEventId = relatesToEventId, }); } else { return(new EventStreamRow { Kind = kind, SynapseVersion = version }); } } }
/// <summary> /// Helper function that returns a <see cref="string"/> representation of given <see cref="FieldId"/> in the particular <see cref="RowVersion"/>. /// </summary> /// <param name="field">Field to convert to string.</param> /// <param name="version"><see cref="RowVersion"/> to where this field come from.</param> /// <returns>A <see cref="string"/> representation of given <see cref="FieldId"/>.</returns> protected abstract string FieldToString(FieldId field, RowVersion version);
/// <summary> /// Initializes a new instance of the <see cref="Row"/> class. /// </summary> /// <param name="fields"><see cref="Field"/> collection to store in row.</param> /// <param name="version">Row version.</param> public Row(IEnumerable <Field> fields, RowVersion version) { Version = version; _data = fields.ToDictionary(_ => _.Id, _ => _.Value); }
public void JHopkinsDataProviderDoesntFailOnGetFields([Values] RowVersion version) { var provider = new JHopkinsDataProvider(); Assert.DoesNotThrow(() => { provider.GetFields(version); }); }
internal static string SetParameterValue(this RowVersion value) => $"{((ulong)value).GetBytes().ToHexString()}";
public void Hook(IPipelines p) { // Use Pipeline Hook so that we can get information about the requests before performing action // with the data p.AfterRequest.AddItemToStartOfPipeline((ctx) => { if (_Events == null) { return; } var siteconfig = ctx.Items["CurrentSite"] as JObject; var user = ctx.CurrentUser as NcbUser; // this will not work when more than one person updating the site at the same time var events = _Events.ToList(); _Events = null; if (siteconfig.Property("watcher") == null) { return; // not configured } var watcher = siteconfig.Property("watcher").Value as JObject; var userIP = ctx.Request.UserHostAddress; Task.Run(() => { foreach (var item in events) { var datatype = watcher.Property(item.DataTypeName.ToLowerInvariant()); if (datatype == null) { continue; } var config = datatype.Value.ToObject <WatcherConfig>(); if (item.Action == "newAttachments") { if (config.autoPrintAttachment == true) { new DataWatcherHub().PrintDocument(item.AffectedRow); } continue; } if (config.version == true) { var version = new RowVersion() { Action = item.Action, RowData = item.AffectedRow, UserId = user.Id, UserHostAddress = userIP, __createdAt = DateTime.Now, RowId = (int)item.AffectedRow.Id, DataType = item.DataTypeName }; item.Database.UpsertRecord(version); } var emailConfig = config[item.Action]; if (emailConfig != null) { var autoGenPdfConfig = emailConfig.autoGeneratePDF; if (autoGenPdfConfig != null && autoGenPdfConfig.enable == true) { Object dataToClient = new { config = autoGenPdfConfig, data = item.AffectedRow, }; new DataWatcherHub().GenPDFandUpload(dataToClient); } if (emailConfig.enable) { var subject = Razor.Parse <DatabaseEvent>(emailConfig.emailSubject, item); var body = Razor.Parse <DatabaseEvent>(emailConfig.emailTemplate, item); MailSenderModule.SendEmail(emailConfig.sendTo, subject, body); } } } }); }); }
void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { writer.WriteAttributeString("ActionId", ActionId.ToString()); writer.WriteAttributeString("Success", Success.ToString()); writer.WriteAttributeString("AffectedRows", AffectedRows.ToString()); writer.WriteAttributeString("RowVersion", RowVersion.ToString()); writer.WriteStartElement("FailureMessage"); if (!string.IsNullOrEmpty(FailureMessage)) { writer.WriteValue(FailureMessage); } writer.WriteEndElement(); writer.WriteStartElement("Result"); if (Result != null) { writer.WriteValue(Result); } writer.WriteEndElement(); writer.WriteStartElement("Parameters", Namespaces.Data); if (Parameters != null) { foreach (System.Xml.Serialization.IXmlSerializable parameter in Parameters) { writer.WriteStartElement("Parameter", Namespaces.Data); if (parameter != null) { parameter.WriteXml(writer); } writer.WriteEndElement(); } } writer.WriteEndElement(); writer.WriteStartElement("AlternativeActions", Namespaces.Data); foreach (System.Xml.Serialization.IXmlSerializable parameter in AlternativeActions) { writer.WriteStartElement("PersistenceActionResult", Namespaces.Data); if (parameter != null) { parameter.WriteXml(writer); } writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteStartElement("BeforeActions", Namespaces.Data); foreach (System.Xml.Serialization.IXmlSerializable parameter in BeforeActions) { writer.WriteStartElement("PersistenceActionResult", Namespaces.Data); if (parameter != null) { parameter.WriteXml(writer); } writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteStartElement("AfterActions", Namespaces.Data); foreach (System.Xml.Serialization.IXmlSerializable parameter in AfterActions) { writer.WriteStartElement("PersistenceActionResult", Namespaces.Data); if (parameter != null) { parameter.WriteXml(writer); } writer.WriteEndElement(); } writer.WriteEndElement(); }