I keep getting a lot of questions about whether DLinq is for data objects or for business objects. There is no shortage of advice – both good and bad about what one should and should not do with DLinq.
Here, I want to briefly describe what we had in mind. It might not match someone’s specific ideas about the terms “data objects” or “business objects” so hold your flame and send your feedback.
Here is how we approached the design:
- An entity maps to a single table or view and closely matches it in shape. Of course, you can project, change types, add non-persistent members, use inheritance but still, the entity is not a denormalized view of database entities. This helps us keep the updatability story simple. You don’t have to be a rocket scientist or computer scientist for that matter to prove that the set of operations is fully contained within the ….
- For queries – LINQ provides punch through many traditional layers on mid-tier – your where clause on a collection can be sent to the server for execution directly. In this respect this is not like doing data access with a handful of sprocs (even though we try our best to support sproc-only access). If you don’t need layers upon layers, don’t add them. If your business entities are close enough to database tables/views, just add the logic in partial classes and minimize layers – avoid overengineering on speculation that some day, somewhere, somehow you might need the flexibility. When you actually need something, chances are that the overdesign really doesn’t match the need.
- If you are doing significant denormalization and/or aggregation in a business object, LINQ provides enough capability to shape your objects programmatically. Of course, the results may not be updatable but that is a hard problem that is best solved based on knowledge of the domain – not blindly through another programming language in the disguise of mapping.
- Entities are not loaded with database-specific methods (see this previous post).
- Entities are not loaded with persistence-infrastructure like original state. This keeps the objects fairly simple. You can start POCO and make an explicit decision to buy into change notification if you like. Plus, you don’t have to worry about how to represent the state for transactional semantics when you have deleted the object. You don’t have to worry about synching foreign keys with CLR references.
- Code generation is your friend and yet it is optional. You can write and map your own classes if you have are allergic to code generators. If you are not allergic and practical, we have done lots of things to make your life easy with options to change generated code. Partial classes, partial methods etc. offer plenty of customization points.Again, we are targeting the common case and may not have a solution for every single case.
These choices may not solve every object persistence problem and satisfy every taste. They were not meant to. We wanted something that made sense to a general .NET developer who is not steeped in ORM esoterica and expects something that is dependable and easily understandable. There were many times when we discarded flexible designs that had too many nuances and required too much expertise to clearly understand. We believe that the average .NET developer is trying to solve a business problem, not write another persistence framework on top. We wanted the existing intuition about CLR data model and CLR objects to be your friend and worked on minimizing additional concepts. As a result, there may not be lots of ways to do one thing. In some cases, there may not be any direct way to do exactly what you want to do. That, is “by design”.
Some day, I hope to write more about the approach of C# design meeting (chaired by Anders Hejlsberg and attended regularly by colleagues like Matt Warren, Erik Meijer, Mads Torgersen and Cyrus Najmabadi). That is where LINQ and DLinq were cooked. But I must stop here – I have to run to catch a flight.