- Home
- LLBLGen Pro
- Architecture
Remoting IDataAccessAdapter with Manager Templates
Joined: 22-Oct-2007
I'm in the process of putting together a system which needs to talk to both oracle and sql server. I'm using LLBLGEN 2.5 against .net 3.0.
As I don't want to deal with the configuration, security and installation issues of letting clients talk directly to the database I would like to have this connectivity handled on a central server via a remoting interface.
I'm dealing with a developer pool which is totally unfamiliar with LLBLGEN. I love the simplicity of the self servicing model and think this would allow them to ramp up faster however the constraints of multiple databases basically rules that out.
Upon a little investigation I ran across the manager templates which seem like they would be a good answer to return that simplicity to the adapter model however I've noticed several things about that template group which appear to prevent this out of the box.
First off all the template methods are marked as static. Obviously this could be changed in the templates so that the managers were created as instances with instance methods but remoting the managers directly would still require more work such as creating interfaces they obeyed etc. It seems like this would also be a pain in that they'd have to be registered individually on the server as remoting endpoints so that might be a thorn in the side to maintain.
The second issue with the Manager templates is that they directly create DataAccessAdapters inline. IE "using( DataAccessAdapter adapter = new DataAccessAdapter() )" Obviously the templates could be modified to call a factory method which would return an IDataAccessAdapter but again I'd like the real DataAccessAdapters to live on the server.
From a programmatic standpoint it seems like it would be easiest to keep the Managers local to the client and then have them proxy their calls across a remoting boundary to a Decorator of the IDataAccessAdapter which would then talk to a factory created DataAccessAdapter. The local managers would just talk to the local IDataAccessAdapter which could call the server instance to do the heavy lifting. This would treat the adapter methods like exposed service methods allowing the most reuse for the least code on the client.
First question.... is this architecture sane? Has anyone tried something similar? If so did it work or is it a dead end? Has anyone got some manager templates they've already modified to do something similar?
Is there an easier approach that meet my constraints? I've been reading the forums searching for an answer for the past couple of days and drawn a blank. It seems like this would be a common problem for people who had to support multiple databases. Essentially I need something which will:
- Place all interaction with the real DataAccessAdapters on the server where the DataAccessAdapters can be either Oracle or Sql Server.
- Offer the simplicity of the manager templates/Self Servicing.
- Avoid registering potentially 100s of managers as remoting interface endpoints.
Any help, suggestions, code/template samples would be greatly appreciated.
Joined: 22-Oct-2007
Rogelio wrote:
Hi,
I can give my implementation with the adapter model, do you want it?
Absolutely! I've been context switching to some other tasks so I haven't gotten around to implementing it yet and I'm sure you've probably jumped over some hurdles I'm going to run into. I've modified the manager templates to create their own project and was getting ready to put in a factory implementation for the adapter so they can call off to a proxy adapter clientside which in turn calls the server one so that would be great if you've already crossed that bridge.
Thanks!
Scott
Joined: 08-Jul-2005
Rogelio wrote:
Hi,
I can give my implementation with the adapter model, do you want it?
Rogelio, I too would be interested in taking a look at your implementation. I have a similar situation that I am addressing and welcome any ideas on how to create a better / more flexible architecture.
Thanks
Chris
Joined: 29-Mar-2005
GizmoTN76 wrote:
Absolutely! I've been context switching to some other tasks so I haven't gotten around to implementing it yet and I'm sure you've probably jumped over some hurdles I'm going to run into. I've modified the manager templates to create their own project and was getting ready to put in a factory implementation for the adapter so they can call off to a proxy adapter clientside which in turn calls the server one so that would be great if you've already crossed that bridge.
Thanks!
Scott
Ok,
First the interface assembly:
<Serializable()> _
Public Class DataServerInfo
Implements ICloneable
Public SqlServer As String
Public Database As String
Public UserID As String
Public CultureName As String
.....
Public Function Clone() As Object Implements System.ICloneable.Clone
Dim info As DataServerInfo
info = Me.MemberwiseClone
Return info
End Function
End Class
The DataServerInfo class allows to use the same remote business objects
to access differences database (with the same structure, for example when
you have each one database for each branch. Normally people prefer to have
only one database with all branches data; but when you want performance
it is better to have one database per branch).
Public Enum ServiceMode
Local = 0
Remoting = 1
WebServices = 2
......
End Enum
This enumeration is for the kind of business objects that you want yo use.
At this moment I only work with local and remoting.
Public Interface IBasicBusiness
Function GetByPK(ByVal pk() As Object, ByVal serverInfo As DataServerInfo) As ORMSupportClasses.EntityBase2
Function GetByID(ByVal id() As Object, ByVal serverInfo As DataServerInfo) As ORMSupportClasses.EntityBase2
Function Update(ByVal entity As ORMSupportClasses.EntityBase2, ByVal serverInfo As DataServerInfo) As Object
Sub Delete(ByVal pk() As Object, ByVal serverInfo As DataServerInfo)
......
End Interface
This interface contains the basic methods that must be implemented by each
business class, I have not migrated it to generic yet.
Public Interface IBusinessFactory
Function GetCustomerBusiness() As Interfaces.ICustomerBusiness
.....
End Interface
This interface contains the functions that must be implemented by all
the factories (LocalServices, RemotingServices, etc) that return business object
Public Interface ICustomerBusiness
Inherits IBasicBusiness
End Interface
This interface is an example of interface for a business class (in this case Customer)
The business assembly:
Public MustInherit Class BasicBusiness
Inherits MarshalByRefObject
Implements Interfaces.IBasicBusiness, ....
Protected errorDelete As String
Protected recordDB As DataAccess.BasicDB ' this keep the data access object for Customer
' config the object to stay alive forever during remoting
Public Overrides Function InitializeLifetimeService() As Object
MyBase.InitializeLifetimeService()
Return Nothing
End Function
Public Overridable Sub Delete(ByVal pk() As Object, ByVal serverInfo As Interfaces.DataServerInfo) Implements Interfaces.IBasicBusiness.Delete
Try
' try to delete the record
Me.recordDB.Delete(pk, serverInfo, Nothing)
Catch ex As MyCustomAppException
Throw
Catch ex As Exception
Throw New MyCustomAppException(Me.errorDelete, ex)
End Try
End Sub
Public Overridable Function GetByID(ByVal id() As Object, ByVal serverInfo As Interfaces.DataServerInfo) As ORM.EntityBase2 Implements Interfaces.IBasicBusiness.GetByID
Dim entity As ORM.EntityBase2 = Me.recordDB.GetByID(id, serverInfo, Nothing)
Return entity
End Function
Public Overridable Function GetByPK(ByVal pk() As Object, ByVal serverInfo As Interfaces.DataServerInfo) As ORM.EntityBase2 Implements Interfaces.IBasicBusiness.GetByPK
Dim entity As ORM.EntityBase2 = Me.recordDB.GetByPK(pk, serverInfo, Nothing)
Return entity
End Function
Public Overridable Function Update(ByVal entity As ORM.EntityBase2, ByVal serverInfo As Interfaces.DataServerInfo) Implements Interfaces.IBasicBusiness.Update
Return Me.recordDB.Update(entity, serverInfo, False, Nothing)
End Function
........
End Class
This class implements the basic business interface , all the business classes will
inherit from this. Most methods are overridable to be allow overrides them in the specific business class.
Public Class CustomerBusiness
Inherits BasicBusiness
Implements ICustomerBusiness
Private Shared _instance As New CustomerBusiness
' singleton pattern
Public Shared ReadOnly Property Instance() As CustomerBusiness
Get
Return _instance
End Get
End Property
Private Sub New()
MyBase.New()
Me.errorDelete = "Error message when the customer could not be deleted"
Me.recordDB = DataAccess.UserDB.Instance
End Sub
End Class
The Data access assembly:
Imports ORM = SD.LLBLGen.Pro.ORMSupportClasses
Imports DH = .....HelperClasses
Imports DI = ........DataLibrary
Public MustInherit Class BasicDB
Protected errorRead As String
Protected errorUpdate As String
Protected MustOverride Function GetNewEntityPK(ByVal pk() As Object) As ORM.EntityBase2
Protected MustOverride Function GetNewEntityFactory() As ORM.IEntityFactory2
Protected MustOverride Sub ConfigBucketByID(ByVal id() As Object, ByVal bucket As ORM.RelationPredicateBucket)
Public Overridable Function GetByID(ByVal id() As Object, ByVal serverInfo As Interfaces.DataServerInfo, _
ByVal adapter As ORM.DataAccessAdapterBase) As ORM.EntityBase2
Dim bucket As New ORM.RelationPredicateBucket
Dim entities As DH.EntityCollection
Try
entities = New DH.EntityCollection(Me.GetNewEntityFactory)
Me.ConfigBucketByID(id, bucket)
Me.FillCollection(entities, bucket, serverInfo, adapter)
If entities.Count > 0 Then
Return entities(0)
Else
Return Nothing
End If
Catch ex As MyCustomAppException
Throw
Catch ex As Exception
Throw New MyCustomAppException(Me.errorRead, ex)
End Try
End Function
Public Overridable Function GetByPK(ByVal pk() As Object, ByVal serverInfo As Interfaces.DataServerInfo, _
ByVal adapter As ORM.DataAccessAdapterBase) As ORM.EntityBase2
Dim entity As ORM.EntityBase2
Try
entity = Me.GetNewEntityPK(pk)
Me.FetchEntity(entity, serverInfo, adapter)
If entity.Fields.State = ORM.EntityState.Fetched Then
Return entity
Else
Return Nothing
End If
Catch ex As MyCustomAppException
Throw
Catch ex As Exception
Throw New MyCustomAppException(Me.errorRead, ex)
End Try
End Function
Protected Sub FetchEntity(ByVal entity As ORM.EntityBase2, ByVal serverInfo As Interfaces.DataServerInfo, ByVal adapter As ORM.DataAccessAdapterBase)
Dim newAdapter As Boolean = False
Try
If adapter Is Nothing Then
adapter = DataSetSupport.GetNewDataAdapter(serverInfo, False)
newAdapter = True
End If
adapter.FetchEntity(entity)
Catch ex As MyCustomAppException
Throw
Catch ex As Exception
Throw New MyCustomAppException(Me.errorRead, ex)
Finally
If newAdapter AndAlso Not adapter Is Nothing Then
adapter.Dispose()
End If
End Try
End Sub
.....
End Class
This class implements the basic data access functions. All the data access classes
inherit from it.
Public Class CustomerDB
Inherits BasicDB
' singelton pattern
Public Shared ReadOnly Instance As New CustomerDB
Private Sub New()
Me.errorRead = ......
Me.errorUpdate = .........
End Sub
Protected Overrides Sub ConfigBucketByID(ByVal id() As Object, ByVal bucket As ORM.RelationPredicateBucket)
bucket.PredicateExpression.Add(DF.PredicateFactory.CompareValue(DataLibrary.CustomerFieldIndex.CustomerId, ORM.ComparisonOperator.Equal, id(0)))
End Sub
Protected Overrides Function GetNewEntityPK(ByVal pk() As Object) As SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2
Dim entity As New DataLibrary.EntityClasses.CustomerEntity
entity.CustomerPk = pk(0)
Return entity
End Function
Protected Overrides Function GetNewEntityFactory() As ORM.IEntityFactory2
Return New DataLibrary.FactoryClasses.CustomerEntityFactory
End Function
End Class
This class is an example of a data access class.
The business factory assembly:
Public NotInheritable Class BusinessFactory
Private Sub New()
End Sub
Private Shared _factory As IBusinessFactory
' to get the default business factory
Public Shared Function GetBusinessFactory() As Interfaces.IBusinessFactory
Return _factory
End Function
' this method allow to get a business factory that is not the default
Public Shared Function GetBusinessFactory(ByVal mode As Interfaces.ServiceMode) As Interfaces.IBusinessFactory
Select Case mode
Case Interfaces.ServiceMode.Local
Return LocalServices.Instance
Case Interfaces.ServiceMode.Remoting
Return RemotingServices.Instance
Case Interfaces.ServiceMode.WebServices
' Return WebServices.Instance
End Select
End Function
' to config the default business factory
Public Shared Sub ReadConfig()
Dim serviceMode As String
Dim _serviceMode As Interfaces.ServiceMode
serviceMode = System.Configuration.ConfigurationSettings.AppSettings("BusinessServiceMode")
If Not serviceMode Is Nothing Then
Select Case serviceMode.ToLower
Case "local"
_serviceMode = ServiceMode.Local
Case "remoting"
_serviceMode = ServiceMode.Remoting
Case "webservices"
_serviceMode = ServiceMode.WebServices
Case Else
_serviceMode = ServiceMode.Local
End Select
End If
_factory = GetBusinessFactory(_serviceMode)
End Sub
End Class
The businessFactory class is used to get factories that can return business object (the returned business objects can be local, remote, etc)
Friend Class RemotingServices
Implements Interfaces.IBusinessFactory
' singleton pattern
Public Shared ReadOnly Instance As New RemotingServices
Private Sub New()
Me._remoteServicesFactory = New Services.Remoting.RemoteServicesFactory
End Sub
Private _remoteServicesFactory As Services.Remoting.RemoteServicesFactory
Private ReadOnly Property RemoteServiceFactory() As Services.Remoting.RemoteServicesFactory
Get
Return Me._remoteServicesFactory
End Get
End Property
Private iCustomerBusiness As Interfaces.ICustomerBusiness
Public Function GetCustomerBusiness() As Interfaces.ICustomerBusiness Implements Interfaces.IBusinessFactory.GetCustomerBusiness
If Me.iCustomerBusiness Is Nothing Then
Me.iCustomerBusiness = Me.RemoteServiceFactory.GetSCustomerBusiness
End If
Return Me.iCustomerBusiness
End Function
End Class
Friend Class LocalServices
Implements Interfaces.IBusinessFactory
Private Sub New()
End Sub
Public Shared ReadOnly Instance As New LocalServices
Public Function GetCustomerBusiness() As Interfaces.ICustomerBusiness Implements Interfaces.IBusinessFactory.GetCustomerBusiness
Return CustomerBusiness.Instance
End Function
End Class
As you can see the LocalServices and RemotingServices implement IBusinessFactory, this allow to work with any instance of this classes as the IBusinessFactory.
The RemotingServices assembly. The RemoteServicesFactory class is used to get the business objects via Remoting, this is the only class that you need to register, with remoting, at the server and at the client, this is the only class in the assembly.
Public Class RemoteServicesFactory
Inherits MarshalByRefObject
Implements Interfaces.IBusinessFactory
' config the object to stay alive forever during remoting
Public Overrides Function InitializeLifetimeService() As Object
MyBase.InitializeLifetimeService()
Return Nothing
End Function
Public Function GetCustomerBusiness() As Interfaces.ISucursalCustomerBusiness Implements Interfaces.IBusinessFactory.GetCustomerBusiness
Return CustomerBusiness.Instance
End Function
.........
End Class
The UI assembly:
To register your remote object factory:
RemotingConfiguration.Configure("MyApp.exe.config")
Your MyApp.exe.config:
<appSettings file="user.config">
<add key="BusinessServiceMode" value="Local"/>
........
</appSettings>
<system.runtime.remoting>
<application name="MyAppClient">
<client>
<wellknown type="MyNameSpace.Remoting.RemoteServicesFactory, MyRemotingServicesAssembly" url="tcp://MyServer:MyPort/MyServer/RemoteServicesFactory" />
</client>
<channels>
<channel ref="tcp client">
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full" />
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
In you main form to config the default business factory:
BusinessFactory.ReadConfig()
When you want a customer business object:
Dim customerBS as ICustomerBusiness
customerBS = BusinessFactory.GetBusinessFactory().GetSucursalBusiness
As you can see your code do not know nothing about the source of your business objects, your customer business can be a local object or a remote object, all depend of your app configuration. At any time you can be using remote and local business objects at the same time.
The configuration at the server:
<system.runtime.remoting>
<application name="MyAppServer">
<service>
<wellknown mode="Singleton"
type="MyNameSpace.Remoting.RemoteServicesFactory, MyRemotingServicesAssembly"
objectUri="RemoteServicesFactory" />
</service>
<channels>
<channel ref="tcp server" port="MyPort">
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full" />
</serverProviders>
</channel>
</channels>
</application>
<customErrors mode="off" />
</system.runtime.remoting>
If you need additional details let me know.
Joined: 22-Oct-2007
Thanks so much! It's been a few years since I've done vb.net so I went ahead and did a c# conversion so I could study in my native tongue . I'm going to go ahead and post that conversion for the benefit of others. I really appreciate the effort you put into that post.
[Serializable()]
public class DataServerInfo : ICloneable
{
public string SqlServer;
public string Database;
public string UserID;
public string CultureName;
public object System.ICloneable.Clone()
{
DataServerInfo info;
info = this.MemberwiseClone;
return info;
}
}
public enum ServiceMode
{
Local = 0,
Remoting = 1,
WebServices = 2
}
public interface IBasicBusiness
{
ORMSupportClasses.EntityBase2 GetByPK(object[] pk, DataServerInfo serverInfo);
ORMSupportClasses.EntityBase2 GetByID(object[] id, DataServerInfo serverInfo);
object Update(ORMSupportClasses.EntityBase2 entity, DataServerInfo serverInfo);
void Delete(object[] pk, DataServerInfo serverInfo);
}
public interface IBusinessFactory
{
Interfaces.IUserBusiness GetUserBusiness();
}
public interface ICustomerBusiness : IBasicBusiness
{
}
public abstract class BasicBusiness : MarshalByRefObject, Interfaces.IBasicBusiness {
protected string errorDelete;
protected DataAccess.BasicDB recordDB;
// this keep the data access object for Customer
// config the object to stay alive forever during remoting
public override object InitializeLifetimeService() {
base.InitializeLifetimeService();
return null;
}
public virtual void Delete(object[] pk, Interfaces.DataServerInfo serverInfo) {
try {
// try to delete the record
this.recordDB.Delete(pk, serverInfo, null);
}
catch (MyCustomAppException ex) {
throw;
}
catch (Exception ex) {
throw new MyCustomAppException(this.errorDelete, ex);
}
}
public virtual ORM.EntityBase2 GetByID(object[] id, Interfaces.DataServerInfo serverInfo) {
ORM.EntityBase2 entity = this.recordDB.GetByID(id, serverInfo, null);
return entity;
}
public virtual ORM.EntityBase2 GetByPK(object[] pk, Interfaces.DataServerInfo serverInfo) {
ORM.EntityBase2 entity = this.recordDB.GetByPK(pk, serverInfo, null);
return entity;
}
public virtual void Update(ORM.EntityBase2 entity, Interfaces.DataServerInfo serverInfo) {
return this.recordDB.Update(entity, serverInfo, false, null);
}
}
public class CustomerBusiness : BasicBusiness, ICustomerBusiness {
private static CustomerBusiness _instance = new CustomerBusiness();
private CustomerBusiness() {
this.errorDelete = "Error message when the customer could not be deleted";
this.recordDB = DataAccess.UserDB.Instance;
}
// singleton pattern
public static CustomerBusiness Instance {
get {
return _instance;
}
}
}
public abstract class BasicDB
{
protected string errorRead;
protected string errorUpdate;
protected abstract ORM.EntityBase2 GetNewEntityPK(object[] pk);
protected abstract ORM.IEntityFactory2 GetNewEntityFactory();
protected abstract void ConfigBucketByID(object[] id, ORM.RelationPredicateBucket bucket);
public virtual ORM.EntityBase2 GetByID(object[] id, Interfaces.DataServerInfo serverInfo, ORM.DataAccessAdapterBase adapter)
{
ORM.RelationPredicateBucket bucket = new ORM.RelationPredicateBucket();
DH.EntityCollection entities;
try {
entities = new DH.EntityCollection(this.GetNewEntityFactory);
this.ConfigBucketByID(id, bucket);
this.FillCollection(entities, bucket, serverInfo, adapter);
if (entities.Count > 0) {
return entities(0);
}
else {
return null;
}
}
catch (MyCustomAppException ex) {
throw;
}
catch (Exception ex) {
throw new MyCustomAppException(this.errorRead, ex);
}
}
public virtual ORM.EntityBase2 GetByPK(object[] pk, Interfaces.DataServerInfo serverInfo, ORM.DataAccessAdapterBase adapter)
{
ORM.EntityBase2 entity;
try {
entity = this.GetNewEntityPK(pk);
this.FetchEntity(entity, serverInfo, adapter);
if (entity.Fields.State == ORM.EntityState.Fetched) {
return entity;
}
else {
return null;
}
}
catch (MyCustomAppException ex) {
throw;
}
catch (Exception ex) {
throw new MyCustomAppException(this.errorRead, ex);
}
}
protected void FetchEntity(ORM.EntityBase2 entity, Interfaces.DataServerInfo serverInfo, ORM.DataAccessAdapterBase adapter)
{
bool newAdapter = false;
try {
if (adapter == null) {
adapter = DataSetSupport.GetNewDataAdapter(serverInfo, false);
newAdapter = true;
}
adapter.FetchEntity(entity);
}
catch (MyCustomAppException ex) {
throw;
}
catch (Exception ex) {
throw new MyCustomAppException(this.errorRead, ex);
}
finally {
if (newAdapter && (adapter != null)) {
adapter.Dispose();
}
}
}
}
public class CustomerDB : BasicDB {
private CustomerDB Instance = new CustomerDB();
private CustomerDB() {
}
protected override void ConfigBucketByID(object[] id, ORM.RelationPredicateBucket bucket) {
bucket.PredicateExpression.Add(DF.PredicateFactory.CompareValue(DataLibrary.CustomerFieldIndex.CustomerId, ORM.ComparisonOperator.Equal, id(0)));
}
protected override SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2 GetNewEntityPK(object[] pk) {
DataLibrary.EntityClasses.CustomerEntity entity = new DataLibrary.EntityClasses.CustomerEntity();
entity.CustomerPk = pk(0);
return entity;
}
protected override ORM.IEntityFactory2 GetNewEntityFactory() {
return new DataLibrary.FactoryClasses.CustomerEntityFactory();
}
}
public sealed class BusinessFactory {
private static IBusinessFactory _factory;
private BusinessFactory() {
}
// to get the default business factory
public static Interfaces.IBusinessFactory GetBusinessFactory() {
return _factory;
}
// this method allow to get a business factory that is not the default
public static Interfaces.IBusinessFactory GetBusinessFactory(Interfaces.ServiceMode mode) {
switch (mode) {
case Interfaces.ServiceMode.Local:
return LocalServices.Instance;
break;
case Interfaces.ServiceMode.Remoting:
return RemotingServices.Instance;
break;
case Interfaces.ServiceMode.WebServices:
// Return WebServices.Instance
break;
}
}
// to config the default business factory
public static void ReadConfig() {
string serviceMode;
Interfaces.ServiceMode _serviceMode;
serviceMode = System.Configuration.ConfigurationSettings.AppSettings("BusinessServiceMode");
if (!(serviceMode == null)) {
switch (serviceMode.ToLower) {
case "local":
_serviceMode = ServiceMode.Local;
break;
case "remoting":
_serviceMode = ServiceMode.Remoting;
break;
case "webservices":
_serviceMode = ServiceMode.WebServices;
break;
default:
_serviceMode = ServiceMode.Local;
break;
}
}
_factory = GetBusinessFactory(_serviceMode);
}
}
class RemotingServices : Interfaces.IBusinessFactory {
private RemotingServices Instance = new RemotingServices();
private Services.Remoting.RemoteServicesFactory _remoteServicesFactory;
private Interfaces.ICustomerBusiness iCustomerBusiness;
private RemotingServices() {
this._remoteServicesFactory = new Services.Remoting.RemoteServicesFactory();
}
private Services.Remoting.RemoteServicesFactory RemoteServiceFactory {
get {
return this._remoteServicesFactory;
}
}
public Interfaces.ICustomerBusiness GetCustomerBusiness() {
if ((this.iCustomerBusiness == null)) {
this.iCustomerBusiness = this.RemoteServiceFactory.GetSCustomerBusiness;
}
return this.iCustomerBusiness;
}
}
public class RemoteServicesFactory : MarshalByRefObject, Interfaces.IBusinessFactory {
// config the object to stay alive forever during remoting
public override object InitializeLifetimeService() {
base.InitializeLifetimeService();
return null;
}
public Interfaces.ISucursalCustomerBusiness GetCustomerBusiness() {
return CustomerBusiness.Instance;
}
}
class LocalServices : Interfaces.IBusinessFactory {
private LocalServices Instance = new LocalServices();
private LocalServices() {
}
public Interfaces.ICustomerBusiness GetCustomerBusiness() {
return CustomerBusiness.Instance;
}
}
Joined: 22-Oct-2007
Rogelio, it looks like a pretty clean design in how you separated the layers out. Did you hand code the custom stuff per entity or did you use a template/code generator to handle it? It looks alot of the entity specific stuff could be done with templates, just curious if you were already.
Joined: 29-Mar-2005
GizmoTN76 wrote:
Rogelio, it looks like a pretty clean design in how you separated the layers out. Did you hand code the custom stuff per entity or did you use a template/code generator to handle it? It looks alot of the entity specific stuff could be done with templates, just curious if you were already.
I hand code the custom stuff. If you develop the template then let me know.