Hi,

I am having problems trying to implement CustomLinq rewriter to calculate the age of a person. I am getting an over that the method GetAge is not valid.

I am not sure that I am implementing it properly.

Thank you,

Richard

public class Person : BaseEntity { [Field(Length = 128)] public string FirstName { get; set; }

 [Field (Length=128)]
    public string MiddleName { get; set; }

    [Field(Length = 128)]
    public string LastName { get; set; }

    [Field (Length=128)]
    public string SurName { get; set; }

    public string FullName
    {
        get { return string.Format("{0} {1}", FirstName, LastName); }
    }

    [Field]
    public DateTime DOB { get; set; }

/// <summary>
    /// Gets Age in Years
    /// </summary>
    /// <returns></returns>
    public int Age_in_Years
    {
        get { return GetAge(DOB); }

    }

}

[CompilerContainer(typeof(Expression))]
public static class CustomLinqCompilerContainer 
{

    [Compiler(typeof(Person), "Age_in_Years", TargetKind.PropertyGet)]
    public static Expression Age_in_Years(Expression person)
    {
        Expression<Func<Person, int>> ex = p => GetAge(p.DOB);
        return ex.BindParameters(person);
    }

    [Compiler(typeof(Person), "FullName", TargetKind.PropertyGet)]
    public static Expression FullName(Expression personExpression)
    {
        // Since "ex" type is specified, C# compiler
        // allows to use Person properties:
        Expression<Func<Person, string>> ex =
          person => person.FirstName + " " + person.LastName;

        // Binding lambda parameters replaces parameter usage in lambda.
        // In this case resulting expression body looks like this:
        // personExpression.FirstName + " " + personExpression.LastName
        return ex.BindParameters(personExpression);
    }

}

asked May 17 '14 at 21:04

rasxte's gravatar image

rasxte
20151617

edited May 17 '14 at 21:09

Hello X-tensive,

Have you been able to look for an answer to my issue. I am working on a project and really need this resolved.

Thank you,

Richard

(May 20 '14 at 10:10) rasxte rasxte's gravatar image

3 Answers:

Hello Richard,

since all of your code needs to be translate somehow to SQL you need to provide translation for GetAge method as well. The same applies to any other methods or properties used by it. DataObjects.Net out-of-the-box supports some limited set of base class library API. Everything else need to be expressed by supported members.

answered May 21 '14 at 00:49

Denis%20Krjuchkov's gravatar image

Denis Krjuchkov
179325

Denis,

GetAge is my own custom function... It's says that the function is not supported in my LINQ query. Everything compiles find so the function exists..

Thank you,

Here is the error message

Xtensive.Orm.QueryTranslationException was unhandled HResult=-2146233088 Message=Unable to translate 'Query.All().Where(v => (v.Visitor.Age_in_Years <= 18)).GroupBy(i => i.Visitor.FullName).Select(g => new @<race, male,="" female="">( g.Key, g.Count(s => (((Int32)s.Visitor.Sex) == 77)), g.Count(s => (((Int32)s.Visitor.Sex) == 70)) ))' expression. See inner exception for details. Source=Xtensive.Orm StackTrace: at Xtensive.Orm.Linq.QueryProvider.TranslateTResult at Xtensive.Orm.Linq.QueryProvider.ExecuteTResult at Xtensive.Orm.Linq.Queryable1.GetEnumerator() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) InnerException: System.NotSupportedException HResult=-2146233067 Message=Member 'NetString.RDFLib.DateAndTime.AgeFunc.GetAge' is not supported Source=Xtensive.Orm StackTrace: at Xtensive.Orm.Providers.ExpressionProcessor.CompileMember(MemberInfo member, SqlExpression instance, SqlExpression[] arguments) at Xtensive.Linq.ExpressionVisitor1.Visit(Expression e) at Xtensive.Orm.Providers.ExpressionProcessor.Visit(Expression e, Boolean smartNull) at Xtensive.Orm.Providers.ExpressionProcessor.VisitMemberAccess(MemberExpression m) at Xtensive.Linq.ExpressionVisitor1.Visit(Expression e) at Xtensive.Orm.Providers.ExpressionProcessor.Visit(Expression e, Boolean smartNull) at Xtensive.Orm.Providers.ExpressionProcessor.VisitBinary(BinaryExpression expression) at Xtensive.Linq.ExpressionVisitor1.Visit(Expression e) at Xtensive.Orm.Providers.ExpressionProcessor.Visit(Expression e, Boolean smartNull) at Xtensive.Orm.Providers.ExpressionProcessor.VisitLambda(LambdaExpression l) at Xtensive.Linq.ExpressionVisitor1.Visit(Expression e) at Xtensive.Orm.Providers.ExpressionProcessor.Visit(Expression e, Boolean smartNull) at Xtensive.Orm.Providers.ExpressionProcessor.Translate() at Xtensive.Orm.Providers.SqlCompiler.ProcessExpression(LambdaExpression le, List1[] sourceColumns) at Xtensive.Orm.Providers.SqlCompiler.VisitFilter(FilterProvider provider) at Xtensive.Orm.Rse.Compilation.Compiler1.Compile(CompilableProvider cp) at Xtensive.Orm.Providers.SqlCompiler.VisitCalculate(CalculateProvider provider) at Xtensive.Orm.Rse.Compilation.Compiler1.Compile(CompilableProvider cp) at Xtensive.Orm.Providers.SqlCompiler.VisitAggregate(AggregateProvider provider) at Xtensive.Orm.Rse.Compilation.Compiler1.Compile(CompilableProvider cp) at Xtensive.Orm.Providers.SqlCompiler.VisitApply(ApplyProvider provider) at Xtensive.Orm.Rse.Compilation.Compiler1.Compile(CompilableProvider cp) at Xtensive.Orm.Providers.SqlCompiler.VisitApply(ApplyProvider provider) at Xtensive.Orm.Rse.Compilation.Compiler1.Compile(CompilableProvider cp) at Xtensive.Orm.Rse.Compilation.Compiler1.Xtensive.Orm.Rse.Compilation.ICompiler.Compile(CompilableProvider provider) at Xtensive.Orm.Rse.Compilation.CompilationService.Compile(CompilableProvider provider, CompilerConfiguration configuration) at Xtensive.Orm.Linq.Translator.Translate[TResult](ProjectionExpression projection, IEnumerable1 tupleParameterBindings) at Xtensive.Orm.Linq.QueryProvider.TranslateTResult InnerException:

