Automapping Linq queries

Posts   
 
    
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 07-Feb-2010 09:58:17   

Hello,

i'm jumping on the Model bandwagon and there is something in particular I am trying to achieve. I'd to be to leverage AutoMapper within my linq query.

For example I'd like to change this bit of code:


public IQueryable<InstituteFollowUp> FindPendingFollowUps(int assigneeId)
        {
            LinqMetaData meta = new LinqMetaData(Adapter);

            var followUps = from c in meta.InstituteFollowUp
                            where c.IsComplete == false && c.FollowUpAssigneeId == assigneeId
                            select new InstituteFollowUp()
                            {
                                InstituteId = c.InstituteUsingInstituteId.Id,
                                Institute = c.InstituteUsingInstituteId.Name,
                                Note = c.Note,
                                FollowUpDate = c.FollowUpDate,
                                FollowUpId = c.Id
                            };

            return followUps;
        }

to (something close to)


public IQueryable<InstituteFollowUp> FindPendingFollowUps(int assigneeId)
        {
            LinqMetaData meta = new LinqMetaData(Adapter);

            var followUps = from c in meta.InstituteFollowUp
                            where c.IsComplete == false && c.FollowUpAssigneeId == assigneeId
                            select AutoMapper.Mapper.Map<IAEC.SMS.Core.EntityClasses.InstituteFollowUpEntity, InstituteFollowUp>(c);

            return followUps;
        }

There being a number of benefits to this. It's brevity and it allows me to not have to keep defining how an entity is mapped to my model. Also, I would assume it would only select the columns that are mapped.

Heres the mapping:


AutoMapper.Mapper.CreateMap<IAEC.SMS.Core.EntityClasses.InstituteFollowUpEntity, InstituteFollowUp>().ForMember(
                p => p.FollowUpId, p => p.MapFrom(x => x.Id));

But that doesn't work, I get an error saying that XEntity cannot be cast to System.Int32. Which is a bit odd... Is there a way to make this work?

I'm using llbl 2.6.

Stacktrace:

[InvalidCastException: Unable to cast object of type 'System.Int32' to type 'IAEC.SMS.Core.EntityClasses.InstituteFollowUpEntity'.] lambda_method(ExecutionScope , Object[] , Int32[] ) +50 SD.LLBLGen.Pro.LinqSupportClasses.DataProjectorToObjectList1.AddRowToResults(IList projectors, Object[] rawProjectionResult) +104 SD.LLBLGen.Pro.LinqSupportClasses.DataProjectorToObjectList1.SD.LLBLGen.Pro.ORMSupportClasses.IGeneralDataProjector.AddProjectionResultToContainer(List1 valueProjectors, Object[] rawProjectionResult) +9 SD.LLBLGen.Pro.ORMSupportClasses.ProjectionUtils.FetchProjectionFromReader(List1 valueProjectors, IGeneralDataProjector projector, IDataReader datasource, Int32 maxNumberOfItemsToReturn, Int32 pageNumber, Int32 pageSize, Boolean clientSideLimitation, Boolean clientSideDistinctFiltering, Boolean clientSidePaging, UniqueList1 stringCache, Dictionary2 typeConvertersToRun) +1221 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List1 valueProjectors, IGeneralDataProjector projector, IRetrievalQuery queryToExecute, Dictionary2 typeConvertersToRun) +132 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List1 valueProjectors, IGeneralDataProjector projector, IEntityFields2 fields, IRelationPredicateBucket filter, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, IGroupByCollection groupByClause, Boolean allowDuplicates, Int32 pageNumber, Int32 pageSize) +245 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2.ExecuteValueListProjection(QueryExpression toExecute) +312 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression) +183 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression) +23 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression) +17 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.Execute() +16 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.System.Collections.Generic.IEnumerable&lt;T&gt;.GetEnumerator() +16 System.Linq.Buffer1..ctor(IEnumerable1 source) +247 System.Linq.&lt;GetEnumerator&gt;d__0.MoveNext() +108 System.Web.Script.Serialization.JavaScriptSerializer.SerializeEnumerable(IEnumerable enumerable, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +76 System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +1294 System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +175 System.Web.Script.Serialization.JavaScriptSerializer.SerializeCustomObject(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +566 System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +1338 System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +175 System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj, StringBuilder output, SerializationFormat serializationFormat) +24 System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj, SerializationFormat serializationFormat) +74 System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj) +6 System.Web.Mvc.JsonResult.ExecuteResult(ControllerContext context) +254 System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +10 System.Web.Mvc.&lt;&gt;c__DisplayClass11.&lt;InvokeActionResultWithFilters&gt;b__e() +20 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func1 continuation) +251 System.Web.Mvc.<>c__DisplayClass13.<InvokeActionResultWithFilters>b__10() +19 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func1 continuation) +251 System.Web.Mvc.&lt;&gt;c__DisplayClass13.&lt;InvokeActionResultWithFilters&gt;b__10() +19 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList1 filters, ActionResult actionResult) +178 System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +399 System.Web.Mvc.Controller.ExecuteCore() +126 System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +27 System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7 System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +151 System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +57 System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +7 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 07-Feb-2010 11:01:15   

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};

