1

It happens so that the best practice of using EntityFramework includes taking advantage of the IQueryable inside using block, like:

using (var ctx = new SomeDbContext())
{
  ...(do something with ctx)
}

If we want to decouple database and business (and eventually presentation) tires, it involves creating other objects from EF context-originated objects. Automapper comes handy here. This means I can do something like this:

using (var ctx = new SomeDbContext())
{
    var dbHeaders = ctx.Headers.ToList();
    var headers = Mapper.Map<List<MyBusinessModel.Headers>>(dbHeaders);
    return headers;
}

Now it looks like the DB tire is coupled to business tire - doesn't it? List<MyBusinessModel.Headers> is fairly used inside EF-owned using block. However, if I wanted to do this...

List<Headers> dbHeaders = null;
using (var ctx = new SomeDbContext())
{
    dbHeaders = ctx.Headers.ToList();
}
...decoupled!...or?...
var headers = Mapper.Map<List<MyBusinessModel.Headers>>(dbHeaders);
return headers;

...it might work, but only if I don't map child entities (so called navigation properties). Why? When using block exits, context is dead and if Automapper needs lazy loaded children, they won't load and I will get an exception.

One posibility is using Include() that instructs EF to eagerly load stuff. But what if we have a complex object graph, where sometimes we work with one depths in the graph, and sometimes with others? Automapper is simply configured to always map the same way. This means that never mind the depth, we always have to use aAll the includes, like

ctx.Headers
   .Include("Items")
   .Include("Items.Details")
   .Include("Items.Details.MoreDetails")
   ...

Am I the only one who thinks this is ugly?

I decided to take a bold step beyond and try the following.

First the business tire...

List<MyBusinessModel.Headers> headers = null;
SomeDbTireWrapper.MapHeaders((dbHeaders) => {
  headers = Mapper.Map<List<MyBusinessModel.Headers>>(dbHeaders);
});

Then the database tire...

public static void MapHeaders(Action<List<Headers>> callbackHeaders)
{
  using (var ctx = new SomeDbContext())
  {
    var items = ctx.Headers.ToList();
    callbackHeaders(items);
  }
}

This seems to work, but I'm afraid it'll hit me upside the head somewhere along the path.

Does anyone see the problem with this approach?

  • What actual, real-world business-domain problem are you attempting to solve here? – Robert Harvey Sep 9 '16 at 22:12
1

A solution for the problem that you're facing is the Application Service Layer pattern. In Domain Driven Design parlance this is called the Anti-corruption Layer.

Assuming you are using a rich domain model, the Application Service Layer wraps around this model and maps between the objects and operations that are pertinent to your application and the rich domain model that your database supports. For example let's say your application is a customer facing portal for an e-commerce site. And for the sake of argument, let's say that your domain model on the backend has a very complex mapping for creating a customer, but you want to provide a simple method for your customer make an order, pay for it, and have it shipped to them.

For the scenario that you need on the front end, you might decide that a flat order information form would be optimal to get your customer efficiently through the checkout process. ecommerce screen

In the front end, you have one object (Customer) that represents their registration. On the backend, you're creating three objects to represent that customer. Here is a rough object diagram for you:

customer registration object diagram

As you see the Application Service Layer is responsible for translating between the two. It offers the front end application a simplified view of the domain model. The DM is free to evolve without impacting the UI and the Service Layer handles the translation between the two.

That being said, I've found that projecting from the context directly into my "Application Model" is more efficient (because it requires less round trips and less data retrieval than trying to use an automatic mapper for the purpose...Save auto mapper for simple, repetitive mappings.

Hope this helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.