answered May 21 '14 at 10:16

rasxte's gravatar image

rasxte
20151617

Sorry, I did understand your reply before. So I guess there is no other way other than sql user defined function?

Richard

(May 21 '14 at 10:32) rasxte rasxte's gravatar image

Richard, you need to write translation for GetAge method just like you've did it for Age_in_Years property.

(May 21 '14 at 10:46) Denis Krjuchkov Denis%20Krjuchkov's gravatar image

You can use many DateTime and TimeSpan methods so this is likely possible.

(May 21 '14 at 10:48) Denis Krjuchkov Denis%20Krjuchkov's gravatar image

How would I go about writing a transalation.. Do You mean I have to write another custom compiler container function for GetAge?

(May 21 '14 at 10:54) rasxte rasxte's gravatar image

Yes, you're right. LINQ translator will apply all your custom compilers recursively. After all transformations performed you should be using only supported members, otherwise an exception will be thrown.

(May 21 '14 at 10:58) Denis Krjuchkov Denis%20Krjuchkov's gravatar image

I created the translation function but I get an error. I am using the same target member Age_In_Years. Should it be something else? Here is the function.

[Compiler(typeof(Person), "Age_in_Years", TargetKind.PropertyGet)] public static Expression GetAge(Expression person) { Expression<func<person, netstring.rdflib.types.age="">> ex = p => AgeFunc.GetAge(p.DOB); return ex.BindParameters(person); }

System.ArgumentException was unhandled by user code HResult=-2147024809 Message=An item with the same key has already been added. Source=mscorlib StackTrace: at System.Collections.Generic.Dictionary2.Insert(TKey key, TValue value, Boolean add) at Xtensive.Orm.Linq.MemberCompilation.MemberCompilerProvider1.RegisterCompilers(Type compilerContainer, ConflictHandlingMethod conflictHandlingMethod) at Xtensive.Orm.Building.Builders.MemberCompilerProviderBuilder.RegisterContainers(IEnumerable1 containers) at Xtensive.Orm.Building.Builders.MemberCompilerProviderBuilder.Build() at Xtensive.Orm.Building.Builders.MemberCompilerProviderBuilder.Build(DomainConfiguration configuration, IEnumerable1 systemCompilerContainers) at Xtensive.Orm.Providers.DomainHandler.InitializeServices() at Xtensive.Orm.Building.Builders.DomainBuilder.InitializeServices() at Xtensive.Orm.Building.Builders.DomainBuilder.Run(DomainBuilderConfiguration builderConfiguration) at Xtensive.Orm.Upgrade.UpgradingDomainBuilder.BuildMultistageDomain() at Xtensive.Orm.Upgrade.UpgradingDomainBuilder.Run() at Xtensive.Orm.Upgrade.UpgradingDomainBuilder.Build(DomainConfiguration configuration) at NetString.RDFLib.Data.DomainBuilder.Build(String name) in e:\Program Develop.Net V4.5.1\Projects\NetString.RDFLib\NetString.RDFLib\Data\DomainBuilder.cs:line 61 at NetString.RDFLib.Data.DomainBuilder.Build() in e:\Program Develop.Net V4.5.1\Projects\NetString.RDFLib\NetString.RDFLib\Data\DomainBuilder.cs:line 43 at NetString.RDFLib.Data.SessionManager.EnsureDomainIsBuilt() in e:\Program Develop.Net V4.5.1\Projects\NetString.RDFLib\NetString.RDFLib\Data\SessionManager.cs:line 308 at NetString.RDFLib.Data.SessionManager.get_Domain() in e:\Program Develop.Net V4.5.1\Projects\NetString.RDFLib\NetString.RDFLib\Data\SessionManager.cs:line 165 at NetString.RDFLib.Data.SessionManager.ProvideSession() in e:\Program Develop.Net V4.5.1\Projects\NetString.RDFLib\NetString.RDFLib\Data\SessionManager.cs:line 260 at NetString.RDFLib.Data.SessionManager.EnsureSessionIsProvided() in e:\Program Develop.Net V4.5.1\Projects\NetString.RDFLib\NetString.RDFLib\Data\SessionManager.cs:line 322 at NetString.RDFLib.Data.SessionManager.get_Session() in e:\Program Develop.Net V4.5.1\Projects\NetString.RDFLib\NetString.RDFLib\Data\SessionManager.cs:line 188 at COIBV.Global.Application_BeginRequest(Object sender, EventArgs e) in e:\Program Develop.Net V4.5.1\WebSites\COIBV\App_Code\Global.asax.cs:line 58 at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) InnerException:

answered May 21 '14 at 11:33

rasxte's gravatar image

rasxte
20151617

Denis,

Could you help me write the transalation for my function GetAge(dob). The function is not a member of an entity. It's just a static method takes one parameter "DateTime".

I've but just don't have the know how to write the transalator code.

Thank you,

Richard

(May 22 '14 at 10:05) rasxte rasxte's gravatar image

[Compiler] attribute parameters are used to specify member which this compiler method is suitable for.

Suppose you have the AgeFunc class defined as follows

public static class AgeFunc
{
    public static int GetAge(DateTime date)
    {
        // ...
    }
}
(May 26 '14 at 06:58) Denis Krjuchkov Denis%20Krjuchkov's gravatar image

then you can provide translation for this method as follows:

[CompilerContainer(typeof (Expression)]
public static AgeFuncCompilers
{
    [Compiler(typeof(AgeFunc), "GetAge", TargetKind.Method | TargetKind.Static)]
    public static Expression GetAge(Expression date)
    {
        // ...
    }
}

As you can see [Compiler] attribute expects at least three components to identify member: type, member name and member attributes.

(May 26 '14 at 06:58) Denis Krjuchkov Denis%20Krjuchkov's gravatar image

Hi Denis,

I am getting a stackoverflow error. I am not sure if I am implementing the function correctly. The statement that generates the error is the following: Expression<func<datetime, age="">> ex = d => AgeFunc.GetAge(d.Date); Age is a type defined in my code. AgeFunc.GetAge is also defined.

[Compiler(typeof(AgeFunc), "GetAge", TargetKind.Method | TargetKind.Static)] public static Expression GetAge(Expression date) { Expression<func<datetime, age="">> ex = d => AgeFunc.GetAge(d.Date);

 return ex;

}

Thank you,

Richard

(May 28 '14 at 23:29) rasxte rasxte's gravatar image

Hi, Richard. Let's see. You try to implement translation for GetAge method and using the same method inside translator. You should use something like DateTime.Now.Year - d.Year; to get age

(May 30 '14 at 01:35) Alexey Kulakov Alexey%20Kulakov's gravatar image

Hi Alex,

Why original inquiry is how could I implement a custom function to be called from a calculated field. My GetAge function has the algorithm to calculage age based on a giving date. Is this possible? Your example is using simple code to calculate age.

Thank you,

Richard

(Jun 02 '14 at 00:05) rasxte rasxte's gravatar image

Hi, Richard.

Yes, it's possible. But you must implement translator for each custom method which used. Eventually, your translation expression must use only simple expressions, which DataObjects.Net can translate by built-in translator.

(Jun 02 '14 at 03:49) Alexey Kulakov Alexey%20Kulakov's gravatar image

Hi Alexey,

How would I implement the translator using my own GetAge function with my algorithm?... Below is what I have implemented but I get stackoverlow error.

[Compiler(typeof(AgeFunc), "GetAge", TargetKind.Method | TargetKind.Static)] public static Expression GetAge(Expression date) { Expression<func<datetime, age="">> ex = d => AgeFunc.GetAge(d.Date);

Thank you,

Richard

(Jun 02 '14 at 11:19) rasxte rasxte's gravatar image

Hi Richard. It will throw exception. You have infinite loop in your translator. DataObjects.Net tries to translate your custom method. It finds custom translator and uses him. But in translator you use the same method. And DataObjects.Net uses the same translator again. And we have a loop which overflow the stack. You should repeat logic of GetAge(DateTime date) in a expression of translator. I don't know your algorithm and can't answer more concretely.

(Jun 02 '14 at 23:29) Alexey Kulakov Alexey%20Kulakov's gravatar image

Alexey,

I am correct on assuming that you can't call a custom function within a translator function.

Thank you,

Richard

(Jun 03 '14 at 23:41) rasxte rasxte's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA