Add DBFunctionCall field to collection

Posts   
 
    
jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 04-Sep-2009 17:16:48   

Hi,

Probably a simple question but I can't find an example in the documentation or elsewhere on this forum.

I have a collection of a certain entity. I want to do a GetMulti but add a field. This field is generated with a dbfunction and this function uses another field in the collection as parameter.

SQL would be like:

select field1, field2, func(field3) from table

I can add a field to a predicate but I just want to add the field to the collection.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 04-Sep-2009 19:48:23   

Hi,

Look at this Frans article: http://weblogs.asp.net/fbouma/archive/2006/06/09/LLBLGen-Pro-v2.0-with-ASP.NET-2.0.aspx

In Step 6 (fetching entities with related entities and paging) he is adding a field like so:

IEntityField2 scalarField = new EntityField2("NumberOfOrders",
                    new ScalarQueryExpression(OrderFields.OrderId.SetAggregateFunction(
                        AggregateFunction.Count),
                    (CustomerFields.CustomerId == OrderFields.CustomerId)));

You can do the same, but instead of ScalarQueryExpression, use a DBFunctionCall (both are IExpression).

Hope helpful.

David Elizondo | LLBLGen Support Team
jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 05-Sep-2009 18:21:27   

Hi, helpfull, but still having some troubles.. disappointed

I wrote this custom factory:

    public class ExtendedChannelMovieEntityFactory : ChannelMovieEntityFactory
    {
        public override IEntityFields CreateFields()
        {
            IEntityFields toReturn = base.CreateFields();
            addFullCategoryNameField(toReturn);
            return toReturn;
        }

        public override IEntity Create()
        {
            IEntity toReturn = base.Create();
            addFullCategoryNameField(toReturn.Fields);
            return toReturn;
        }

        private void addFullCategoryNameField(IEntityFields fields)
        {
            fields.Expand(1);
            IEntityField fullCategoryField = new EntityField("FullCategoryName",
                new DbFunctionCall("FullCategoryName({0})", new object[] { ChannelMovieFields.CategoryId }));
            fields.DefineField(fullCategoryField, fields.Count - 1);
        }
    }

This partial entity class:

    public partial class ChannelMovieEntity
    {
        public string FullCategoryName
        {
            // Read the last field in the fieldlist, as we've added the FullCategoryField to the end of the field list
            get
            {
                object value = this.Fields[this.Fields.Count - 1].CurrentValue;
                if (value != null)
                {
                    return value.ToString();
                }
                else
                {
                    return string.Empty;
                }
            }
        }
    }

And use this code to fill the collection:

            ChannelMovieCollection colChannelMovies = new ChannelMovieCollection();
            colChannelMovies.EntityFactoryToUse = new ExtendedChannelMovieEntityFactory();
            colChannelMovies.GetMulti(filter, 0, null, relations, prefetch);

The "FullCategoryName" field is filled with the last field of the ChannelMovieEntity instead of the newly added field. When I set breakpoints in the extended factory I can see the field is being created and the fieldcount is increased by one. Afterwards I hit the breakpoint in the partial ChannelMovieEntity class and the fieldcound is back to it's original value?

I'm using llblgen 2.5 with VS2008 and my project targets .Net 3.5.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 06-Sep-2009 23:56:41   

I can't reproduce your issue. Everything works here (Adapter, SelfServicing, v2.5 and v2.6).

Please update to the latest llblgen runtime library version. Also, please debug and check if what specific method is the problem.

One thing you can test is, just expand the field in CreateFields method, without refactor the code to your addFullCategoryNameField.

David Elizondo | LLBLGen Support Team
jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 07-Sep-2009 10:03:43   

It looks like the extended factory class is not used when the entities are created. Only the overridden Create and CreateFields functions in the original factories are called, not the ones in the derived class.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 07-Sep-2009 11:52:01   

Your code should work according to this thread: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=6756

Which LLBLGen Pro runtime library build are you using? (build number)

jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 07-Sep-2009 12:38:34   

Hi, I'm currently using runtime version v2.0.50727

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 07-Sep-2009 12:45:48   

That's not a build number, please check this: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7717

jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 07-Sep-2009 12:54:55   

All I can find is version 2.5 Final (March 28th, 2008 ).

jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 07-Sep-2009 14:25:33   

I've upgraded to the latest build of version 2.5 (June 2nd, 2008 ) but the problem still remains.

jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 07-Sep-2009 15:10:09   

Could it be that it has something to do with the fact that ChannelMovieEntity is a Sub-type of another entity? confused

jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 07-Sep-2009 15:45:23   

Where else is this factory linked to the collection?

I now wrote a new factory, the same as the ChannelMovieFactory, added the code to add the field there. This new factory also derives from EntityFactoryBase so you would expect that the ChannelMovieEntityFactory is never to be entered? Still this is the only factory that is being used... confused

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39905
Joined: 17-Aug-2003
# Posted on: 07-Sep-2009 18:03:36   

Could you post the new factory code you wrote? You also use inheritance? Could you also describe where your code differs from my example on the blogpost linked earlier in this thread (if possible)?

Frans Bouma | Lead developer LLBLGen Pro
jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 07-Sep-2009 21:49:45   

Hmm, I noticed something strange. I went back to the original code. Suddenly I noticed the debugger DID enter the overridden functions in the extended class but my sql profiler was showing me incorrect queries.

