Thursday, September 27, 2007

Mike Griffin, creator of MyGeneration and EntitySpaces, talks with Carl and Richard about EntitySpaces, a persistence layer and business object system for the Microsoft .NET 2.0 Framework, as well as his experiences with LINQ and other technologies.


Mike Griffin co-authored and founded MyGeneration Software with Justin Greenwood in late 2003 to early 2004. MyGeneration is now right at the top in CNET download.com’s .NET Utility category.


But, it wasn’t always that way. MyGeneration opened with a meager offering that supported SQL Server and Oracle, and only supported VBScript and Jscript. Since then, it has grown to support 13 databases, added support for both C# and VB.NET, has a huge online community sharing templates via the MyGeneration Online Template library, and has been featured in several magazines. The MyGeneration Source code has now been released on SourceForge.NET under the BSD license.


Mike also created dOOdads, a .NET architecture that became very popular and helped fuel the desire for MyGeneration. Mike created the MyMeta metadata engine for MyGeneration and there is no better metadata engine for the Microsoft .NET Framework.


In 2005, Mike started working on a new architecture, one that would be sold as a commercial offering and take advantage of the features in .NET 2.0, and thus EntitySpaces was born. Creating a new offering that would be commercial was more than a one person project, so Mike set out to find some key resources and he found them.


Mike has proven that he can build communities around products and energize others to rally around them. Mike is a senior architect with Leaf Software Solutions.


Show #276 | 9/27/2007 (67 minutes)

 

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net

posted on Thursday, September 27, 2007 6:41:56 AM (Eastern Standard Time, UTC-05:00)  #   
 Wednesday, September 26, 2007

On October 13th in Indianapolis, Indiana I'll be in the TridgeAlliance booth handing out EntitySpaces glossies and talking to people about EntitySpaces. I might be giving a "chalk talk" or a "Lunch & Learn Session" on EntitySpaces as well, I'm not sure on that yet. Next year for sure we will have our own booth. Anyway, if you're near Indy you might want to check this out, registration is filling up, there are already near 400 registrants. 

IndyTechFest
http://www.indytechfest.com/

If you're there you can find me in the TridgeAlliance booth.

EntitySpaces

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net

posted on Wednesday, September 26, 2007 6:29:25 PM (Eastern Standard Time, UTC-05:00)  #   
 Wednesday, September 19, 2007

I just had to make another post, this is so much fun. Writing queries in our enhanced dynamic query language is totally awesome, and so powerful. I'm writing some unit tests to go into our massive NUnit test suite and just had to post these ...

public void JoinWithArithmeticExpressionOrderByCalulatedColumn()
{
    // Notice I create a calulated columns based on the TotalSales, then Order by it descending
    CustomerQuery cust = new CustomerQuery("c");
    OrderQuery order = new OrderQuery("o");
    OrderItemQuery item = new OrderItemQuery("oi");

    cust.Select(cust.CustomerName, (item.Quantity * item.UnitPrice).Sum().As("TotalSales"));
    cust.InnerJoin(order).On(order.CustID == cust.CustomerID);
    cust.InnerJoin(item).On(item.OrderID == order.OrderID);
    cust.GroupBy(cust.CustomerName);
    cust.OrderBy("TotalSales", esOrderByDirection.Descending);

    CustomerCollection coll = new CustomerCollection();
    coll.Load(cust);
}

The SQL produced:

SELECT c.[CustomerName],SUM(oi.[Quantity]) AS 'TotalSales'
FROM [ForeignKeyTest].[dbo].[Customer] c
JOIN [ForeignKeyTest].[dbo].[Order] o ON (o.[CustID] = c.[CustomerID])
JOIN [ForeignKeyTest].[dbo].[OrderItem] oi ON (oi.[OrderID] = o.[OrderID])
GROUP BY c.[CustomerName]
ORDER BY TotalSales DESC

The result set is CustomerName / TotalSales sorted in decending order by TotalSales ...

Another query I wrote merely concatenated the Employee FirstName and LastName columns, and upper cased them.

public void ArithmeticConcatenationNoJoinWithSubOperator()
{
    EmployeeCollection coll = new EmployeeCollection();
    EmployeeQuery q = coll.Query;

    q.Select( (q.LastName + "," + q.FirstName).ToUpper().As("FullName") );
    q.OrderBy(q.LastName.Ascending);

    coll.Query.Load();
}

I've seen many of our competitors query languages, I think by far EntitySpaces has the cleanest, most straight-forward syntax, by light years in most cases. We've got some really cool stuff coming, hang on ...

EntitySpaces

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net


kick it on DotNetKicks.com
posted on Wednesday, September 19, 2007 7:47:35 PM (Eastern Standard Time, UTC-05:00)  #   
 Tuesday, September 18, 2007

The EntitySpaces Dynamic Query API will soon be sporting Join’s and Arithmetic Expressions. The syntax is so elegant even LINQ enthusiasts will take pause. Let’s take a look at how you would do a join using the enhanced EntitySpaces DynamicQuery. Note that we are using all of your existing objects to build the query.

Performing Joins …

CustomersQuery cust = new CustomersQuery ("c");
OrdersQuery orders = new OrdersQuery ("o");
OrderDetailsQuery details = new OrderDetailsQuery ("d");

cust.Select(cust.ContactName, details.Quantity.Sum().As("TotalQuantity"));
cust.InnerJoin(orders).On(cust.CustomerID == orders.CustomerID);
cust.InnerJoin(details).On(orders.OrderID == details.OrderID);
cust.Where(cust.ContactName.Like("%Mike%"));
cust.GroupBy(cust.ContactName);

CustomersCollection coll = new CustomersCollection ();
coll.Load(cust);    // Load it …

Now that is a pretty sweet syntax …

Of course, RightJoin, LeftJoin, and FullJoin are also supported. The nice thing about this approach is that you are spoonfed the syntax via intellisense, no need to stop and create a view (although you can if you want to and generate your business entities off of the view). The "o", "c" and "c" shown above are merely the aliases used when building the SQL.

Arithmetic Expressions

You can now use arithmetic expressions in your query's. Notice how you can use the natural language syntax with * / + - and %. Take a look at this sample …

CustomersQuery cust = new CustomersQuery ("c");
OrdersQuery orders = new OrdersQuery ("o");
OrderDetailsQuery details = new OrderDetailsQuery ("d");

cust.Select(cust.ContactName, (details.Quantity * details.Price).Sum().As("TotalPrice"));
cust.InnerJoin(orders).On(cust.CustomerID == orders.CustomerID);
cust.InnerJoin(details).On(orders.OrderID == details.OrderID);
cust.Where(cust.ContactName.Like("%Mike%"));

CustomersCollection coll = new CustomersCollection ();
coll.Load(cust);    // Load it …

How Does this Effect Binding?

Your EntitySpaces collections will now provide a new method named LowLevelBind(). Normally, you bind directly to the properties in your EntitySpaces entities. However, when joins are in play you are bringing back columns that aren’t in your entities. Thus, the LowLevelBind will bind directly to the underlying DataTable. The are other ways of course using EntitySpaces but we wont cover those in this post.

grid.DataSource = coll.LowLevelBind();

Can I still Save an Entity that was Built off a Join?

The answer is "yes". Of course, it will only save to the main table, in the above case the Customer table.
We already have this implemented and are hoping to have a beta out with joins and arithmetic expressions around October 1st, 2007.

These new features will be available for all of the many databases supported by EntitySpaces.

EntitySpaces

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net

posted on Tuesday, September 18, 2007 11:22:28 AM (Eastern Standard Time, UTC-05:00)  #   
 Friday, September 14, 2007

