Issues with FluentValidator after upgrade from ServiceStack 6.5.1 to 8.2.3

Hello.

Repo recreating the issue: GitHub - thomasbrenna/servicestack-gist

After trying to upgrade from ServiceStack 6.5.1 to 8.2.3, my app fails to startup, and I get this error:

System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'

The error itself pops up here:

app.UseServiceStack(new AppHost());

in Program.cs.

It seems to be related to FluentValidator. I’m creating my own validator based on AbstractValidator (like the UserValidator shown here, but am also using regular declarative validations in some cases.

I’ve been able to recreate the issue in a fresh “x new web” project, with some modification to make it look more like my actual project.

If I comment out this line, then everything works fine (well, except for the validator not being picked up of course).

Let me know if you need anything else!
Thanks in advance.

Edit: I don’t know if it is relevant (probably not), as the result is the same,
but in my actual app everything is in the same assembly, as it’s a fairly small one, and instead of having to comment out something like this, I instead have to comment out the entire Validator class, like so:

using ServiceStack;
using ServiceStack.FluentValidation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace testss.ServiceModel;

public class ListEntry
{
    public required string Prop1 { get; set; }

    public required int Prop2 { get; set; }

    public required ListEntryChild Child { get; set; }
}
/*
public class ListEntryValidator : AbstractValidator<ListEntry>
{
    public ListEntryValidator()
    {
        RuleFor(h => h.Prop1).NotEmpty().WithMessage("Prop1 cannot be empty!");

        RuleFor(h => h.Prop2).Must(t => t >= 10).WithMessage("Prop2 must be at least 10!");

        RuleFor(h => h.Child).NotEmpty().WithMessage("Child cannot be empty!");
    }
}
*/

It seems to be an issue with the combination of the parent class that uses a custom AbstractValidator<TParent> with a property that uses a Validator attribute like ValidateNotEmpty on ListEntryChild. If you let the parent handle the rule exclusively and remove the ValidateNotEmpty attribute used on ListEntryChild that should get you going again with your upgrade. Will need to look into possible options if this combination can be added for future use, but for now the safest bet would be to switch let the parent class handle it if it is the only use of ListEntryChild or use its own AbstractValidator.

1 Like

Okay, so in practice it means that after going from ServiceStack 6 => 8, mixing declarative and fluentvalidation is a no go?
Or is it just an issue if a child property uses declarative validation?
Anyways, something like this seems to work fine (not sure if there’s something like a ServiceStack “best pratice” here).

I’m also seeing another issues with my integration tests after going from 6 => 8.
This is a bit harder to recreate in a short example as it uses xunit and fixtures, but I can do so if the error means absolutely nothing to you guys.
(If you want I can create a new post for this)

When I try to run my integration tests I now get this:

Exception discovering tests from <MyProject>.IntegrationTests: System.TypeLoadException: Method 'get_Options' in type '<MyProject>.IntegrationTests.AppHost' from assembly '<MyProject>.IntegrationTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.

The AppHost in my integration test is fairly straight forward I think, and looks something like this:

public class AppHost : AppSelfHostBase
{
    private InMemoryAuthRepository _authRepository;

    public AppHost() : base("ServiceTests", typeof(Service).Assembly) // so this points to a relevant service in my main project
    {
        _authRepository = new InMemoryAuthRepository();
    }

    public override void Configure(Container container)
    {
        Plugins.Add(new AuthFeature(() => new CustomUserSession(), new IAuthProvider[] { new CustomAuthProvider() }));

        RegisterXervices(container);

        RegisterYServices(container);

        MyClass myClass = new(container.Resolve<IXService>(), container.Resolve<IYService>());

        container.Register(myClass);

        LogManager.LogFactory = new SerilogFactory(new Serilog.LoggerConfiguration()
            .MinimumLevel.Information()
            .WriteTo.File(Path.Combine(AppSettings.Get("LogPath", "."), "log.txt"),
                rollingInterval: RollingInterval.Day)
            .CreateLogger());
    }

    private void RegisterXServices(Container container)
    {
        var connectionString = TestConfiguration.GetXConnectionString();

        OrmLiteConnectionFactory dbFactory = new OrmLiteConnectionFactory(connectionString, SqlServer2017Dialect.Provider);

        container.Register<IDbConnectionFactory>(c =>
        {
            return dbFactory;
        });

        XRepository xRepo = new(dbFactory);
        XService xService = new(xRepo);

        container.Register<IXRepository>(xRepo);
        container.Register<IXService>(xService);
    }

    private void RegisterYServices(Container container)
    {
        YConfig yConf = TestConfiguration.GetYConfig();

        YRepository yRepo = new YRepository(new YOptionsMonitorMock(yConf));

        ZCache zCache = new(yRepo);

        YCache yCache = new YCache(zCache);

        YService yService = new YService(yCache);

        container.Register<IAuthRepository>(_authRepository);
        container.Register<IYRepository>(yCache);
        container.Register<IYService>(yService);
    }

    protected override void Dispose(bool disposing)
    {
        _authRepository.Clear();
        base.Dispose(disposing);
    }
}

If this isn’t good enough I’ll try to recreate this and push it to the repository.

EDIT: I do have regular unit tests that still seem to work just fine FYI.
EDIT2: Seems to be an issue when trying to create a new migration with EntityFramework also. I.e. I get the same error about “get_Options” not having an implementation.

That sounds like dirty dependencies. Try doing a dotnet nuget locals all --clear and a full restore.

Currently, but I’ll look into the root cause of what changed and update this thread if resolved.

1 Like

That sounds like dirty dependencies. Try doing a dotnet nuget locals all --clear and a full restore.

That didn’t work.
And as mentioned this isn’t just when running those tests, but it happens when I run certain entityframework commands, like:

dotnet ef migrations add initial

TypeLoad and Missing Method exceptions are typically result from the use of dirty .dll’s which happens when you’re using different versions of ServiceStack packages together when they all should be referencing the same version, or preferably all using a 8.* wildcard, e.g:

<PackageReference Include="ServiceStack" Version="8.*" />

Options is a relatively new property, which suggests you’re referencing an older ServiceStack package version somewhere before it was added. I’d check all your ServiceStack packages in all projects to make sure they’re all using a 8.* wildcard version or the same fixed version.

We’ve added a potential fix for the Collection was modified issue that’s now available from v8.2.3+ that’s now available from the Pre Release packages.

1 Like

Ah! I of course found one single package that I’d forgotten to update to 8.2.3 …

-    <PackageReference Include="ServiceStack.Kestrel" Version="8.0.0" />
+    <PackageReference Include="ServiceStack.Kestrel" Version="8.2.3" />

Now everything seems to work fine, thanks!

I’ll have a look at the pre-release fix.
Using FluentValidation with SetValidator doesn’t feel too bad though, more explicit I guess.

1 Like