Unit of work and Service dealing with XDocument

S

shapper

Hello,

I have 4 services that deal with XML data.

I would like to create a InitOfWork pattern with Commit and Rollback
methods so I could use as follows:

ISession session = new Context(String path);
AssetService assetService = new AssetService(session);
DocumentService documentService = new DocumentService(session);
assetService.Create(asset)
documentService.DeleteById(1)
session.Commit();

So my idea is when I use session.Commit() the XML files used by each
service would be saved.
If I would choose Roolback the files would be disposed with no
changes.

A service example is as follows:

public class AssetService {

private XDocument _assets;

// AssetService
public AssetService(String path) {

if (String.IsNullOrEmpty(path))
throw new ArgumentNullException("path", "Path cannot be
null");
_assets = XDocument.Load(String.Concat(path, "Assets.xml"),
LoadOptions.SetBaseUri);

} // AssetService

public void Create(Models.Asset asset) {

Int32? id = _assets.Root.Elements("Asset").Select(s =>
Int32.Parse(s.Element("Id").Value)).Aggregate((Int32?)null, (a, i) => !
a.HasValue || a.Value < i ? i : a);
XElement _asset = new XElement("Asset",
new XElement("Id", id == null ? "1" : (id + 1).ToString()),
new XElement("Content", asset.Content),
new XElement("Locked", asset.Locked.ToString()),
new XElement("Name", asset.Name)
);
_assets.Root.Add(_asset);
_assets.Save(new Uri(_assets.BaseUri).LocalPath);

} // Create

public Models.Asset GetById(Int32 id) {

return _assets.Root.Elements("Asset").Select(s => new
Models.Asset {
Id = Int32.Parse(s.Element("Id").Value),
Content = s.Element("Content").Value,
Locked = Boolean.Parse(s.Element("Locked").Value),
Name = s.Element("Name").Value
}).FirstOrDefault(s => s.Id == id);

} // GetById

}

How can I create this UnitOfWork?

Thanks,
Miguel
 
A

Arne Vajhøj

shapper said:
Hello,

I have 4 services that deal with XML data.

I would like to create a InitOfWork pattern with Commit and Rollback
methods so I could use as follows:

ISession session = new Context(String path);
AssetService assetService = new AssetService(session);
DocumentService documentService = new DocumentService(session);
assetService.Create(asset)
documentService.DeleteById(1)
session.Commit();

So my idea is when I use session.Commit() the XML files used by each
service would be saved.
If I would choose Roolback the files would be disposed with no
changes.

A service example is as follows:

public class AssetService {

private XDocument _assets;

// AssetService
public AssetService(String path) {

if (String.IsNullOrEmpty(path))
throw new ArgumentNullException("path", "Path cannot be
null");
_assets = XDocument.Load(String.Concat(path, "Assets.xml"),
LoadOptions.SetBaseUri);

} // AssetService

public void Create(Models.Asset asset) {

Int32? id = _assets.Root.Elements("Asset").Select(s =>
Int32.Parse(s.Element("Id").Value)).Aggregate((Int32?)null, (a, i) => !
a.HasValue || a.Value < i ? i : a);
XElement _asset = new XElement("Asset",
new XElement("Id", id == null ? "1" : (id + 1).ToString()),
new XElement("Content", asset.Content),
new XElement("Locked", asset.Locked.ToString()),
new XElement("Name", asset.Name)
);
_assets.Root.Add(_asset);
_assets.Save(new Uri(_assets.BaseUri).LocalPath);

} // Create

public Models.Asset GetById(Int32 id) {

return _assets.Root.Elements("Asset").Select(s => new
Models.Asset {
Id = Int32.Parse(s.Element("Id").Value),
Content = s.Element("Content").Value,
Locked = Boolean.Parse(s.Element("Locked").Value),
Name = s.Element("Name").Value
}).FirstOrDefault(s => s.Id == id);

} // GetById

}

How can I create this UnitOfWork?

If you are on Vista/2008 then you could use NTFS with
transactions.

http://msdn.microsoft.com/en-us/magazine/cc163388.aspx

You would need to write some custom save code, but that
should be trivial.

More general solutions would require more code.

Arne
 
S

shapper

If you are on Vista/2008 then you could use NTFS with
transactions.

http://msdn.microsoft.com/en-us/magazine/cc163388.aspx

You would need to write some custom save code, but that
should be trivial.

More general solutions would require more code.

Now I got completely lost. I am thinking something like the following:

public interface ISession {

String Path { get; set; }
void Commit();
void Rollback();

} // ISession

public class AssetService : ISession {

private XDocument _assets;

// AssetService
public AssetService(ISession session) {

// Check session
if (session == null)
throw new ArgumentNullException("session", "Session cannot be
null");
_assets = XDocument.Load(String.Concat(session.Path,
"Assets.xml"), LoadOptions.SetBaseUri);

} // AssetService

// Commit
public void Commit() {
_assets.Save(new Uri(_assets.BaseUri).LocalPath);
} // Commit

// Rollback
public void Rollback() {
// ???? I don't know how to dispose the file with no changes
} // Rollback

// Create
public void Create(Models.Asset asset) {

Int32? id = _assets.Root.Elements("Asset").Select(s =>
Int32.Parse(s.Element("Id").Value)).Aggregate((Int32?)null, (a, i) => !
a.HasValue || a.Value < i ? i : a);
XElement _asset = new XElement("Asset",
new XElement("Id", id == null ? "1" : (id + 1).ToString()),
new XElement("Content", asset.Content),
new XElement("Locked", asset.Locked.ToString()),
new XElement("Name", asset.Name)
);
_assets.Root.Add(_asset);

} // Create

// GetById
public Models.Asset GetById(Int32 id) {

return _assets.Root.Elements("Asset").Select(s => new
Models.Asset {
Id = Int32.Parse(s.Element("Id").Value),
Content = s.Element("Content").Value,
Locked = Boolean.Parse(s.Element("Locked").Value),
Name = s.Element("Name").Value
}).FirstOrDefault(s => s.Id == id);

} // GetById

public void Update(Models.Asset asset) {

XElement _asset = _assets.Root.Elements("Asset").FirstOrDefault
(s => s.Element("Id").Value == asset.Id.ToString());
if (_asset != null) {
_asset.Element("Content").Value = asset.Content;
_asset.Element("Locked").Value = asset.Locked.ToString();
_asset.Element("Name").Value = asset.Name;
}

} // Update

} // AssetService

I am not sure how to dispose the XML file.
Anyway does this make any sense?

Thanks,
Miguel
 
A

Arne Vajhøj

shapper said:
Now I got completely lost. I am thinking something like the following:

public interface ISession {

String Path { get; set; }
void Commit();
void Rollback();

} // ISession

public class AssetService : ISession {

private XDocument _assets;

// AssetService
public AssetService(ISession session) {

// Check session
if (session == null)
throw new ArgumentNullException("session", "Session cannot be
null");
_assets = XDocument.Load(String.Concat(session.Path,
"Assets.xml"), LoadOptions.SetBaseUri);

} // AssetService

// Commit
public void Commit() {
_assets.Save(new Uri(_assets.BaseUri).LocalPath);
} // Commit

// Rollback
public void Rollback() {
// ???? I don't know how to dispose the file with no changes
} // Rollback

// Create
public void Create(Models.Asset asset) {

Int32? id = _assets.Root.Elements("Asset").Select(s =>
Int32.Parse(s.Element("Id").Value)).Aggregate((Int32?)null, (a, i) => !
a.HasValue || a.Value < i ? i : a);
XElement _asset = new XElement("Asset",
new XElement("Id", id == null ? "1" : (id + 1).ToString()),
new XElement("Content", asset.Content),
new XElement("Locked", asset.Locked.ToString()),
new XElement("Name", asset.Name)
);
_assets.Root.Add(_asset);

} // Create

// GetById
public Models.Asset GetById(Int32 id) {

return _assets.Root.Elements("Asset").Select(s => new
Models.Asset {
Id = Int32.Parse(s.Element("Id").Value),
Content = s.Element("Content").Value,
Locked = Boolean.Parse(s.Element("Locked").Value),
Name = s.Element("Name").Value
}).FirstOrDefault(s => s.Id == id);

} // GetById

public void Update(Models.Asset asset) {

XElement _asset = _assets.Root.Elements("Asset").FirstOrDefault
(s => s.Element("Id").Value == asset.Id.ToString());
if (_asset != null) {
_asset.Element("Content").Value = asset.Content;
_asset.Element("Locked").Value = asset.Locked.ToString();
_asset.Element("Name").Value = asset.Name;
}

} // Update

} // AssetService

I am not sure how to dispose the XML file.
Anyway does this make any sense?

If you save the files at commit, then you do not really need
to do anything at rollback (except not saving).

But commit and rollback are known transactional concepts and
the usage described does not match traditional database
transaction behavior.

Arne
 
S

shapper

But commit and rollback are known transactional concepts and
the usage described does not match traditional database
transaction behavior.

What do you mean? I can rename it Commit to Save if it is what you
mean and drop Rollback.

I am also not sure if I can use this in a way when I do session.Save()
I save all files used by all services that use those files.
I am not sure it this will work ... Because each service ha its own
instance XDocument.

Thanks,
Miguel
 
A

Arne Vajhøj

shapper said:
What do you mean? I can rename it Commit to Save if it is what you
mean and drop Rollback.

It it is not intended to have ACID semantics similar to
database usage, then I would pick some names that does not
give people the wrong associations. Or at least write something
in the documentation.
I am also not sure if I can use this in a way when I do session.Save()
I save all files used by all services that use those files.
I am not sure it this will work ... Because each service ha its own
instance XDocument.

If your real problem is to save multiple "thingies" when calling
a single Save method, then you should have all the "thingies"
register themselves at a place where the super save method can get
a list of what need to be saved.

Arne
 
S

shapper

If your real problem is to save multiple "thingies" when calling
a single Save method, then you should have all the "thingies"
register themselves at a place where the super save method can get
a list of what need to be saved.

Maybe I will post some name more appropriate to database, if that
happens please let me know.

Maybe I can do something as follows:

public interface ISession {
void Save();
} // ISession

public class Context : ISession {
public void Save() {
// Save the needed files
}
} // Context


// ArticleService
public ArticleService(ISession session) {
if (session == null)
throw new ArgumentNullException("session", "Session cannot be
null");
_context = (Context)session;
} // ArticleService

Context would have properties like Assets, Products, etc.
These would be XDocuments that could be loaded or not.
That decision is taken on each service if it needs the file or not.

As far as I can see the problem is a service does not know if the
other service already loaded it.
So maybe the files should be loaded on the Context? But how to know
what files to load according to the services that are being used?

Then on each service I would use:
_context.Assets. ....

Anyway, I am not sure if this is the correct approach or how to pull
this.

Any idea?

Thank you,
Miguel
 
A

Arne Vajhøj

shapper said:
Maybe I will post some name more appropriate to database, if that
happens please let me know.

Maybe I can do something as follows:

public interface ISession {
void Save();
} // ISession

public class Context : ISession {
public void Save() {
// Save the needed files
}
} // Context


// ArticleService
public ArticleService(ISession session) {
if (session == null)
throw new ArgumentNullException("session", "Session cannot be
null");
_context = (Context)session;
} // ArticleService

Context would have properties like Assets, Products, etc.
These would be XDocuments that could be loaded or not.
That decision is taken on each service if it needs the file or not.

As far as I can see the problem is a service does not know if the
other service already loaded it.
So maybe the files should be loaded on the Context? But how to know
what files to load according to the services that are being used?

Then on each service I would use:
_context.Assets. ....

Anyway, I am not sure if this is the correct approach or how to pull
this.

Any idea?

I am still not sure that I understand your context. Actually less
sure than 2 days ago.

But my best guess is still that you want something like:

public interface IService {
void Save();
}

public interface ISession {
void Save();
void RegisterForSave(IService srv);
}

public class FoobarService : IService {
public FoobarService(ISession ses) {
ses.RegisterForSave(this);
}
public void Save() {
...
}
}

public class MySession : ISession {
private List<IService> toBeSaved = new List<IService>();
public void Save() {
foreach(IService srv in toBeSaved) {
srv.Save();
}
}
public void RegisterForSave(IService srv) {
toBeSaved.Add(srv);
}
}

Arne
 
S

shapper

I am still not sure that I understand your context. Actually less
sure than 2 days ago.

But my best guess is still that you want something like:

public interface IService {
     void Save();

}

public interface ISession {
     void Save();
     void RegisterForSave(IService srv);

}

public class FoobarService : IService {
     public FoobarService(ISession ses) {
          ses.RegisterForSave(this);
     }
     public void Save() {
        ...
     }

}

public class MySession : ISession {
     private List<IService> toBeSaved = new List<IService>();
     public void Save() {
         foreach(IService srv in toBeSaved) {
             srv.Save();
         }
     }
     public void RegisterForSave(IService srv) {
          toBeSaved.Add(srv);
     }

}

Arne

Yes, I am not sure either and I am getting more and more confused. :)

But basically my initial idea was to have a common "class" to all
services.
When starting a service the XML file(s) needed in that service would
be loaded only if it wasn't loaded already by other service using the
same XML file.
If it was then it would use it ... Because some services access the
same files.
Then when wanting to submit the data i would call save and it would
save all XML files that are being used by the services using that
session.
So basically it would work as follows:

ISession session = new Session(String path);
AssetService assetService = new AssetService(session);
DocumentService documentService = new DocumentService(session);
assetService.Create(asset)
documentService.DeleteById(1)
session.Save();

path is where all the XML files are.

This was my idea.
 
A

Arne Vajhøj

shapper said:
Yes, I am not sure either and I am getting more and more confused. :)

That is not good. You are supposed to be the one knowing what
you want to do!

:)
But basically my initial idea was to have a common "class" to all
services.
When starting a service the XML file(s) needed in that service would
be loaded only if it wasn't loaded already by other service using the
same XML file.
If it was then it would use it ... Because some services access the
same files.
Then when wanting to submit the data i would call save and it would
save all XML files that are being used by the services using that
session.
So basically it would work as follows:

ISession session = new Session(String path);
AssetService assetService = new AssetService(session);
DocumentService documentService = new DocumentService(session);
assetService.Create(asset)
documentService.DeleteById(1)
session.Save();

path is where all the XML files are.

This was my idea.

I still think that it fits with what I outlined.

AssetService and DocumentService both registers themselves
with Session. So that session.Save() can call code in both
AssetService and DocumentService.

Arne
 
S

shapper

That is not good. You are supposed to be the one knowing what
you want to do!

:)

I completely agree with you! :)
I still think that it fits with what I outlined.

AssetService and DocumentService both registers themselves
with Session. So that session.Save() can call code in both
AssetService and DocumentService.

Yes, it does. I just added a property path in ISession interface and
inside the service that property is used to get the necessary XML
files.
It is working fine now and doing what I was hoping for.

Thank You For Your Help!!
Miguel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top