The db function was called correctly but the FROM clause was incorrect. Turned out I made a typo. Instead of deriving from the ChannelMovieEntityFactory I derived from the ChannelEntityFactory. ChannelEntity is an entity that DOESN'T use inheritence whereas ChannelMovieEntity is a subtype of MovieEntity. So my guess that it has something to do with the inheritance looks to be a good one. wink

Now only I need a solution for this problem. stuck_out_tongue_winking_eye I'd like to solve this in code instead of creating a view to acquire the correct resultset.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39905
Joined: 17-Aug-2003
# Posted on: 08-Sep-2009 09:47:01   

jeroen1980 wrote:

Hmm, I noticed something strange. I went back to the original code. Suddenly I noticed the debugger DID enter the overridden functions in the extended class but my sql profiler was showing me incorrect queries.

The db function was called correctly but the FROM clause was incorrect. Turned out I made a typo. Instead of deriving from the ChannelMovieEntityFactory I derived from the ChannelEntityFactory. ChannelEntity is an entity that DOESN'T use inheritence whereas ChannelMovieEntity is a subtype of MovieEntity. So my guess that it has something to do with the inheritance looks to be a good one. wink

Now only I need a solution for this problem. stuck_out_tongue_winking_eye I'd like to solve this in code instead of creating a view to acquire the correct resultset.

And what problem is 'this' referring to? wink (as I now don't know exactly what the problem is, or what's wrong)

Frans Bouma | Lead developer LLBLGen Pro
jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 08-Sep-2009 19:36:52   

I'm trying to add a field to an entitycollection. So I have this collection, and SQL-wise I like to do

select field1, field2, field3, dbfunction(field4) from table

where the table is ChannelMovie in this case.

Following the examples on this site and the forum I wrote a custom factory that derives from the ChannelMovieEntityFactory class and a partial ChannelMovieEntity class to add the extra field to the entity. This didn't work (as described above). For some reason the collection doesn't use my custom entityfactory when calling GetMulti (the one I set with collection.EntityFactoryToUse). Instead it only uses the default ChannelMovieEntityFactory.

Using the exact same code with another entity and deriving a custom factory from this other entity's factory class (with the only difference that this enity is NOT a subtype of another entity, in contrast to the ChannelMovieEntity which is a subtype of MovieEntity) the custom factory IS used when calling GetMulti as it should be and the extra field IS added to the collection.

jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 09-Sep-2009 13:36:07   

Anyone been able to reproduce this issue?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39905
Joined: 17-Aug-2003
# Posted on: 09-Sep-2009 13:46:39   

So the subtype is the problem. That's something we can work with. The object fetcher uses the row returned from the DB to determine which factory to use for creating a new entity. That mechanism apparently uses your original factory. I'll see if that's changeable for your situation.

(edit) I'm afraid it's not possible to do it this way. The problem is that the inheritanceinfo provider, the store which contains the static inheritance information about entities, is static and not changeable and it contains the original factory as the factory to use for a given entity. So even though you create a new one, the original one is used when a row is fetched from the db through the normal entity channels as it looks up the factory in the inheritance info provider.

There is a way though, using a [urldescription="projection fetch"http://www.llblgen.com/documentation/2.6/hh_goto.htm#Using%20the%20generated%20code/SelfServicing/gencode_datareadersprojections.htm%23projectingdynamiclistontocustomclasses[/url]. There is an example which fetches the results of a procedure in an entity collection, but you can also use a projection produced from a dynamic list (as described later in that section) and fetch that into an entity collection.

One caveat, these fetches aren't polymorphic, so you get the types produced by the factory of the collection, with the fields from the DB. Would that work for you?

Frans Bouma | Lead developer LLBLGen Pro
jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 09-Sep-2009 14:30:31   

Ok, so I now use this code to retreive the data:


            ResultsetFields fields = new ResultsetFields(3);
            fields[0] = ChannelMovieFields.MovieId;
            fields[1] = ChannelMovieFields.Name;
            fields[2] = new EntityField("FullCategoryName", new DbFunctionCall("dbo.FullCategoryName({0})", new object[] { ChannelMovieFields.CategoryId }));

            TypedListDAO dao = new TypedListDAO();

            using (IDataReader reader = dao.GetAsDataReader(null, fields, null, null, CommandBehavior.CloseConnection, 0, false))
            {
                List<IDataValueProjector> valueProjectors = new List<IDataValueProjector>();

                valueProjectors.Add(new DataValueProjector(ChannelMovieFieldIndex.MovieId.ToString(), 0, typeof(int)));
                valueProjectors.Add(new DataValueProjector(ChannelMovieFieldIndex.Name.ToString(), 1, typeof(string)));
                valueProjectors.Add(new DataValueProjector("FullCategoryName", 2, typeof(string)));

                DataProjectorToIEntityCollection projector = new DataProjectorToIEntityCollection(colChannelMovies);
                dao.GetAsProjection(valueProjectors, projector, reader);
            }

The query showing up in my SQL Server Profiler is ok. But I don't see how I can add the FullCategoryName field to my ChannelMovieEntity now? Do I still need a partial class?

jeroen1980
User
Posts: 28
Joined: 25-Mar-2008
# Posted on: 09-Sep-2009 15:15:38   

Never mind, changed my partial class to have a private field fullCategoryName and added a property wrapping this field and now everything is working fine!

Thnx! smile