Our fall ES2007 release is slotted to add these new or enhanced features.

  • ASPX Templates (enhanced not new)
  • Constructors in your Custom Classes
  • Hierarchical DataBinding
  • Hierarchical Serialization
  • Cascading Deletes (Hierarchical)
  • New Stored Procedure Template - (generates invoking code doesn't create objects)
  • Ability to Save without transactions (useful when importing)
  • Many other items from our tracking system

Our first ES2007 fall beta is slotted for October 15th, 2007. Our ES2008 Q1 roadmap will be published soon as well.

EntitySpaces

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net

posted on Friday, September 14, 2007 8:04:22 AM (Eastern Standard Time, UTC-05:00)  #   
 Thursday, August 30, 2007

Well, the Rodney Dangerfield of software engineering is actually getting some recognition, although I'm not an MVP I do play one on the Internet ...

I've been asked by the .NET Rocks guys to do a show on MyGeneration and EntitySpaces and I am pretty excited about it. There's a lot to talk about. I'm not sure how much time I'll have but I'll try to relay our vision for EntitySpaces 2008 as well. Hopefully you will gain some insight as to how Scott, David, and myself operate and how EntitySpaces runs behind the scenes.

We'll be recording the the show on Tuesday, September 4th and its scheduled for publication on September 11. We will post again and let you know the show is up and online ...


 
The Internet Talk Show for Developers ...
.NET Rocks!

 

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net


kick it on DotNetKicks.com
posted on Thursday, August 30, 2007 7:58:57 PM (Eastern Standard Time, UTC-05:00)  #   
 Tuesday, August 21, 2007

The EntitySpaces persistence layer and business object architecture for Microsoft .NET now fully supports PostgreSQL 8.2. Using PostgreSQL on .NET has never been easier.

The EntitySpaces 2007 v0819 (beta) for PostgreSQL successfully passes our massive NUnit test suite including both the stored procedure and dynamic SQL modes. There is a new template that will generate your stored procedures if you desire to use stored procedures with your EntitySpaces PostgreSQL implementation.

See the Release Notes for more information on setup. You'll need the Npgsql .NET provider for PostgreSQL and MyGeneration. See our Home Page and grab the latest Trial version. It has the PostgreSQL support.

EntitySpaces

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net

posted on Tuesday, August 21, 2007 10:34:09 PM (Eastern Standard Time, UTC-05:00)  #   
 Saturday, July 28, 2007
We have written a webservices example using three different forms of the same service. These webservices allow you to retrieve and update data and are built upon the Microsoft SQL Northwind Employees table using EntitySpaces 2007.0.730.0 maintenance release which will be published this weekend (July 28th, 2007).  You can download both the WSConsumer application and the webservices (including source code) at the end of this article. The webservice is included so you can the see the source and run it locally if desired. The WSConsumer is setup to hit the services listed below on our site.

    Xml - http://www.entityspaces.net/webservices/csharp/ES_Xml.asmx

    XmlProxy - http://www.entityspaces.net/webservices/csharp/ES_XmlProxy.asmx

    Binary - http://www.entityspaces.net/webservices/csharp/ES_Binary.asmx

 

The Xml Webservice

The XML webservice is a basic SOAP example. It simply returns the Employees entity or the EmployeesCollection and everything is serialized accordingly. This type of webservice, the more traditional, doesn't require the webservice consumer to have any knowledge or link to any of your code.

[WebMethod]
public Employees GetEmployee(int i)
{
    Employees emp = new Employees();
    if (emp.LoadByPrimaryKey(i))
        return emp;
    else
        return null;
}

[WebMethod]
public EmployeesCollection GetAllEmployees()
{
    EmployeesCollection emps = new EmployeesCollection();
    if (emps.LoadAll())
        return emps;
    else
        return null;
}

Most people are familiar with traditional webservices. If you are interested in saving data through your webservice all the necessary code is in the demo app. However, security issues when allowing users to update data are not covered, those are left up to the developer to consider.

The XmlProxy Webservice (and WCF)

This is by far the most interesting and powerful example. The EntitySpaces proxy classes can be used in webservice scenarios in which you have control over both the webservice and the consumer as this example requires the proxy to be on both sides of the equation. Additionally, the EntitySpaces proxy classes are perfect for use as your WCF wire format. When we generated the proxy classes for the demo we did not check the "Support WCF" checkbox. We only generated the proxy portion which can be used on it's own without WCF. Some of the features of the proxy classes are follows:

  • Perfect for WCF communications.
  • Very easy to use wrappers
  • Very lightweight format which can serialize only the dirty rows and dirty columns.
  • Manages Added, Modified, and Deleted state for you.
  • Transparent Save logic.

If you're using EntitySpaces in WCF scenarios you can glean a lot from this section, it's shows exactly how your WCF packets will look. In this sample we need to manually serialize them into string form but in WCF scenarios you would just return the actual proxy's in your methods and the serialization is done for you. Take a look at our GetEmployee() method and notice the serialization so that we can return a raw string. Notice how we use the Proxy to wrap the real Employees object.

[WebMethod]
public string GetEmployee(int i)
{
    Employees emp = new Employees();
    if (emp.LoadByPrimaryKey(i))
    {
        // The Generated Proxy class
        EmployeesProxyStub proxy = new EmployeesProxyStub(emp);

        // Manually Serialize into string form (we want to deserialize it on the other side)
        XmlSerializer sf = new XmlSerializer(typeof(EmployeesProxyStub));
        StringWriter sw = new StringWriter();
        sf.Serialize(sw, proxy);

        return sw.ToString();
    }
    else
        return null;
}

 

The output of our call to the GetEmployee() method above can be seen below.

<?xml version="1.0" encoding="utf-16"?>
    <EmployeesProxyStub xmlns:xsi="
" xmlns:xsd="">
    <EmployeeID>1</EmployeeID>
    <LastName>Davolio</LastName>
    <FirstName>Jimmy</FirstName>
    <Title>Sales Representative</Title>
    <TitleOfCourtesy>Ms.</TitleOfCourtesy>
    <BirthDate>1948-12-08T00:00:00</BirthDate>
    <HireDate>1992-05-01T00:00:00</HireDate>
    <Address>507 - 20th Ave. E.Apt. 2A</Address>
    <City>Seattlesss</City>
    <Region>WA</Region>
    <PostalCode>78890</PostalCode>
    <Country>USA</Country>
    <HomePhone>(206) 555-9857</HomePhone>
    <Extension>989</Extension>
    <Notes>Some notes by Rick</Notes>
    <ReportsTo>161</ReportsTo>
   
<esRowState>Unchanged</esRowState>
</EmployeesProxyStub>

 

Notice the esRowState is "Unchanged". Now let's deserialize this in our client (in the demo the client is called WSConsumer). The code necessary to do this (taken from the WSConsumer application) is shown below.

// Call the XmlProxy Service
string data = wsProxyService.GetEmployee(1);

XmlSerializer xs = new XmlSerializer(typeof(BusinessObjects.EmployeesProxyStub));
StringReader sr = new StringReader(data);

// Deserialize into our Proxy
BusinessObjects.EmployeesProxyStub proxy =
xs.Deserialize(sr) as BusinessObjects.EmployeesProxyStub;

// Ask the proxy for the true Employees object
Employees emp = proxy.Entity;

Now let's modify the "emp" object we just deserialized and save it back through our service

emp.FirstName = "Joe";
emp.LastName = " EntitySpaces";

// True here means dirty columns only (primary keys are of course always sent)
BusinessObjects.EmployeesProxyStub proxy =
    new BusinessObjects.EmployeesProxyStub(this.emp, true);

XmlSerializer sf = new XmlSerializer(typeof(BusinessObjects.EmployeesProxyStub));
StringWriter sw = new StringWriter();
sf.Serialize(sw, proxy);

string packet = sw.ToString();

wsProxyService.SaveEmployee(packet);

Let's take a look at the packet sent back from the WSConsumer application to the webservice that was generated from the above code.

<?xml version="1.0" encoding="utf-16"?>
    <EmployeesProxyStub xmlns:xsi="
" xmlns:xsd="">
    <EmployeeID>1</EmployeeID>
    <LastName>EntitySpaces</LastName>
    <FirstName>Joe</FirstName> 
    <esRowState>Modified</esRowState>
</EmployeesProxyStub>

Notice that only the dirty columns are sent back and the esRowState is set to "Modified". We're almost at the end of this sequence, let's see how our XmlProxy webservice implements the SaveEmployee method.

[WebMethod]
public void SaveEmployee(string data)
{

    // Deserialize into our Proxy
    XmlSerializer xs = new XmlSerializer(typeof(EmployeesProxyStub));
    StringReader sr = new StringReader(data);

    EmployeesProxyStub proxy = xs.Deserialize(sr) as EmployeesProxyStub;

    // Ask for the real Employee entity
    Employees emp = proxy.entity;
    if (emp != null)
    {
        // Simply Save it, the added, modified, deleted state is 100% managed by the proxy
        emp.Save();
    }
}

Could it be any easier? Although not shown here (but demonstrated in the WSConsumer application) you can modify and transmit entire collections just as easily. The XmlProxy approach is the best way to go in all scenarios in which you are in control of both ends of the equation. Nothing could be easier or faster.

The Binary Webservice

The binary webservice approach can also be used in two way communication scenario's and while it is very easy to use, like the XmlProxy approach, it is far more bulky and bloated, however, the data is somewhat more secure in that it is not human readable.

[WebMethod]
public byte[] GetEmployee(int id)
{
    Employees obj = new Employees();
    if (obj.LoadByPrimaryKey(id))
    {
        BinaryFormatter bf = new BinaryFormatter();
        MemoryStream ms = new MemoryStream();
        bf.Serialize(ms, obj);

       return ms.ToArray();
    }
    else
        return null;
}

The packet transmitted will look something like this, only much larger.

AAEAAAD/////AQAAAAAAAAAMAgAAAD1CdXNPYmosIFZlcnNpb249MS4wLjAuMCwgQ3
VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsDAMAAABZRW50aXR5U3BhY2
VzLkNvcmUsIFZlcnNpb249MjAwNy4wLjczMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1Ymxp
Y0tleVRva2VuPTFjMjY4MTk5ZmIyMWJmZTgMBAAAAElTeXN0ZW0sIFZlcnNpb249Mi4wLjA
uMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5D
AUAAABOU3lzdGVtLkRhdGEsIFZlcnNpb249Mi4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQ
dWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAZQnVzaW5lc3NPYmplY3RzLkV
tcGxveWVlcw8AAAAfX0VtcGxveWVlc0NvbGxlY3Rpb25CeVJlcG9ydHNUbxpfVXBUb1RlcnJp
dG9yaWVzQ29sbGVjdGlvbh5fRW1wbG95ZWVUZXJyaXRvcmllc0NvbGxlY3Rpb24qX0VtcGxv
eWVlVGVycml0b3JpZXNDb2xsZWN0aW9uQnlFbXBsb3llZUlEHV9PcmRlcnNDb2xsZWN0aW9
uQnlFbXBsb3llZUlEBXF1ZXJ5GV9VcFRvRW1wbG95ZWVzQnlSZXBvcnRzVG8PbW9kaWZpZ
WRDb2x1bW5zA2Zrcxtlc0VtcGxveWVlcytQcm9wZXJ0eUNoY

And that is not all that is transmitted for a just a single employee object. The reason this is so large is that everything is serialized in binary serialization, the underlying DataTable, it's columns, rows, everything.

 

Summary

We encourage you to download and run our WebServices demo, it comes with the v2007.0730 Trial assemblies necessary to run the WSConsumer against our hosted webservices, and shows you the packets as they go back and forth (except in the case of the Xml service, there is no easy way to intercept the soap packets). There re some big improvements (though minor changes in code) in the v2007.0730 release that greatly improve the EntitySpaces proxy classes.

You can download the full source to both the WSConsumer and Webservices here ==> DOWNLOAD

NOTE: There is an enforced two second delay between requests to cut down on the traffic for these demo services.

EntitySpaces

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net

posted on Saturday, July 28, 2007 11:08:59 AM (Eastern Standard Time, UTC-05:00)  #