Is there available and end-to-end example of a Winforms client that uses LLBLGen over a Web Service? I've seen many threads that touch on this, but none that show code examples for each tier.
In my previous system we filled typed datasets from the database and sent these across the wire using Web methods. DataGridViews were bound to these, and on hitting save we did a GetChanges, sent this up to the server, did a save, sent this back down to the client and Merged this back into the dataset the grid was bound to. During the save our Oracle stored proc updated columns such as ModifiedTime (used for optimistic locking), ModifiedUser etc which was automatically updated in the DataRow and merged back into the DataSet on the client. The new values appeared in the grid.
This all worked very well but it was time consuming to develop, which is where I was hoping LLBLGen would speed up our development. However I have not yet successfully proved this and currently am getting a load of ORMEntityOutOfSyncException exceptions on the client.
My server WebMethod is as follows:
[WebMethod]
public EntityCollection<StodgepartyEntity> Save(EntityCollection<StodgepartyEntity> deletedEntities, List<StodgepartyEntity> dirtyEntities)
{
EntityCollection<StodgepartyEntity> dirtyCol = new EntityCollection<StodgepartyEntity>();
dirtyCol.AddRange(dirtyEntities);
using (DataAccessAdapter adapter = new DataAccessAdapter())
{
UnitOfWork2 uow=new UnitOfWork2();
uow.AddCollectionForDelete(deletedEntities);
uow.AddCollectionForSave(dirtyCol);
uow.Commit(adapter);
}
return dirtyCol;
}
The client Save method is:
private void buttonSave_Click(object sender, EventArgs e)
{
FxPosManServiceClient.WebService.StodgeLLBLGenWebService ws = new StodgeLLBLGenWebService();
ws.Credentials = CredentialCache.DefaultCredentials;
EntityCollection<StodgepartyEntity> res = ws.Save(_deleted,_dataSource.DirtyEntities);
Context context = new Context(true);
context.Add(_dataSource);
foreach (StodgepartyEntity returned in res)
{
StodgepartyEntity fetched = (StodgepartyEntity)context.Get(returned);
fetched.Fields.State = EntityState.Fetched;
fetched.IsDirty = false;
context.Get(returned);
}
dataGridView1.Refresh();
MessageBox.Show("Saved");
}
For optimistic locking we are using the following:
[DependencyInjectionInfo(typeof(IEntity2), "ConcurrencyPredicateFactoryToUse",
ContextType = DependencyInjectionContextType.NewInstancePerTarget,
TargetNamespaceFilter = "InHouse.FXPosMan.LLBLGen")]
[Serializable]
public class GeneralConcurrencyPredicateFactory : IConcurrencyPredicateFactory
{
public IPredicateExpression CreatePredicate(ConcurrencyPredicateType predicateTypeToCreate,
object containingEntity)
{
IPredicateExpression toReturn = new PredicateExpression();
EntityBase2 entity = (EntityBase2)containingEntity;
IEntityField2 field = entity.Fields["ModifiedTime"];
EntityField2 f = (EntityField2)EntityFieldFactory.Create(entity.GetType().Name, "ModifiedTime");
switch (predicateTypeToCreate)
{
case ConcurrencyPredicateType.Delete:
toReturn.Add(f == field.CurrentValue);
break;
case ConcurrencyPredicateType.Save:
// only for updates
toReturn.Add(f == field.CurrentValue);
break;
}
return toReturn;
}
}
which seems to work (an Oracle trigger updates ModifiedTime).
I've seen a thread that suggests reselecting all the rows and rebinding on the client, but this is wasteful if say only 100 out of 1000 rows have been updated, and is awkward for maintaining the users editing position in the grid.
Thanks in anticipation.
Stodge.