Showing models some love

Posts   
 
    
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 25-Mar-2010 03:29:36   

I have a pattern that I would like to implement. Currently I have implemented it using LLBLGen but with one major caveat and a few minor ones.

The issue is to do with mapping from my entity objects to my model objects. Heres an example of what I want to achieve:

    
public IQueryable<Office> Find()
        {
            LinqMetaData meta = new LinqMetaData(Adapter);

            return (from c in meta.Office
                          select AutoMapper.Mapper.Map<OfficeEntity, Office>(c));
        }

This part would work in LLBL if i took Automapper out and manually mapped from my entity to my model class. Automapper issue aside (this is needed as I don't want to be constantly mapping for all my dataaccess objects for each and every query) (Frans has already told me this might work in v3).

However the behaviour I get with LLBL is if I apply any further modifications to the return IQueryable they are applied in memory. See this example of calling this method, modifyign the result and then enumerating it. (i havent actually compiled any of these examples so might be a few silly mistakes)


var offices = officeService.Find();

offices = offices.Where(p => p.PostalAddress.Contains("melbourne")).OrderBy(p => p.OfficeNo);

var foo = offices.ToList();

If I were still working with OfficeEntity instead of Office the ToList call would execute a SQL statement with the ORDER BY and WHERE in it. But because I have have mapped from the original entity what ends up happening is the inital query is executed (a vanilla select statement) and then the orderby and where are implemented in memory. Which is really bad for obvious reasons.

Now my initial mapping is just a series of fields from the original entity. I can see the issue though, it is difficult for the "system" to know that Order.PostalAddress is actually OfficeEntity.PostalAddress. What it really needs to do is internally treat Office.PostalAddress like a Func<string, OfficeEntity> (p=>p.PostalAddress) (or maybe I mean an Expression) that it can reference and map to the PostalAddress address column of the Office entity. It could also handle more complex mappings this way (like (p=>p.PostalAddress + " - " + p.OfficeNo) to SELECT PostalAddress + ' - ' + OfficeNo [PostalAddress]) this way.

Without this I am finding Models a bit of a pain. I don't want to reference anywhere the entity classes from my DomainService consuming code... I kind of feel like I'm asking Santa Claus for a Ferrari but I'm asking anyway smile

Can live without the AutoMapper compatibility but it sure would be nice to have.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 25-Mar-2010 05:50:21   

Why are you using Automapper in the first place? Shouldn't be your *Entity objects your business facade, what you use all along? Anyway, in your case, should be wise to fetch first and then map to the new objects, a separate layer. Your manager classes should fetch and then translate the result. That's my point of view. maybe I don't understand 100% your scenario.

David Elizondo | LLBLGen Support Team
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 25-Mar-2010 06:09:44   

I'm using Automapper because with MVC model binding against entity classes just doesn't work. Too hard. And the entity classes seldom respresent a 1:1 mapping with how my UI is presented and the fields in the UI.

Also I don't know if EntityCollection is ModelBinder compatible. I doubt it is.

The automapper part of my question is a low priority. Mapping to Models is the important part, be it manually or via Automapper.

If I have a method on my domain service that returns models in a specific order but for whatever reason 1 or several use cases of that method want the models in a different order I don't want to have to write another method on my domain service that is identical to other except it returns in a different order. I just want to order the result (efficiently - via SQL - not in memory).

A controller managing paging/ordering of a data set is a good example. The domain service accepts arguments and returns a filtered result. However my controller is the point that I dictate the ordering of the result and which page to get.

I could put a paging aware method on my domain service... but do u think that's the place for it? How would I specify the order? Magic strings? I can't pass in a Func<> as it would need a type reference to the entity - if I use a Func<> against the Model type I am stuck with the the same issue of the OrderBy being applied to the result as a result of models and not entities.

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 25-Mar-2010 11:02:29   

How would I specify the order? Magic strings? I can't pass in a Func<> as it would need a type reference to the entity - if I use a Func<> against the Model type I am stuck with the the same issue of the OrderBy being applied to the result as a result of models and not entities.

You can pass FieldIndexEnums to your Service method. And in there you can get the entityField to use in the SortExpression as follows:

(EntityField2)EntityFieldFactory.Create(DocumentFieldIndex.CreateDate);

worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 26-Mar-2010 08:32:14   

I'm looking into creating a wrapper that will intercept expressions like p=>p.Number == 1 (where method calls) and modify the expression by mapping the model property to the entity property and applying it to the Entity IQueryable.

Expressions are doing my head in but I am making some progress. Successfully swapper the type around in an expression today in my sandbox app.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 26-Mar-2010 16:52:46   

(Frans has already told me this might work in v3).

Wait... where? I'm not aware I said anything in this light, because I have never worked with automapper... ? Could you provide a link please?

I think what you want is not doable: you want to write a query against in-memory types which are mapped using auto mapper and that query is then translated into a query using entities by converting the mappings in automapper ? I don't think that's possible, you have to convert the expression tree so automapped types (in-memory types) are converted to entity types before it reaches our linq provider.

Frans Bouma | Lead developer LLBLGen Pro
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 29-Mar-2010 00:52:43   

Yes I'm finding it quite impossible so far smile

Maintaining a delegate to select the fields to create the Model doesn't seem that hard. But magically transforming references to Model.XX to Entity.XX is proving troublesome. The main problem being recognising the reference.

you have to convert the expression tree so automapped types (in-memory types) are converted to entity types before it reaches our linq provider

Yes that's exactly what I am trying to do. It's no so much a conversion, just a replacement.

In simple scenarios I have it working. Eg. XX.Name == "Sam" I can change the type of the ParameterExpression as well as recreate the MemberAccessExpression pointing to a different PropertyInfo. It's all based on a dictionary of PropertyInfo's though which makes it very limiting.

Once you try something like XX.Name + "s" == "Sams" it all falls down. It really needs to scan the expression for a particular memberaccessexpression and replace it. That in itself seems quite easy using the "visitor" code i got from MSDN, but the method of defining the expression to search for as well as getting a consistent hashcode (or method of comparing the expressions as identical) continues to illude me.

Once again, the automapper part of this question was more ancillary than primary. The main goal being able to work on IQuerable<Model> instances and have alterations like Where, OrderBy be performed at the database level as opposed to in memory. The how being not that important to me.

The problem is that the linq provider doesn't support a projection to a class type (anonymous or custom) which receives an entity typed variable, because the entity instance you pass to the element you return isn't instantiated and won't be instantiated as well.

This is something we hope to fix in v3.0 (it's on the list of issues to fix before RTM), but it's likely a difficult task as our framework uses 2 paths to fetch data: one for entity instances and one for all other projections. This is because entity projections support inheritance and custom projections are as-is.

You run into this particular scenario. It's the same as: var q = from c in metadata.Customer select new { Entity = c};

I don't think the context of your statement is Automapper specific, rather I think your referring to creating the instance in a way other than the usual inline - new X { Y = z.C } approach. Sorry if I've misquoted...

Could you provide a link please?

http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=17364

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 06-Apr-2010 10:22:42   

The object-object mapping consumption to linq query conversion is not something I see become a reality. It's simply too difficult.

The issue you quoted is about returning an entity instance in a projection. The problem we currently have is that we use two pipelines separately, and the projection requires to run one after the other. It's unclear if we can make that happen before 3.0 RTM, as it requires runtime lib changes and we don't want to make them at this point as we need to ship 3.0 (otherwise it will take forever, as there is always something else to change/fix as well). So it will likely be postponed till we make the list of changes to the runtime lib. We in general try to keep focus on 1 part of the system in every release to make conversion to the new release as easy as possible. This time it's the designer, next time it's likely the runtime.

Frans Bouma | Lead developer LLBLGen Pro