ASP.NET + LLBLGen Architecture Recommendation

Posts   
 
    
Posts: 94
Joined: 26-Feb-2006
# Posted on: 27-Feb-2006 00:23:39   

Dear folks,

I am doing some research on the different existing architectural concepts. I worked a lot with .NETTiers , MyGeneration and of course our beloved generator which is and there is no need for discussion the most versatile on the market. On my review of the different Architecture I came across the MS Design Guideline for Application and Services... I don´t want to borther you with the details but to summarize it the guide strongly recommend´s NOT to use custom business objects in ASP.NET apps. Instead it prefers the uses of (typed) DataSets and just in case of need to "hydrate" them to Objects.... To cut a long story short the whole ASP.net System is a bit DataSet centric and when I look at the "new" ASP.NET Examples it doesn´t look like that they are going to change that before LINQ or however it will be called.. (the ASP.NET 2.0 Architecture Guide even uses only WEBServices as the DataBackend.

We all know of the caveats of the DataSet compared to our LLBLEntities but the question is should we try to do market education or take what the market has to offer and do the best with it? I followed the threat about webservices and to me it seems that there are some points which will prevent LLBLEntities from working in the same manner through webservice as datasets do (correct me if I am wrong here!)

So to come to a point I would like to know if you found any alternative ASP.NET Architecture design guidelines (or developed your own) using LLbLEntities,manager classes,webservices,CSLA or whatever..may be we as a community are able to work out such a guideline?

Comments are highly appreciated!

Adrian

JimFoye avatar
JimFoye
User
Posts: 656
Joined: 22-Jun-2004
# Posted on: 27-Feb-2006 00:35:14   

I can tell you this, I've read some stupid stuff on MSDN about how to do things. I generally don't pay too much attention to them.

Posts: 94
Joined: 26-Feb-2006
# Posted on: 27-Feb-2006 01:10:21   

I can tell you this, I've read some stupid stuff on MSDN about how to do things. I generally don't pay too much attention to them.

Basically I agree...

but this is what I call market education...we can tell them but I think we will hit on deaf ears stuck_out_tongue_winking_eye If we must use ASP.net we have to arange with MS and this means we have to build solutions which integrate as good into "their" system as for example the DS and fullfils our wishes concerning clean Data Management (like LLBLEnities)

Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 27-Feb-2006 13:38:16   

If you write a stateless, service based middle tier, like manager classes, and you use the adapter pattern, you can wrap and expose your services using other techniques such as web services and remoting and whatever else comes down the pipe (indigo, windows workflow).

I beleive that to say hands down that all things asp.net should be XML DataSets is wrong.

Here is what I have found,

  • If I need to communicate across process boundaries, all components are on the same network, and all components are .NET I use remoting, and I expose my services layer using interfaces
  • If I need to communicate with other non-managed applications in a message based way, I use SOAP messages. I reserve xml datasets for the simple operations and the prototypes, because lets face it, sending a dataset with any amount of data over the wire is just a waste of bandwidth
  • If I dont need to accomodate point 1 or point 2, I make my application use the manager classes directly.

So to answer your question, by using the adapter pattern, you can already use LLBLGen to appease the listeners that only want to hear the buzz words, like web services and dataset.

Skeeterbug
User
Posts: 165
Joined: 21-May-2004
# Posted on: 27-Feb-2006 22:43:59   

Devildog74 wrote:

If you write a stateless, service based middle tier, like manager classes, and you use the adapter pattern, you can wrap and expose your services using other techniques such as web services and remoting and whatever else comes down the pipe (indigo, windows workflow).

I beleive that to say hands down that all things asp.net should be XML DataSets is wrong.

Here is what I have found,

  • If I need to communicate across process boundaries, all components are on the same network, and all components are .NET I use remoting, and I expose my services layer using interfaces
  • If I need to communicate with other non-managed applications in a message based way, I use SOAP messages. I reserve xml datasets for the simple operations and the prototypes, because lets face it, sending a dataset with any amount of data over the wire is just a waste of bandwidth
  • If I dont need to accomodate point 1 or point 2, I make my application use the manager classes directly.

So to answer your question, by using the adapter pattern, you can already use LLBLGen to appease the listeners that only want to hear the buzz words, like web services and dataset.

I am just curious what you do about converting types though. In the adapter pattern, the target has a virtual function that the adapters override that. So say you are using webservices, and instead of returning an entity, you want to return an xml string (as suggested in the LLBLGen docs). Doing the override keeps the same return type in the funciton definition..

Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 28-Feb-2006 04:24:48   

I am not too sure that I follow your question, but here goes.....

Take the 2 methods below, the web method wraps the manager method:

Web Method:


        [WebMethod]
        public string GetAllEmployees(int countOfItemsToReturn, int pageSize, 
            int pageNumber)
        {
            EntityCollection tmpCol = EmployeeManager.GetAllEmployees(
                countOfItemsToReturn, pageSize, pageNumber);

            string result;
            tmpCol.WriteXml(XmlFormatAspect.Compact | 
                XmlFormatAspect.DatesInXmlDataType,
                "Employees", out result);
            return result;
        }

Manager Method


    public static EntityCollection GetAllEmployees(int maxItems, int pageNumber, 
        int pageSize)
    {
        EntityCollection col = new EntityCollection(new EmployeesEntityFactory());
        using (DataAccessAdapter adapter = new DataAccessAdapter())
        {
            adapter.FetchEntityCollection(col, null, maxItems, null, pageNumber, pageSize);
            return col;
        }
    }

Consumers consume the web service, they could care less about the internals. I manage the web service, so I can safely use LLBLGen entities in my own code. I have to make sure that any systems that interoperate with me can exchange data in a format that they understand.

When a consumer posts data to my service, I demand a schema that I can use to re-create or re-fetch LLBLGen entities.

You can also create classes that convert from LLBLGen entities to data sets, to soap messages, to XML, you could event use JSON C#, its really up to you. The point is that it can be done.

So the big question is "if we have to write a wrapper, why do we need to use LLBLGen and not native .NET technologies like DataAdapters, Stored Procs, and ADO.NET datasets?"

My response is that if the core system is LLBLGen, you can get your initial product out to market faster, and you can still support interop and almost any distrubuted technology that you want. All subsequent services will be based around the same core components thus making the code more maintainable.

I hope that answers your questions.

Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 28-Feb-2006 17:03:30   

Thanks for your input devildog, its good to see that someone else is doing stuff similar to what im doing simple_smile

im doing it slighlty different. I have llblgen generate simple c# objects based off the entities. Then using vs2005s partial classes i extend the llblgen entities to include casting operators so i can easily convert back and forth.

Generated casting operators:


        public static explicit operator AddressEntity(Address item)
        {
            AddressEntity entity = new AddressEntity();
        
            entity.SetNewFieldValue((int)AddressFieldIndex.Id, item.Id);
            entity.SetNewFieldValue((int)AddressFieldIndex.Street, item.Street);
            entity.SetNewFieldValue((int)AddressFieldIndex.City, item.City);
            entity.SetNewFieldValue((int)AddressFieldIndex.StateProvinceId, item.StateProvinceId);
            entity.SetNewFieldValue((int)AddressFieldIndex.CountryRegionCode, item.CountryRegionCode);
            entity.SetNewFieldValue((int)AddressFieldIndex.PostalCode, item.PostalCode);
            entity.SetNewFieldValue((int)AddressFieldIndex.CreatedBy, item.CreatedBy);
            entity.SetNewFieldValue((int)AddressFieldIndex.CreatedDate, item.CreatedDate);
            entity.SetNewFieldValue((int)AddressFieldIndex.ModifiedBy, item.ModifiedBy);
            entity.SetNewFieldValue((int)AddressFieldIndex.ModifiedDate, item.ModifiedDate);
            return  entity;
        }

        public static explicit operator Address(AddressEntity entity)
        {
            Address item = new Address(
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.Id) ? default(System.Guid) : entity.Id,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.Street) ? default(System.String) : entity.Street,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.City) ? default(System.String) : entity.City,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.StateProvinceId) ? default(System.Guid) : entity.StateProvinceId,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.CountryRegionCode) ? default(System.String) : entity.CountryRegionCode,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.PostalCode) ? default(System.String) : entity.PostalCode,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.CreatedBy) ? default(System.Guid?) : entity.CreatedBy,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.CreatedDate) ? default(System.DateTime?) : entity.CreatedDate,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.ModifiedBy) ? default(System.Guid?) : entity.ModifiedBy,
                entity.TestCurrentFieldValueForNull(AddressFieldIndex.ModifiedDate) ? default(System.DateTime?) : entity.ModifiedDate
                );
            return item;
        }

        public static implicit operator Address[](EntityCollection collection)
        {
            Address[] list = new Address[ collection.Count ];
            for (int x = 0; x < collection.Count; x++)
            {
                list[ x ] = (Address)((AddressEntity)collection[ x ]);
            }
            return list;
        }

        public static implicit operator List<Address>(EntityCollection collection)
        {
            List<Address> list = new List<Address>();
            foreach (AddressEntity entity in collection)
            {
                list.Add((Address)entity);
            }
            return list;
        }
        
        public static implicit operator EntityCollection(Address[] items)
        {
            EntityCollection list = new EntityCollection(new AddressEntityFactory());
            foreach (Address item in items)
            {
                list.Add((AddressEntity) item);
            }
            return list;
        }

        public static implicit operator EntityCollection(List<Address> items)
        {
            EntityCollection list = new EntityCollection(new AddressEntityFactory());
            foreach (Address item in items)
            {
                list.Add((AddressEntity) item);
            }
            return list;
        }