Frans Bouma | Lead developer LLBLGen Pro
vivek
User
Posts: 45
Joined: 06-Sep-2012
# Posted on: 12-Sep-2012 10:32:39   

Hi,

Any updates on this? Does not look like it's been fixed in v3.5 either.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 12-Sep-2012 13:00:22   

What have you tried? You can return entities in a custom projection now

Frans Bouma | Lead developer LLBLGen Pro
vivek
User
Posts: 45
Joined: 06-Sep-2012
# Posted on: 13-Sep-2012 02:09:49   

Otis wrote:

What have you tried? You can return entities in a custom projection now

i tried couple of things and both didn't work. not sure what you mean by custom projection..sorry

This is the first bit i tried

  Dim metaData As New LinqMetaData
    Dim q = From p In metaData.TblCostCentre _
                Select p
    Mapper.CreateMap(Of SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery(Of CostCentre), EntityClasses.TblCostCentreEntity)()
    Mapper.Map(Of SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery(Of CostCentre), EntityClasses.TblCostCentreEntity)(q)

    Return q

This is the second bit i tried

Public Function GetAllCostCentres() As IQueryable(Of Model.CostCentre) Implements ICostCentreRepository.GetAllCostCentres
    Mapper.CreateMap(Of TblCostCentreEntity, CostCentre)()
    Dim metaData As New LinqMetaData
    Dim q = From p In metaData.TblCostCentre _
                Select Mapper.Map(Of IQueryable(Of CostCentre), TblCostCentreEntity)(p)


    'Dim t As IQueryable(Of CostCentre) = Mapper.Map(Of CostCentre)(q)
    'Select New CostCentre With {.Active = p.Active, .CostCentre = p.CostCentre, .CreatedBy = p.CreatedBy, .DateCreated = p.DateCreated, .DateLastModified = p.DateLastModified, .ModifiedBy = p.ModifiedBy, .CostCentreID = p.CostCentreId}

    Return q
End Function

The last one throws me this error

Unable to cast object of type 'SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1[Mail.DAL.EntityClasses.TblCostCentreEntity]' to type 'System.Linq.IQueryable1[Mail.Model.CostCentre]'.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 13-Sep-2012 07:43:28   

You mapper looks like:

AutoMapper.Mapper.CreateMap<IAEC.SMS.Core.EntityClasses.InstituteFollowUpEntity, InstituteFollowUp>().ForMember(
                p => p.FollowUpId, p => p.MapFrom(x => x.Id));

... but then you are trying to map like this:

Select Mapper.Map(Of IQueryable(Of CostCentre), TblCostCentreEntity)(p)

In order to use your map you should materiailze the entities and use your mapping in the last projection (select) as you are doing already. See this snippet:

public class CustomerDto
{
    public string Id { get; set;}
    public string Name { get; set; }
    public string Other { get; set; }
}
...
Mapper.CreateMap<CustomerEntity, CustomerDto>().ForMember(
    p => p.Id, p => p.MapFrom(x => x.CustomerId));
...
var dtos = new List<CustomerDto>();
using (var adapter = new DataAccessAdapter())
{
    var metaData = new LinqMetaData(adapter);
    dtos = (from c in metaData.Customer
                select Mapper.Map<CustomerDto>(c)).ToList();
}

That is indeed a custom projection. Now in v3.5 you also can use QuerySpec's projections.

David Elizondo | LLBLGen Support Team
vivek
User
Posts: 45
Joined: 06-Sep-2012
# Posted on: 13-Sep-2012 07:53:10   

Thanks for that. but what if i want to return an Iqueryable object rather an instance of LLBLGenProQuery class? How can i do that or should i do that? I am using Repositorypattern and everywhere they are advising to return an iqueryable object to the View so we can query later if we need. Since i am using LLBLgenPro code (Self-Servicing) and LinqMetaData, it's returning an object of LLBLgenProQuery class and i am sort of confused that should i use LLBLgenproquery or should i use iqueryable?

I am sort of confused and guess in turn confusing my question.

What's the difference between an object returned by LLBLGenProQuery and IQueryable return by System.Linq?

Edit

Sorry guys. My mistake. this code works and returns an iqueryable object.

Public Function GetAllCostCentres() As IQueryable(Of Model.CostCentre) Implements ICostCentreRepository.GetAllCostCentres Mapper.CreateMap(Of TblCostCentreEntity, CostCentre)() Dim metaData As New LinqMetaData Dim q = From p In metaData.TblCostCentre _ Select Mapper.Map(Of CostCentre)(p) Return q End Function

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 20-Oct-2012 05:34:12   

ABOH wrote:

.

Hi Mike. I don't quite understand your question stuck_out_tongue_winking_eye I will close this for now, if you meant to ask something you can open a new thread wink

David Elizondo | LLBLGen Support Team