IGNITE-4904 .NET: DML Delete via LINQ This closes #2009
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/d38ca8b1 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/d38ca8b1 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/d38ca8b1 Branch: refs/heads/ignite-5075 Commit: d38ca8b1004effefa5126981f434649d26db1e68 Parents: 279f35b Author: Pavel Tupitsyn <ptupit...@apache.org> Authored: Fri May 26 13:02:01 2017 +0300 Committer: Pavel Tupitsyn <ptupit...@apache.org> Committed: Fri May 26 13:02:01 2017 +0300 ---------------------------------------------------------------------- .../Cache/Query/CacheLinqTest.cs | 70 +++++++++++++++- .../Apache.Ignite.Linq.csproj | 3 + .../Apache.Ignite.Linq/CacheExtensions.cs | 65 +++++++++++++++ .../Impl/CacheQueryModelVisitor.cs | 60 +++++++++++-- .../Apache.Ignite.Linq/Impl/CacheQueryParser.cs | 21 ++++- .../Impl/Dml/RemoveAllExpressionNode.cs | 88 ++++++++++++++++++++ .../Impl/Dml/RemoveAllResultOperator.cs | 61 ++++++++++++++ .../dotnet/Apache.Ignite.sln.DotSettings | 1 + 8 files changed, 359 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/d38ca8b1/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs index cb3fece..b603d75 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs @@ -37,6 +37,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query using Apache.Ignite.Core.Cache; using Apache.Ignite.Core.Cache.Configuration; using Apache.Ignite.Core.Cache.Query; + using Apache.Ignite.Core.Common; using Apache.Ignite.Linq; using NUnit.Framework; @@ -1269,7 +1270,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query var cache = GetPersonCache(); // Check regular query - var query = (ICacheQueryable) cache.AsCacheQueryable(new QueryOptions + var query = cache.AsCacheQueryable(new QueryOptions { Local = true, PageSize = 999, @@ -1277,7 +1278,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query Timeout = TimeSpan.FromSeconds(2.5), ReplicatedOnly = true, Colocated = true - }).Where(x => x.Key > 10); + }).Where(x => x.Key > 10).ToCacheQueryable(); Assert.AreEqual(cache.Name, query.CacheName); Assert.AreEqual(cache.Ignite, query.Ignite); @@ -1325,7 +1326,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query var distrQuery = cache.AsCacheQueryable(new QueryOptions {EnableDistributedJoins = true}) .Where(x => x.Key > 10 && x.Value.Age > 20 && x.Value.Name.Contains("x")); - query = (ICacheQueryable) distrQuery; + query = distrQuery.ToCacheQueryable(); Assert.IsTrue(query.GetFieldsQuery().EnableDistributedJoins); @@ -1424,6 +1425,69 @@ namespace Apache.Ignite.Core.Tests.Cache.Query } /// <summary> + /// Tests the RemoveAll extension. + /// </summary> + [Test] + public void TestRemoveAll() + { + // Use new cache to avoid touching static data. + var cache = Ignition.GetIgnite().CreateCache<int, Person>(new CacheConfiguration("deleteAllTest", + new QueryEntity(typeof(int), typeof(Person)))); + + Enumerable.Range(1, 10).ToList().ForEach(x => cache.Put(x, new Person(x, x.ToString()))); + + var queryable = cache.AsCacheQueryable(); + + Func<int[]> getKeys = () => cache.Select(x => x.Key).OrderBy(x => x).ToArray(); + + // Without predicate. + var res = queryable.Where(x => x.Key < 3).RemoveAll(); + Assert.AreEqual(2, res); + Assert.AreEqual(Enumerable.Range(3, 8), getKeys()); + + // With predicate. + res = queryable.RemoveAll(x => x.Key < 7); + Assert.AreEqual(4, res); + Assert.AreEqual(Enumerable.Range(7, 4), getKeys()); + + // Subquery-style join. + var ids = GetPersonCache().AsCacheQueryable().Where(x => x.Key == 7).Select(x => x.Key); + + res = queryable.Where(x => ids.Contains(x.Key)).RemoveAll(); + Assert.AreEqual(1, res); + Assert.AreEqual(Enumerable.Range(8, 3), getKeys()); + + // Row number limit. + res = queryable.Take(2).RemoveAll(); + Assert.AreEqual(2, res); + Assert.AreEqual(1, getKeys().Length); + + // Unconditional. + queryable.RemoveAll(); + Assert.AreEqual(0, cache.GetSize()); + + // Skip is not supported with DELETE. + var nex = Assert.Throws<NotSupportedException>(() => queryable.Skip(1).RemoveAll()); + Assert.AreEqual( + "RemoveAll can not be combined with result operators (other than Take): SkipResultOperator", + nex.Message); + + // Multiple result operators are not supported with DELETE. + nex = Assert.Throws<NotSupportedException>(() => queryable.Skip(1).Take(1).RemoveAll()); + Assert.AreEqual( + "RemoveAll can not be combined with result operators (other than Take): SkipResultOperator, " + + "TakeResultOperator, RemoveAllResultOperator", nex.Message); + + // Joins are not supported in H2. + var qry = queryable + .Where(x => x.Key == 7) + .Join(GetPersonCache().AsCacheQueryable(), p => p.Key, p => p.Key, (p1, p2) => p1); + + var ex = Assert.Throws<IgniteException>(() => qry.RemoveAll()); + Assert.AreEqual("Failed to parse query", ex.Message.Substring(0, 21)); + } + + /// <summary> /// Gets the person cache. /// </summary> /// <returns></returns> http://git-wip-us.apache.org/repos/asf/ignite/blob/d38ca8b1/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj index a7ec402..f4949d7 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj @@ -63,6 +63,8 @@ <Compile Include="Impl\CacheQueryExpressionVisitor.cs" /> <Compile Include="Impl\CacheQueryModelVisitor.cs" /> <Compile Include="Impl\CacheQueryParser.cs" /> + <Compile Include="Impl\Dml\RemoveAllExpressionNode.cs" /> + <Compile Include="Impl\Dml\RemoveAllResultOperator.cs" /> <Compile Include="Impl\ICacheQueryableInternal.cs" /> <Compile Include="Impl\MethodVisitor.cs" /> <Compile Include="Impl\QueryData.cs" /> @@ -83,6 +85,7 @@ <Name>Apache.Ignite.Core</Name> </ProjectReference> </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. http://git-wip-us.apache.org/repos/asf/ignite/blob/d38ca8b1/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs index 2c609c6..f759dbb 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs @@ -17,11 +17,15 @@ namespace Apache.Ignite.Linq { + using System; + using System.Diagnostics.CodeAnalysis; using System.Linq; + using System.Linq.Expressions; using Apache.Ignite.Core.Cache; using Apache.Ignite.Core.Cache.Configuration; using Apache.Ignite.Core.Impl.Common; using Apache.Ignite.Linq.Impl; + using Apache.Ignite.Linq.Impl.Dml; /// <summary> /// Extensions methods for <see cref="ICache{TK,TV}"/>. @@ -126,5 +130,66 @@ namespace Apache.Ignite.Linq return new CacheQueryable<TKey, TValue>(cache, queryOptions); } + + /// <summary> + /// Casts this query to <see cref="ICacheQueryable"/>. + /// </summary> + public static ICacheQueryable ToCacheQueryable<T>(this IQueryable<T> query) + { + IgniteArgumentCheck.NotNull(query, "query"); + + return (ICacheQueryable) query; + } + + /// <summary> + /// Removes all rows that are matched by the specified query. + /// <para /> + /// This method results in "DELETE FROM" distributed SQL query, performing bulk delete + /// (as opposed to fetching all rows locally). + /// </summary> + /// <typeparam name="TKey">Key type.</typeparam> + /// <typeparam name="TValue">Value type.</typeparam> + /// <param name="query">The query.</param> + /// <returns>Affected row count.</returns> + [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", + Justification = "Validation is present.")] + public static int RemoveAll<TKey, TValue>(this IQueryable<ICacheEntry<TKey, TValue>> query) + { + IgniteArgumentCheck.NotNull(query, "query"); + + var method = RemoveAllExpressionNode.RemoveAllMethodInfo.MakeGenericMethod(typeof(TKey), typeof(TValue)); + + return query.Provider.Execute<int>(Expression.Call(null, method, query.Expression)); + } + + /// <summary> + /// Deletes all rows that are matched by the specified query. + /// <para /> + /// This method results in "DELETE FROM" distributed SQL query, performing bulk delete + /// (as opposed to fetching all rows locally). + /// </summary> + /// <typeparam name="TKey">Key type.</typeparam> + /// <typeparam name="TValue">Value type.</typeparam> + /// <param name="query">The query.</param> + /// <param name="predicate">The predicate.</param> + /// <returns> + /// Affected row count. + /// </returns> + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", + Justification = "Only specified type of predicate is valid.")] + [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", + Justification = "Validation is present.")] + public static int RemoveAll<TKey, TValue>(this IQueryable<ICacheEntry<TKey, TValue>> query, + Expression<Func<ICacheEntry<TKey, TValue>, bool>> predicate) + { + IgniteArgumentCheck.NotNull(query, "query"); + IgniteArgumentCheck.NotNull(predicate, "predicate"); + + var method = RemoveAllExpressionNode.RemoveAllPredicateMethodInfo + .MakeGenericMethod(typeof(TKey), typeof(TValue)); + + return query.Provider.Execute<int>(Expression.Call(null, method, query.Expression, + Expression.Quote(predicate))); + } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/d38ca8b1/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index f566caa..12b9502 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -26,6 +26,7 @@ namespace Apache.Ignite.Linq.Impl using System.Linq; using System.Linq.Expressions; using System.Text; + using Apache.Ignite.Linq.Impl.Dml; using Remotion.Linq; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; @@ -100,22 +101,69 @@ namespace Apache.Ignite.Linq.Impl { _aliases.Push(); - // SELECT - _builder.Append("select "); + var hasDelete = VisitRemoveOperator(queryModel); - // TOP 1 FLD1, FLD2 - VisitSelectors(queryModel, includeAllFields); + if (!hasDelete) + { + // SELECT + _builder.Append("select "); + + // TOP 1 FLD1, FLD2 + VisitSelectors(queryModel, includeAllFields); + } // FROM ... WHERE ... JOIN ... base.VisitQueryModel(queryModel); - // UNION ... - ProcessResultOperatorsEnd(queryModel); + if (!hasDelete) + { + // UNION ... + ProcessResultOperatorsEnd(queryModel); + } _aliases.Pop(); } /// <summary> + /// Visits the remove operator. Returns true if it is present. + /// </summary> + private bool VisitRemoveOperator(QueryModel queryModel) + { + var resultOps = queryModel.ResultOperators; + + if (resultOps.LastOrDefault() is RemoveAllResultOperator) + { + _builder.Append("delete "); + + if (resultOps.Count == 2) + { + var resOp = resultOps[0] as TakeResultOperator; + + if (resOp == null) + { + throw new NotSupportedException( + "RemoveAll can not be combined with result operators (other than Take): " + + resultOps[0].GetType().Name); + } + + _builder.Append("top "); + BuildSqlExpression(resOp.Count); + _builder.Append(" "); + } + else if (resultOps.Count > 2) + { + throw new NotSupportedException( + "RemoveAll can not be combined with result operators (other than Take): " + + string.Join(", ", resultOps.Select(x => x.GetType().Name))); + } + + return true; + } + + return false; + } + + /// <summary> /// Visits the selectors. /// </summary> public void VisitSelectors(QueryModel queryModel, bool includeAllFields) http://git-wip-us.apache.org/repos/asf/ignite/blob/d38ca8b1/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs index 794ef2e..17ec0a3 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs @@ -18,10 +18,12 @@ namespace Apache.Ignite.Linq.Impl { using System.Threading; + using Apache.Ignite.Linq.Impl.Dml; using Remotion.Linq.Parsing.ExpressionVisitors.Transformation; using Remotion.Linq.Parsing.ExpressionVisitors.TreeEvaluation; using Remotion.Linq.Parsing.Structure; using Remotion.Linq.Parsing.Structure.ExpressionTreeProcessors; + using Remotion.Linq.Parsing.Structure.NodeTypeProviders; /// <summary> /// Cache query parser. @@ -49,12 +51,29 @@ namespace Apache.Ignite.Linq.Impl var proc = CreateCompoundProcessor(transformerRegistry); - var parser = new ExpressionTreeParser(ExpressionTreeParser.CreateDefaultNodeTypeProvider(), proc); + var parser = new ExpressionTreeParser(CreateNodeTypeProvider(), proc); return new QueryParser(parser); } /// <summary> + /// Creates the node type provider. + /// </summary> + private static INodeTypeProvider CreateNodeTypeProvider() + { + var methodInfoRegistry = MethodInfoBasedNodeTypeRegistry.CreateFromRelinqAssembly(); + + methodInfoRegistry.Register(RemoveAllExpressionNode.GetSupportedMethods(), + typeof(RemoveAllExpressionNode)); + + return new CompoundNodeTypeProvider(new INodeTypeProvider[] + { + methodInfoRegistry, + MethodNameBasedNodeTypeRegistry.CreateFromRelinqAssembly() + }); + } + + /// <summary> /// Creates CompoundExpressionTreeProcessor. /// </summary> private static CompoundExpressionTreeProcessor CreateCompoundProcessor( http://git-wip-us.apache.org/repos/asf/ignite/blob/d38ca8b1/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/RemoveAllExpressionNode.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/RemoveAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/RemoveAllExpressionNode.cs new file mode 100644 index 0000000..53674ac --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/RemoveAllExpressionNode.cs @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Linq.Impl.Dml +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using Apache.Ignite.Core.Cache; + using Remotion.Linq.Clauses; + using Remotion.Linq.Parsing.Structure.IntermediateModel; + + /// <summary> + /// Represents a <see cref="MethodCallExpression"/> for + /// <see cref="CacheLinqExtensions.RemoveAll{TKey,TValue}(IQueryable{ICacheEntry{TKey,TValue}})"/>. + /// When user calls RemoveAll, this node is generated. + /// </summary> + internal sealed class RemoveAllExpressionNode : ResultOperatorExpressionNodeBase + { + /** */ + private static readonly MethodInfo[] RemoveAllMethodInfos = typeof(CacheLinqExtensions) + .GetMethods().Where(x => x.Name == "RemoveAll").ToArray(); + + /// <summary> + /// The RemoveAll() method. + /// </summary> + public static readonly MethodInfo RemoveAllMethodInfo = + RemoveAllMethodInfos.Single(x => x.GetParameters().Length == 1); + + /// <summary> + /// The RemoveAll(pred) method. + /// </summary> + public static readonly MethodInfo RemoveAllPredicateMethodInfo = + RemoveAllMethodInfos.Single(x => x.GetParameters().Length == 2); + + /// <summary> + /// Initializes a new instance of the <see cref="RemoveAllExpressionNode"/> class. + /// </summary> + /// <param name="parseInfo">The parse information.</param> + /// <param name="optionalPredicate">The optional predicate.</param> + /// <param name="optionalSelector">The optional selector.</param> + public RemoveAllExpressionNode(MethodCallExpressionParseInfo parseInfo, + LambdaExpression optionalPredicate, LambdaExpression optionalSelector) + : base(parseInfo, optionalPredicate, optionalSelector) + { + // No-op. + } + + /** <inheritdoc /> */ + [ExcludeFromCodeCoverage] + public override Expression Resolve(ParameterExpression inputParameter, Expression expressionToBeResolved, + ClauseGenerationContext clauseGenerationContext) + { + throw CreateResolveNotSupportedException(); + } + + /** <inheritdoc /> */ + protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) + { + return new RemoveAllResultOperator(); + } + + /// <summary> + /// Gets the supported methods. + /// </summary> + public static IEnumerable<MethodInfo> GetSupportedMethods() + { + yield return RemoveAllMethodInfo; + yield return RemoveAllPredicateMethodInfo; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d38ca8b1/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/RemoveAllResultOperator.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/RemoveAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/RemoveAllResultOperator.cs new file mode 100644 index 0000000..76789cb --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/RemoveAllResultOperator.cs @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Linq.Impl.Dml +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Linq.Expressions; + using Apache.Ignite.Core.Cache; + using Remotion.Linq.Clauses; + using Remotion.Linq.Clauses.ResultOperators; + using Remotion.Linq.Clauses.StreamedData; + + /// <summary> + /// Represents an operator for <see cref="CacheLinqExtensions.RemoveAll{TK,TV}(IQueryable{ICacheEntry{TK,TV}})"/>. + /// </summary> + internal sealed class RemoveAllResultOperator : ValueFromSequenceResultOperatorBase + { + /** <inheritdoc /> */ + public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo) + { + return new StreamedScalarValueInfo(typeof(int)); + } + + /** <inheritdoc /> */ + [ExcludeFromCodeCoverage] + public override ResultOperatorBase Clone(CloneContext cloneContext) + { + return new RemoveAllResultOperator(); + } + + /** <inheritdoc /> */ + [ExcludeFromCodeCoverage] + public override void TransformExpressions(Func<Expression, Expression> transformation) + { + // No-op. + } + + /** <inheritdoc /> */ + [ExcludeFromCodeCoverage] + public override StreamedValue ExecuteInMemory<T>(StreamedSequence sequence) + { + throw new NotSupportedException("RemoveAll is not supported for in-memory sequences."); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/d38ca8b1/modules/platforms/dotnet/Apache.Ignite.sln.DotSettings ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.sln.DotSettings b/modules/platforms/dotnet/Apache.Ignite.sln.DotSettings index 078e9fb..9d5b728 100644 --- a/modules/platforms/dotnet/Apache.Ignite.sln.DotSettings +++ b/modules/platforms/dotnet/Apache.Ignite.sln.DotSettings @@ -7,4 +7,5 @@ <s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/QualifiedUsingAtNestedScope/@EntryValue">True</s:Boolean> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertClosureToMethodGroup/@EntryIndexedValue">DO_NOT_SHOW</s:String> + <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean> </wpf:ResourceDictionary> \ No newline at end of file