ArgumentNullException with nullable enum on typed view in v5.5

Posts   
1  /  2
 
    
TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 17-Dec-2018 11:28:47   

Running this in LINQPad (or anywhere)

AllManageReports.Where(mr=>mr.OccurrenceStatus==null)

gives

ArgumentNullException: Value cannot be null.
Parameter name: value  at System.Enum.ToObject(Type enumType, Object value)
   at lambda_method(Closure , ProjectionRow , Int32[] )
   at SD.LLBLGen.Pro.LinqSupportClasses.DataProjectorToObjectList`1.<>c__DisplayClass4_0.<.ctor>b__0(Object[] r, Int32[] i)
   at SD.LLBLGen.Pro.LinqSupportClasses.DataProjectorToObjectList`1.AddRowToResults(Object[] rawProjectionResult)
   at SD.LLBLGen.Pro.ORMSupportClasses.ProjectionUtils.FetchProjectionFromReader(List`1 valueProjectors, IGeneralDataProjector projector, IDataReader dataSource, Int32 rowsToSkip, Int32 rowsToTake, Boolean clientSideLimitation, Boolean clientSideDistinctFiltering, Boolean clientSidePaging, Boolean performValueProjectionsOnRawRow, Boolean postProcessDBNullValues, Dictionary`2 typeConvertersToRun, IRetrievalQuery queryExecuted)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.FetchProjection(List`1 valueProjectors, IGeneralDataProjector projector, IRetrievalQuery queryToExecute, Boolean performValueProjectionsOnRawRow, Boolean postProcessDBNullValues, Dictionary`2 typeConvertersToRun)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.FetchProjection(List`1 valueProjectors, IGeneralDataProjector projector, QueryParameters parameters)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.<>c__DisplayClass22_0.<FetchProjection>b__0()
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2.ExecuteValueListProjection(QueryExpression toExecute)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression, Type typeForPostProcessing)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery`1.System.Collections.IEnumerable.GetEnumerator()

AllManageReports is a typed view and OccurrenceStatus is nullable enum. This has worked fine in previous versions and works fine on the entity and Where mr=>mr.OccurrenceStatus!=null

v5.5.0

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 17-Dec-2018 20:21:52   

To be sure: it crashes when a NULL is returned for OccurenceStatus ?

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 17-Dec-2018 20:54:00   

yes

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 17-Dec-2018 21:10:46   

Yeah I realized it was a pretty dumb question from my part simple_smile We'll look into it.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 18-Dec-2018 12:07:36   

I can't reproduce it.


[Test]
public void NullableEnumFetchWithNullInTypedViewTest()
{
    using(var adapter = new DataAccessAdapter())
    {
        var metaData = new LinqMetaData(adapter);
        var results = metaData.ProductPocoLinq.Where(p => p.CategoryId == null).ToList();
        Assert.AreEqual(1, results.Count);
        Assert.AreEqual(75, results[0].ProductId);
    }
}

Works fine. 'CategoryId' here is a nullable enum. There's 1 row with a result where CategoryId is null.

ProductPocoLinq is a typedview generated as pocowithlinq query.

(Tested in both 5.4 and 5.5.1 (but 5.5.0 has almost the same code, and no changes in this area are made in 5.5.1 so I doubt it's 5.5.1 vs. 5.5.0, also as it worked fine in 5.4)

Frans Bouma | Lead developer LLBLGen Pro
Jen
User
Posts: 8
Joined: 18-Dec-2018
# Posted on: 18-Dec-2018 17:09:08   

Hi Otis,

We have the same issue with SD.LLBLGen.Pro.ORMSupportClasses version 5.5.0. I can reproduce this with the following test code:

    public class FetchNullableEnumTests
    {
        [Test]
        public void FetchNullableEnumTest()
        {
            var coreData = new CoreData();

            var records = coreData.Linq.EcTypeApproval
                .Where(x => x.FuelType == null)
                .Select(x => new TestData
                {
                    FuelType = x.FuelType // x.FuelType is a Nullable<RobFuelType>
                })
                .ToList();
        }

        public class TestData
        {
            public RobFuelType? FuelType { get; set; }
        }
    }

This works with SD.LLBLGen.Pro.ORMSupportClasses version 5.4.0 but fails with version 5.5.0. The exception is:

System.ArgumentNullException : Value cannot be null.
Parameter name: value
   at System.Enum.ToObject(Type enumType, Object value)
   at lambda_method(Closure , ProjectionRow , Int32[] )
   at SD.LLBLGen.Pro.LinqSupportClasses.DataProjectorToObjectList`1.<>c__DisplayClass4_0.<.ctor>b__0(Object[] r, Int32[] i)

CoreData is a DataAccessAdapter, RobFuelType is a enum. The column FuelType is a nullable nvarchar(20).

Your help is much appreciated.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 18-Dec-2018 17:32:10   

Thanks for that, we'll re-evaluate and see if we can reproduce it with your repro. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 18-Dec-2018 17:58:32   

Still works (with 5.5.1 hotfix build).



[Test]
public void NullableEnumFetchWithNullInTypedViewTest2()
{
    using(var adapter = new DataAccessAdapter())
    {
        var metaData = new LinqMetaData(adapter);
        var q = from p in metaData.ProductPocoLinq
                where p.ProductId == 75
                select new Foo()
                       {
                           CategoryId = p.CategoryId
                       };
        var results = q.ToList();
        Assert.AreEqual(1, results.Count);
        Assert.IsNull(results[0].CategoryId);
    }
}


public class Foo
{
    public CategoryType? CategoryId { get; set; }
}

Will now try a separate project with 5.5.0 release build.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 18-Dec-2018 18:15:35   

v5.5.0 works too... (made a console app for this, as moving the whole test project stack back to an older build of the runtime is more work)


using System;
using NW26.DatabaseSpecific;
using System.Linq;
using EnumTypes;
using NW26.Linq;

namespace ConsoleApplication1
{
    

    public class Foo
    {
        public CategoryType? CategoryId { get; set; }
    }
    
    
    internal class Program
    {
        public static void Main(string[] args)
        {
            using(var adapter = new DataAccessAdapter())
            {
                var metaData = new LinqMetaData(adapter);
                var q = from p in metaData.ProductPocoLinq
                        where p.ProductId == 75
                        select new Foo()
                               {
                                   CategoryId = p.CategoryId
                               };
                var results = q.ToList();

                Console.WriteLine("Count: {0}. Categoryid: {1}", results.Count, results[0].CategoryId.HasValue ? "Value" : "Null");
            }
        }
    }
}

// result:
Count: 1. Categoryid: Null

Which is what I expected as there's no real code difference with v5.5.1 hotfix build in this area.

I'm puzzled what causes the failure on your side...

CategoryType is:


  public enum CategoryType
  {
    Beverages = 1,
    Condiments = 2,
    Confections = 3,
    DairyProducts = 4,
    GrainsCereals = 5,
    MeatPoultry = 6,
    Produce = 7,
    Seafood = 8,
  }

I imported the enum type, then simply created a typedview of the products table of northwind, changed the type of CategoryId to the imported enum type, and in our test db it's a nullable field, so it results in a nullable enum type. We have 1 row in the products table with a null in CategoryId, the one with pk 75.

I tested on .NET 4.6.2. Not that it would make much of a difference I think. TomDog uses linqpad, so I guess that's .NET full as well...

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 18-Dec-2018 19:20:18   

One thing that occurred to me is that I test with an enum based on int32, and yours, Jen, is an enum based on a string. TomDog, could you confirm your field is also an enum based on a string?

I'll try to reproduce it with an enum based on a string tomorrow (wednesday).

Frans Bouma | Lead developer LLBLGen Pro
Jen
User
Posts: 8
Joined: 18-Dec-2018
# Posted on: 18-Dec-2018 19:26:36   

Thanks for your quick response. I am also using .NET full (4.7.2). The only difference is that we have configured a type converter, which converts a string to an enum. I will give the hotfix a try.

TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 18-Dec-2018 20:22:16   

Opps yes it is a string - an important detail.flushed

    [TypeConverter(typeof(OccurrenceStatusEnumConverter))]
    public enum OccurrenceStatus
    {
        [LocalizedDescription(typeof(Labels), "OccurrenceStatus_AllActive")]
        All_Active,

        [LocalizedDescription(typeof(Labels), "OccurrenceStatus_Open")]
        Open,

        [LocalizedDescription(typeof(Labels), "OccurrenceStatus_InProgress")]
        In_Progress,

        [LocalizedDescription(typeof(Labels), "OccurrenceStatus_Closed")]
        Closed,

        [LocalizedDescription(typeof(Labels), "OccurrenceStatus_Deleted")]
        Deleted
    }

The string in the DB is one of these values: null Closed Deleted In Progress Open

(All_Active isn't in DB)

FTR

  public class OccurrenceStatusEnumConverter : EnumerationConverter<OccurrenceStatus>
  {
    public override string ConvertEnumToString(OccurrenceStatus value)
    {
      return Convert.ToString(value).Replace(GeneralHelper.EnumSpaceSubstitute, ' ');
    }
  }

/// <summary>
  /// Converter with as core type System.Enum, for mapping a field with a .NET type System.Enum onto a numeric or string database field
  /// </summary>
  /// <remarks>Unfortunately a subclass is need for every enum as the LLBL designer doesn't support generic type converters</remarks>
  /// <see cref="http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=7355"/>
  /// <see cref="http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=7945"/>
  /// <see cref="http://www.llblgen.com/documentation/2.6/hh_start.htm"/>
  /// <seealso cref="System.ComponentModel.EnumConverter"/>
  /// <example>Usage public class MyEnumConverter : EnumerationConverter<!--MyEnum-->{}</example>
  /// <typeparam name="T"></typeparam>
  [Description("Converter with as core type System.Enum, for mapping a field with a .NET type System.Enum onto a numeric or string database field")]
  public abstract class EnumerationConverter<T> : TypeConverter where T : struct
  {
    public static readonly IList<TypeCode> ValidTypeCodes = new[]
      {
        TypeCode.SByte, TypeCode.Byte, TypeCode.UInt16, TypeCode.Int16, TypeCode.UInt32,
        TypeCode.Int32, TypeCode.UInt64, TypeCode.Int64, TypeCode.Decimal, TypeCode.String
      };

    /// <summary>
    /// Used for converting any spaces in the DB version of an enum name to a substitute in the CLR enum name.
    /// </summary>
    /// <example>In_Progress -to- "In Progress"</example>
    /// <remarks>This can be changed by setting it in a constructor</remarks>
    internal static char SpaceSubstitute = GeneralHelper.EnumSpaceSubstitute;

    /// <summary>
    /// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context.
    /// </summary>
    /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
    /// <param name="sourceType">A <see cref="T:System.Type"/> that represents the type you want to convert from.</param>
    /// <returns>
    /// true if this converter can perform the conversion; otherwise, false.
    /// </returns>
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
      if (sourceType == null)
        throw new ArgumentNullException(nameof(sourceType));
      return ValidTypeCodes.Contains(Type.GetTypeCode(sourceType));
    }

    /// <summary>
    /// Returns whether this converter can convert the object to the specified type, using the specified context.
    /// </summary>
    /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
    /// <param name="destinationType">A <see cref="T:System.Type"/> that represents the type you want to convert to.</param>
    /// <returns>
    /// true if this converter can perform the conversion; otherwise, false.
    /// </returns>
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
      if (destinationType == null)
        throw new ArgumentNullException(nameof(destinationType));
      return ValidTypeCodes.Contains(Type.GetTypeCode(destinationType));
    }

    /// <summary>
    /// Converts the given object to the type of this converter, using the specified context and culture information.
    /// </summary>
    /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
    /// <param name="culture">The <see cref="T:System.Globalization.CultureInfo"/> to use as the current culture.</param>
    /// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
    /// <returns>
    /// An <see cref="T:System.Object"/> that represents the converted value.
    /// </returns>
    /// <exception cref="T:System.NotSupportedException">
    /// The conversion cannot be performed.
    /// </exception>
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
      if (value == null)
        return null;
      var valueTypeCode = Type.GetTypeCode(value.GetType());
      if (valueTypeCode == TypeCode.DBNull)
        return null;

      var type = typeof (T);
      if (valueTypeCode == TypeCode.String)
      {
        var s = (string) value;
        if (string.IsNullOrEmpty(s))
          throw new ArgumentNullException(nameof(value));
        if (s == "undefined" && !Enum.IsDefined(type, s)) //"undefined" =~ javascript null
          return null;
        return ConvertFromStringToEnum(s);
      }
      if (valueTypeCode == TypeCode.Decimal)
        value = Convert.ToUInt64(value);
      if (ValidTypeCodes.Contains(valueTypeCode))
        return Enum.ToObject(MetaDataHelper.GetUnderlyingTypeIfNullable(type), value);
      throw new NotSupportedException(String.Format("Conversion from a value of type '{0}' to '{1}' isn't supported.", value.GetType(), type.Name));
    }

    /// <summary>
    /// Converts a string to the enum.
    /// </summary>
    /// <param name="value">The string value.</param>
    /// <returns>The enum</returns>
    public virtual T ConvertFromStringToEnum(string value)
    {
      return (value).ToEnum<T>(SpaceSubstitute);
    }

    /// <summary>
    /// Converts the given value object to the specified type, using the specified context and culture information.
    /// </summary>
    /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
    /// <param name="culture">A <see cref="T:System.Globalization.CultureInfo"/>. If null is passed, the current culture is assumed.</param>
    /// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
    /// <param name="destinationType">The <see cref="T:System.Type"/> to convert the <paramref name="value"/> parameter to.</param>
    /// <returns>
    /// An <see cref="T:System.Object"/> that represents the converted value.
    /// </returns>
    /// <exception cref="T:System.ArgumentNullException">
    /// The <paramref name="destinationType"/> parameter is null.
    /// </exception>
    /// <exception cref="T:System.NotSupportedException">
    /// The conversion cannot be performed.
    /// </exception>
    public override object ConvertTo(ITypeDescriptorContext context,
                                     CultureInfo culture,
                                     object value,
                                     Type destinationType)
    {
      if (destinationType == null)
        throw new ArgumentNullException(nameof(destinationType));
      if (value == null)
        return null;
      //Debug.Assert(value is T);
      return destinationType == typeof (string)
               ? ConvertEnumToString((T) value)
               : culture == null ? Convert.ChangeType(value, destinationType) : Convert.ChangeType(value, destinationType, culture);
    }

    /// <summary>
    /// Converts the enum to a string.
    /// </summary>
    /// <param name="value">The enum value.</param>
    /// <returns></returns>
    public virtual string ConvertEnumToString(T value)
    {
      return (value as Enum).EnumToString(SpaceSubstitute, false);
    }

    ///<summary>
    /// If the string contains any EnumSpaceSubstitute characters then they are replaced with a space character
    ///</summary>
    ///<param name="value">string version of an enum</param>
    ///<returns>string version of an enum with spaces in it as required</returns>
    public static string ReplaceSubstituteWithSpace(string value)
    {
      return value.Replace(SpaceSubstitute, ' ');
    }

    /// <summary>
    /// Creates an instance of the type that this <see cref="T:System.ComponentModel.TypeConverter"/> is associated with, using the specified context, given a set of property values for the object.
    /// </summary>
    /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
    /// <param name="propertyValues">An <see cref="T:System.Collections.IDictionary"/> of new property values.</param>
    /// <returns>
    /// An <see cref="T:System.Object"/> representing the given <see cref="T:System.Collections.IDictionary"/>, or null if the object cannot be created. This method always returns null.
    /// </returns>
    public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
    {
      return default(T);
    }
  }

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 18-Dec-2018 20:32:33   

Thanks, will try with and without type converter (as enum<->string is supported since v5.5 without the need of a type converter and likely the cause of this problem somewhere).

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 19-Dec-2018 11:32:35   

Without a type converter (as that's no longer needed in v5.5) it works:


[Test]
public void NullableEnumFetchOnStringInTypedViewNoTcTest()
{
    using(var adapter = new DataAccessAdapter())
    {
        var metaData = new LinqMetaData(adapter);
        var q = from c in metaData.CategoryEnumNoTCLinq
                where c.CategoryId == 9
                select c;
        var results = q.ToList();
        Assert.AreEqual(1, results.Count);
        Assert.IsNull(results[0].CategoryName);
    }
}


[Test]
public void NullableEnumFetchOnStringInTypedViewNoTcTest2()
{
    using(var adapter = new DataAccessAdapter())
    {
        var metaData = new LinqMetaData(adapter);
        var q = from c in metaData.CategoryEnumNoTCLinq
                where c.CategoryId == 9
                select new NullableEnumOnStringContainer()
                       {
                           CategoryName = c.CategoryName
                       };
        var results = q.ToList();
        Assert.AreEqual(1, results.Count);
        Assert.IsNull(results[0].CategoryName);
    }
}

With a TC, same tests basically but now a typedview with a type converter converting the enum to string, fails indeed.

Looking into it. For the time being, you can remove the type converter and it will work as expected.

(edit) the core issue is the premature conversion of DBNull.Value to null. The pipeline is heavily optimized and doesn't do a conversion pass if it doesn't have to, so it deals with DBNull.Value if needed and returns null if it runs into that. As you convert DBNull.Value to null (I re-used your type converter code as a basis), the value in the array to convert to values in the projection lambda is null, not DBNull.Value. As it doesn't do unneeded checks for null, it skips that, it only tests for DBNull.Value. This worked previously because it didn't do enum-string conversions.

As you already do the conversion manually, using a type converter, the value is already of that enum type, but the pipeline doesn't know at that level that a TC has already converted it, it simply converts the value. It works normally as Enum.ToObject() checks for the type to convert to and is basically a no-op if you pass in a value of that type.

Telling everyone not to convert DBNull.Value to null in their type converters isn't going to work we'll add an extra test for null to the projection code for this particular method (convert to enum) as that's the common path for TCs.

In general there's no need for converting to null in a TC in ConvertFrom.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 19-Dec-2018 12:29:32   

Fixed in next build. (Released as hotfix later today).

But really, don't convert DBNull.Value to null in a TC, that's unneeded. In case of string<->enum, consider not using a TC at all, as it's no longer necessary (and not using a TC is faster). simple_smile

Edit: hotfix build 5.5.1 with the correction for this issue is now available.

Frans Bouma | Lead developer LLBLGen Pro
Jen
User
Posts: 8
Joined: 18-Dec-2018
# Posted on: 19-Dec-2018 16:24:54   

Thank you!

Jen
User
Posts: 8
Joined: 18-Dec-2018
# Posted on: 20-Dec-2018 10:42:49   

Hi Otis, the hotfix works like a charm. Do you have an indication when the stable release will be available?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 20-Dec-2018 11:05:38   

Good to hear the fix works simple_smile

Jen wrote:

Hi Otis, the hotfix works like a charm. Do you have an indication when the stable release will be available?

Early next year, first two weeks of January I think. This is a release build btw, it's build with the same build pipeline as the RTM builds, so you can use this as-is, it doesn't contain debug/experimental stuff and has passed all unit tests simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Jen
User
Posts: 8
Joined: 18-Dec-2018
# Posted on: 20-Dec-2018 11:18:04   

Nice, thanks simple_smile

TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 21-Dec-2018 12:33:20   

Otis wrote:

But really, don't convert DBNull.Value to null in a TC, that's unneeded. In case of string<->enum, consider not using a TC at all, as it's no longer necessary (and not using a TC is faster). simple_smile

I've updated my TC to leave DBNull alone and all works now. How does the built-in string<->enum conversion handle spaces and other invalid c# name characters? I presume it doesn't and I still have to use my type converter....

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 21-Dec-2018 16:47:44   

The string<-> enum conversion in v5.5 is simply the enumValue.ToString() <-> Enum conversion, so the actual name of the enum value in the enum definition can be used to convert to a real enum instance. If you have a custom value then of course it doesn't know how to convert it to an enum value without assistance so then indeed you need your type converter (e.g. if the string is part of an attribute)

Frans Bouma | Lead developer LLBLGen Pro
Jen
User
Posts: 8
Joined: 18-Dec-2018
# Posted on: 17-Jan-2019 08:56:27   

Otis wrote:

Early next year, first two weeks of January I think. This is a release build btw, it's build with the same build pipeline as the RTM builds, so you can use this as-is, it doesn't contain debug/experimental stuff and has passed all unit tests simple_smile

@Otis, any news for the RTM release? We usually dot not use a preview version, but will consider if the RTM release takes a long time

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 17-Jan-2019 09:08:15   

It's scheduled for release today actually. We have one pending issue to look into but it's otherwise good to go.

Frans Bouma | Lead developer LLBLGen Pro
Jen
User
Posts: 8
Joined: 18-Dec-2018
# Posted on: 17-Jan-2019 09:11:59   

Otis wrote:

It's scheduled for release today actually. We have one pending issue to look into but it's otherwise good to go.

👍

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 17-Jan-2019 11:06:54   

It's available now simple_smile

Frans Bouma | Lead developer LLBLGen Pro
1  /  2