Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A possible way of triggering the expression without explicity calling the property? #1

Closed
AlexKeySmith opened this issue Jul 27, 2012 · 7 comments

Comments

@AlexKeySmith
Copy link

First of all, great project! I think it might be a lifesaver for me, but I'm having a little trouble with something.

I've found that when not explicitly calling the wrapped property that the expression isn't called. For example the top one works ok as I'm doing lr.Title, however the second example doesn't work as I'm getting the entire entity.

var thisWorks = (from lr in LearningResources
                select new 
                {
                    lr.Title
                }).WithTranslations();

var thisDoesNotWork = (from lr in LearningResources
                        select lr).WithTranslations();


thisWorks.Dump();
thisDoesNotWork.Dump();

I'm guessing this is how it's meant to work, but it just took me a little while to figure it out. Unless I'm missing something?

Is there a way I can trigger calling my custom "Title" if I'm calling the entire entity?

-thanks
Alex.

@cburgdorf
Copy link

Assuming Title is you custom property. Then this is probably because your custom property is aggregating data from a related entity. If that's the case you need to use an Include. However, you don't need the Include in case you already use the property in your query, because EF then knows about it. That's why it's working in your first example.

@AlexKeySmith
Copy link
Author

Hi cburgdorf,

Thanks for the quick response.

I've just tweaked it to include the related entity (is this what you mean?), but it didn't work unfortunately:

var thisDoesNotWork = (from lr in LearningResources
                        select lr
                        ).Include(lr => lr.LearningResource_MultitenantCustomisation).WithTranslations();

Unless perhaps should I be doing this on the expression itself somehow?

@AlexKeySmith
Copy link
Author

Here is an example of the expression if it helps:

 private static readonly CompiledExpression<LearningResource, string> titleExpression = DefaultTranslationOf<LearningResource>.Property(lr => lr.Title)
            .Is(lr =>
                lr.LearningResource_MultitenantCustomisation.FirstOrDefault(tenantCustomisation => tenantCustomisation.BusinessUnitID == MyBUID && tenantCustomisation.LearningResourceID == lr.LearningResourceID).Title
                ?? lr.TitleInternal

                );

Interestingly my property is doing an evaluation like in the examples (I couldn't get this to work properly), instead it's just a plain:

public string Title
        {
            get;
            set;
        }

@damieng
Copy link
Owner

damieng commented Jul 30, 2012

It's really designed to be used in the where criteria and in all cases the expression should be capable of being evaluated on the client or the server so in the second case the property should still work fine. If you're trying to do something odd on that property with side-effects or things that can't be evaluated client-side it's probably not going to work.

@AlexKeySmith
Copy link
Author

Thanks Damien,

What I'm doing is swapping out a column with a column for a left joined table (well a db view really). So it's a little odd what I'm trying to do, but I don't think it has side-effects.

In the example where I'm just grabbing the title column, if I set a break point in ExpressiveExtensions.cs it hits

VisitCompiledExpression

Via:

if (map.TryGetValue(node.Member, out cp)) {
                    return VisitCompiledExpression(cp, node.Expression);
 }

But does not in the example when selecting the whole entity. Is there a way do you think that I'll be able to trigger the expression without the where clause?

@damieng
Copy link
Owner

damieng commented Jul 30, 2012

Basically when the WithTranslations visitor executes it replaces all property accesses with the expression. Your second example never references the property so there is nothing for it to replace.

The problem you're going to have is that Entity Framework or LINQ to SQL are not going to be happy returning an Entity object that has had one of the normally stored properties changed behind the scenes - both systems like to be in charge of materializing their objects.

About the best I can think of would be something like:

var thisDoesNotWork = (from lr in LearningResources
select new { Title = lr.Title, Full = lr).WithTranslations()'
foreach(var item in thisDoesNotWork) item.Full.Title = item.Title;

The problem there however is going to be that now the objects are marked as dirty and will rewrite the fake title as the real title if you make any changes.

Probably the safest thing to do would be to add a property that is not stored/mapped in the database called CalculatedTitle that has the expression and have the app refer to that.

@AlexKeySmith
Copy link
Author

Thanks for the info Damien.

Good news, we managed to get the column displaying, it was my dodgy CompiledExpression! It was throwing an exception in the background which asp.net was hiding:

 private static readonly CompiledExpression<LearningResource, string> titleExpression = DefaultTranslationOf<LearningResource>.Property(lr => lr.Title)
            .Is(lr =>
                lr.LearningResource_MultitenantCustomisation.FirstOrDefault(tenantCustomisation => tenantCustomisation.BusinessUnitID == MyBUID && tenantCustomisation.LearningResourceID == lr.LearningResourceID).Title
                ?? lr.TitleInternal

                );

Should of read:

private static readonly CompiledExpression<LearningResource, string> titleExpression = DefaultTranslationOf<LearningResource>.Property(lr => lr.Title)
            .Is(lr =>
                lr.LearningResource_MultitenantCustomisation.FirstOrDefault(tenantCustomisation => tenantCustomisation.BusinessUnitID == MyBUID && tenantCustomisation.LearningResourceID == lr.LearningResourceID) != null ? 
                lr.LearningResource_MultitenantCustomisation.FirstOrDefault(tenantCustomisation => tenantCustomisation.BusinessUnitID == MyBUID && tenantCustomisation.LearningResourceID == lr.LearningResourceID).Title : lr.TitleInternal

                );

When I used (below), it would work when getting the single column, but not the whole entity.

public string Title
        {
            get; set;
        }

If I used the following it revealed the exception. Guessing the expression visitor kicks in when explicitly loading columns, but you need to call the expression directly if just loading the whole entity - but I couldn't figure this out due to my dodgy expression.

public string Title
        {
            get
            {
                return titleExpression.Evaluate(this);                
            }
            set
            {
                this.TitleInternal = value;
            }
        }

Thanks for the help, I wouldn't of noticed it without your assistance. Plus using the include has helped optimise it a bit. I like the idea of the wrapped { Title = lr.Title, Full = lr } I'll keep that in my toolbelt for another day, thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants