WCF & auditing

Posts   
 
    
jlozina
User
Posts: 17
Joined: 13-Mar-2008
# Posted on: 16-Jun-2008 06:22:01   

Hi,

I'm using the auditing functionality of LLBLgen with WCF services. I have a problem when an entity returns back from a client and save it it records an UpdateOfExistingEntity but not a EntityFieldSet. I have tested to see that the EntityFieldSet auditing works fine if the entity isn’t sent and received via a WCF service.

What I am doing is sending an entity to a WPF application via a WCF service, changing the value of a field and sending it back to the WCF service where I want the field change audited.

I have followed the auditing example using the DatabaseAuditor class.

Version is 2.5 final

Regards Joe

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 16-Jun-2008 08:05:51   

How and when is the Auditor set to the entity?

Which LLBLGen Pro runtime library version are you using? (consult: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7722)

jlozina
User
Posts: 17
Joined: 13-Mar-2008
# Posted on: 16-Jun-2008 09:26:21   

I am using dependency injection in the WCF project as in the Auditing example.


<configSections>
        <section name="dependencyInjectionInformation" type="SD.LLBLGen.Pro.ORMSupportClasses.DependencyInjectionSectionHandler, SD.LLBLGen.Pro.ORMSupportClasses.NET20, Version=2.5.0.0, Culture=neutral, PublicKeyToken=ca73b74ba4e3ff27"/>
    </configSections>
    <dependencyInjectionInformation>
        <additionalAssemblies>
            <assembly filename="MMS.DatabaseAuditor.dll"/>
            <assembly filename="MMS.DatabaseAuditorSpecific.dll"/>
        </additionalAssemblies>
    </dependencyInjectionInformation>

Version is 2.5.0.0 Runtime 2.0.50727

jlozina
User
Posts: 17
Joined: 13-Mar-2008
# Posted on: 16-Jun-2008 09:44:41   

I saw the Adapter specific: Auditors in XML serialization/deserialization scenarios in the documentation. Do you have an example of how to do this?

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 16-Jun-2008 10:07:08   
jlozina
User
Posts: 17
Joined: 13-Mar-2008
# Posted on: 16-Jun-2008 15:24:38   

Ive read that post, is it possible to get example of how to do it with the auditing example? Thanks in advance. Joe

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39617
Joined: 17-Aug-2003
# Posted on: 17-Jun-2008 14:14:19   

I think that's given in that thread, semi-last post. (how to get started)

Frans Bouma | Lead developer LLBLGen Pro
jlozina
User
Posts: 17
Joined: 13-Mar-2008
# Posted on: 18-Jun-2008 15:12:47   

Is it only the List<AuditInfoEntity> _auditInfoEntities that I need to serialize in the WriteXml?


        public override void WriteXml(XmlWriter writer, XmlFormatAspect aspects, Dictionary<Guid, IEntity2> processedObjectIDs)
        {

            writer.WriteStartElement("List");
            foreach (AuditInfoEntity auditInfoEntity in _auditInfoEntities)
            {
                string auditAsXml = string.Empty;
                auditInfoEntity.WriteXml(out auditAsXml);

                writer.WriteString(auditAsXml);
            }

            writer.WriteEndElement();

        }

btw. I am using the DatabaseAuditor : AuditorBase with dependency injection from the auditing example.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 18-Jun-2008 16:07:54   

You should use the same passed xmlWriter object, as follows:

  public override void WriteXml(XmlWriter writer, XmlFormatAspect aspects, Dictionary<Guid, IEntity2> processedObjectIDs)
        {
            foreach (AuditInfoEntity auditInfoEntity in _auditInfoEntities)
            {
                auditInfoEntity.WriteXml(writer);
            }
        }

Or

  public override void WriteXml(XmlWriter writer, XmlFormatAspect aspects, Dictionary<Guid, IEntity2> processedObjectIDs)
        {
            _auditInfoEntities.WriteXml(writer);
        }
jlozina
User
Posts: 17
Joined: 13-Mar-2008
# Posted on: 18-Jun-2008 16:35:28   

This doesn’t seem to work, List doesn’t contain a definition for WriteXml

_auditInfoEntities.WriteXml(writer);

Is this all I need to do for the WriteXml?

public override void WriteXml(XmlWriter writer, XmlFormatAspect aspects, Dictionary<Guid, IEntity2> processedObjectIDs) { foreach (AuditInfoEntity auditInfoEntity in _auditInfoEntities) { auditInfoEntity.WriteXml(writer); } }

Is there anything else I need to serialize?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39617
Joined: 17-Aug-2003
# Posted on: 19-Jun-2008 13:49:56   

Serialization works due to the idea that only data is serialized, not objects. So at deserialization time, the objects to deserialize are instantiated first, and then the data is deserialized.

So if you have an auditor, and you want to serialize the auditor's contents, you've to serialize all the data in the auditor in such a way that you can deserialize it back into objects.

If your auditor only contains entity objects, you can serialize them one by one. However, you've to know up front when to stop. simple_smile

so it's ok to serialize the data as: <entities> <entity... <entity... </entities>

this gives you the ability to stop reading data.

So do:

writer.WriteStartElement("Entities"); // <Entities>
foreach (AuditInfoEntity auditInfoEntity in _auditInfoEntities)
{
    auditInfoEntity.WriteXml(writer);
}
writer.WriteEndElement(); // </Entities>

for deserialization, you've to read till /Entities is seen. When you get the call to ReadXml, the reader is located at <Entities>. So do:

string startElementName = reader.LocalName;
while(reader.Read() && !((reader.LocalName == startElementName) && (reader.NodeType == XmlNodeType.EndElement)))
{
      AuditInfoEntity toDeserialize = new AuditInfoEntity();
     toDeserialize.ReadXml(Reader);
     collection.Add(toDeserialize);
}

If you run into issues, don't worry nor panic. Hassling with XmlReaders/writers is a bit of a struggle at first. To check whether the xml looks ok, save the xml to a file so you can examine it (e.g in a webbrowser). WHen deserializing and things go wrong, for example you miss an endelement or scan over it so everythign following it goes wrong), place a breakpoint in the auditor's ReadXml(reader) routine above and check at the end start of the while loop if the reader is indeed located at the start of an entity

Frans Bouma | Lead developer LLBLGen Pro
jlozina
User
Posts: 17
Joined: 13-Mar-2008
# Posted on: 19-Jun-2008 16:34:20   

It already seems to know when to stop reading, I dont need to use <entities>. Ive used the following code and it seems to work.

        public override void WriteXml(XmlWriter writer, XmlFormatAspect aspects, Dictionary<Guid, IEntity2> processedObjectIDs)
        {

            foreach (AuditInfoEntity auditInfoEntity in _auditInfoEntities)
            {
                auditInfoEntity.WriteXml(writer);
            }
        }


        public override void ReadXml(XmlNode auditorNode)
        {

        }


        public override void ReadXml(XmlReader reader)
        {

            StringBuilder xmltest = new StringBuilder();
            AuditInfoEntity toDeserialize = new AuditInfoEntity();

            string startElementName = reader.LocalName;
            while (reader.Read() && !((reader.LocalName == startElementName) && (reader.NodeType == XmlNodeType.EndElement)))
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element: // The node is an Element.
                        xmltest.Append("<" + reader.Name);

                        while (reader.MoveToNextAttribute()) // Read attributes.
                            xmltest.Append(" " + reader.Name + "='" + reader.Value + "'");
                        xmltest.Append(">");
                        break;
                    case XmlNodeType.Text: //Display the text in each element.
                        xmltest.Append(reader.Value);
                        break;
                    case XmlNodeType.EndElement: //Display end of element.
                        xmltest.Append("</" + reader.Name);
                        xmltest.Append(">");

                        if (reader.Name == "AuditInfoEntity")
                        {
                            toDeserialize.ReadXml(xmltest.ToString());
                            _auditInfoEntities.Add(toDeserialize);
                            toDeserialize = new AuditInfoEntity();
                            xmltest.Length = 0;
                        }
                        break;
                }

            }

        }

If there is a better way to do it please let me know.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 16-Feb-2010 16:18:12   

I didn't see the new attached solution.

Anyway, now I have checked it out, and I was able to make it work. The solution is not to write any start or end elements in the WriteXml()

        public override void ReadXml(System.Xml.XmlReader reader)
        {
            string startElementName = reader.LocalName;
            while (reader.Read() && !((reader.LocalName == startElementName) && (reader.NodeType == XmlNodeType.EndElement)))
            {
                AuditEntity toDeserialize = new AuditEntity();
                toDeserialize.ReadXml(reader);
                m_AuditInfoEntities.Add(toDeserialize);
            }
        }

        public override void WriteXml(System.Xml.XmlWriter writer, XmlFormatAspect aspects, Dictionary<Guid, IEntity2> processedObjectIDs)
        {
            //writer.WriteStartElement("Audits"); // <Entities>
            foreach (AuditEntity auditDB in m_AuditInfoEntities)
            {
                auditDB.WriteXml(writer);
            }
            //writer.WriteEndElement();
        }

I checked the database, and the Person was updated and the AuditEntity was saved too.

vaughnpr
User
Posts: 4
Joined: 23-Dec-2010
# Posted on: 23-Dec-2010 15:48:42   

Ok got it to work.

You have to be careful where you put the settings in the configs and also u need to use knowntypes.

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 23-Dec-2010 19:42:46   

Thanks for the update.

Matt

vaughnpr
User
Posts: 4
Joined: 23-Dec-2010
# Posted on: 29-Dec-2010 12:12:43   

I am running into new problems such as ORMEntityOutOfSyncException.

With the AuditUpdateOfExistingEntity function.

Here is how my current application works.

I have a WCF service app that has the config settings

 <section name="dependencyInjectionInformation" type="SD.LLBLGen.Pro.ORMSupportClasses.DependencyInjectionSectionHandler, SD.LLBLGen.Pro.ORMSupportClasses.NET20, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ca73b74ba4e3ff27"/>

  <assembly filename="Audit.dll"/>

In turn I have a client for the WCF.

This Client WCF is used by my WPF application with the same config as the WCF service.

Do I need to put the Audit config setting in the Client of the WCF rather?

Any ideas?

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 29-Dec-2010 15:11:27   

It depends. The DI works when the entity is instantiated, so if the client fetches the entity from the service and modify it, then it sends it back to be persisted (Updated), then you don't need to have the Auditor injected at the client side.

But if the clients creates entities and send them to the service to be persisted (Updated) as follows, then you'll need to have the auditor injected at the client.

var customer = new CustomerEntity()
customer.Id = "ALFKI";
customer.IsNew = false;
customer.SomeProperty = ....;
Service.SaveCustomer(customer);

But anyway I would avoid doing any plumbing at the client side. And so I would always try to inject objects at server side. Even if I have do that in code rather than using DI.

(Next time please open a new thread)