This of course allows me to write code liek this


Address address = new Address(); //Address type is a POCO object generated by llblgen.
AddressEntity entity = (AddressEntity)address; //Address entity is of course llblgen entity.

Skeeterbug
User
Posts: 165
Joined: 21-May-2004
# Posted on: 28-Feb-2006 17:47:30   

Devildog74 wrote:

I am not too sure that I follow your question, but here goes.....

Take the 2 methods below, the web method wraps the manager method:

Web Method:


        [WebMethod]
        public string GetAllEmployees(int countOfItemsToReturn, int pageSize, 
            int pageNumber)
        {
            EntityCollection tmpCol = EmployeeManager.GetAllEmployees(
                countOfItemsToReturn, pageSize, pageNumber);

            string result;
            tmpCol.WriteXml(XmlFormatAspect.Compact | 
                XmlFormatAspect.DatesInXmlDataType,
                "Employees", out result);
            return result;
        }

Manager Method


    public static EntityCollection GetAllEmployees(int maxItems, int pageNumber, 
        int pageSize)
    {
        EntityCollection col = new EntityCollection(new EmployeesEntityFactory());
        using (DataAccessAdapter adapter = new DataAccessAdapter())
        {
            adapter.FetchEntityCollection(col, null, maxItems, null, pageNumber, pageSize);
            return col;
        }
    }

Consumers consume the web service, they could care less about the internals. I manage the web service, so I can safely use LLBLGen entities in my own code. I have to make sure that any systems that interoperate with me can exchange data in a format that they understand.

When a consumer posts data to my service, I demand a schema that I can use to re-create or re-fetch LLBLGen entities.

You can also create classes that convert from LLBLGen entities to data sets, to soap messages, to XML, you could event use JSON C#, its really up to you. The point is that it can be done.

So the big question is "if we have to write a wrapper, why do we need to use LLBLGen and not native .NET technologies like DataAdapters, Stored Procs, and ADO.NET datasets?"

My response is that if the core system is LLBLGen, you can get your initial product out to market faster, and you can still support interop and almost any distrubuted technology that you want. All subsequent services will be based around the same core components thus making the code more maintainable.

I hope that answers your questions.

Thanks for the explanation. simple_smile