Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

110
Views
Implementación de respaldo nivel por nivel

Tengo una clase ScoreStrategy que describe cómo calcular puntos para un cuestionario:

 public class ScoreStrategy { public int Id { get; set; } public int QuizId { get; set; } [Required] public Quiz Quiz { get; set; } public decimal Correct { get; set; } public decimal Incorrect { get; set; } public decimal Unattempted { get; set; } }

Tres propiedades Correct , Incorrect y Unattempted describen cuántos puntos se asignarán a una respuesta. Estos puntos también pueden ser negativos. La estrategia de puntuación se aplica a todas las preguntas de la prueba, por lo que solo puede haber una estrategia de ScoreStrategy por prueba. Tengo dos subclases:

 public class DifficultyScoreStrategy : ScoreStrategy { public QuestionDifficulty Difficulty { get; set; } } public class QuestionScoreStrategy : ScoreStrategy { [Required] public Question Question { get; set; } }

Mis preguntas tienen tres niveles de dificultad ( Easy , Medium , Hard ; la dificultad de la QuestionDifficulty es una enumeración). DifficultyScoreStrategy especifica si los puntos para las preguntas de una dificultad específica deben asignarse de manera diferente. Esto anula la ScoreStrategy base que se aplica a todo el cuestionario. Puede haber una instancia por nivel de dificultad.

En tercer lugar, tengo una clase QuestionScoreStrategy que especifica si los puntos para una pregunta específica deben otorgarse de manera diferente. Esto anula tanto la ScoreStrategy de toda la prueba como la DifficultyStrategy de toda la DifficultyStrategy . Puede haber una instancia por pregunta.

Mientras evalúo las respuestas del cuestionario, quiero implementar un mecanismo de respaldo nivel por nivel :

Para cada pregunta:

  • Compruebe si hay una QuestionScoreStrategy para la pregunta y devuelva la estrategia si la encuentra.
  • De lo contrario, recurra a DifficultyScoreStrategy y verifique si hay una estrategia para el nivel de dificultad de la pregunta que se está evaluando y devuélvala si encuentra una estrategia.
  • De lo contrario, recurra a ScoreStrategy para todo el cuestionario y verifique si existe y devuélvalo si existe.
  • Si tampoco hay ScoreStrategy , use el valor predeterminado como { Correct = 1, Incorrect = 0, Unattempted = 0 } (Sería genial si pudiera hacer esto configurable también, algo muy parecido a la manera elegante de .NET:
 options => { options.UseFallbackStrategy( correct: 1, incorrect: 0, unattempted: 0 ); }

).

Resumen

He resumido la información anterior en una tabla:

Tipo de estrategia Prioridad Instancias máximas por prueba
QuestionScoreStrategy 1º (más alto) Tantas como preguntas haya en el cuestionario
DifficultyScoreStrategy 2do 4, uno para cada nivel de dificultad
ScoreStrategy 3ro Sólo uno
estrategia alternativa
(Predeterminado { Correct = 1, Incorrect = 0, Unattempted = 0} )
4to (más bajo) Configurado para toda la aplicación. Compartido por todos los cuestionarios

Tengo una clase contenedora llamada EvaluationStrategy que contiene estas estrategias de puntaje entre otra información de evaluación:

 partial class EvaluationStrategy { public int Id { get; set; } public int QuizId { get; set; } public decimal MaxScore { get; set; } public decimal PassingScore { get; get; } public IEnumerable<ScoreStrategy> ScoreStrategies { get; set; } }

Lo que he probado:

He agregado un método llamado GetStrategyByQuestion() a la misma clase EvaluationStrategy anterior (tenga en cuenta que se declara como partial ) que implementa este comportamiento alternativo y también un indexador complementario que a su vez llama a este método. He declarado dos HashSet s de tipos DifficultyScoreStrategy y QuestionScoreStrategy y un método Initialize() los instancia. Todas las estrategias de puntuación se cambian por tipo y se agregan al HashSet apropiado, solo puede haber una ScoreStrategy por prueba, que se almacenará en defaultStrategy :

 partial class EvaluationStrategy { private ScoreStrategy FallbackStrategy = new() { Correct = 1, Incorrect = 0, Unattempted = 0 }; private ScoreStrategy defaultStrategy; HashSet<DifficultyScoreStrategy> dStrategies; HashSet<QuestionScoreStrategy> qStrategies; public void Initialize() { qStrategies = new(); dStrategies = new(); // Group strategies by type foreach (var strategy in strategies) { switch (strategy) { case QuestionScoreStrategy qs: qStrategies.Add(qs); break; case DifficultyScoreStrategy ds: dStrategies.Add(ds); break; case ScoreStrategy s: defaultStrategy = s; break; } } } public ScoreStrategy this[Question question] => GetStrategyByQuestion(question); public ScoreStrategy GetStrategyByQuestion(Question question) { if (qStrategies is null || dStrategies is null) { Initialize(); } // Check if question strategy exists if (qStrategies.FirstOrDefault(str => str.Question.Id == question.Id) is not null and var qs) { return qs; } // Check if difficulty strategy exists if (dStrategies.FirstOrDefault(str => str.Question.Difficulty == question.Difficulty) is not null and var ds) { return ds; } // Check if default strategy exists if (defaultStrategy is not null) { return defaultStrategy; } // Fallback return FallbackStrategy; } }

Este método parece un poco torpe y no me parece correcto. Usar una clase parcial y agregar a EvalutationStrategy tampoco parece correcto. ¿Cómo implemento este comportamiento alternativo nivel por nivel? ¿Hay algún patrón/principio de diseño que pueda usar aquí? Sé muchas cosas en el marco de .NET de respaldo a las convenciones predeterminadas si no está configurado. necesito algo parecido ¿O alguien puede simplemente recomendar una solución más limpia y elegante con una mejor capacidad de mantenimiento?


NOTA/INFORMACIÓN ADICIONAL: ScoreStrategy y EvaluationStrategy para todos los cuestionarios se almacenan en una base de datos administrada por EF Core (.NET 5) con asignación de TPH:

 modelBuilder.Entity<ScoreStrategy>() .ToTable("ScoreStrategy") .HasDiscriminator<int>("StrategyType") .HasValue<ScoreStrategy>(0) .HasValue<DifficultyScoreStrategy>(1) .HasValue<QuestionScoreStrategy>(2) ; modelBuilder.Entity<EvaluationStrategy>().ToTable("EvaluationStrategy");

Tengo una sola base DbSet<ScoreStrategy> ScoreStrategies y otra DbSet<EvaluationStrategy> EvaluationStrategies . Dado que EvaluationStrategy es una clase de EF Core, soy un poco escéptico acerca de agregarle lógica ( GetStrategyByQuestion() ) también.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

con polly

Hay una biblioteca de terceros llamada Polly que define una política llamada Fallback .

Con este enfoque, puede definir fácilmente una cadena de respaldo como esta:

 public ScoreStrategy GetStrategyByQuestionWithPolly(Question question) { Func<ScoreStrategy, bool> notFound = strategy => strategy is null; var lastFallback = Policy<ScoreStrategy> .HandleResult(notFound) .Fallback(FallbackStrategy); var defaultFallback = Policy<ScoreStrategy> .HandleResult(notFound) .Fallback(defaultStrategy); var difficultyFallback = Policy<ScoreStrategy> .HandleResult(notFound) .Fallback(() => GetApplicableDifficultyScoreStrategy(question)); var fallbackChain = Policy.Wrap(lastFallback, defaultFallback, difficultyFallback); fallbackChain.Execute(() => GetApplicableQuestionScoreStrategy(question)); }

He extraído la lógica de selección de estrategia para QuestionScoreStrategy y DifficultyScoreStrategy de esta manera:

 private ScoreStrategy GetApplicableQuestionScoreStrategy(Question question) => qStrategies.FirstOrDefault(str => str.Question.Id == question.Id); private ScoreStrategy GetApplicableDifficultyScoreStrategy(Question question) => dStrategies.FirstOrDefault(str => str.Difficulty == question.Difficulty);

ventajas

  • Hay una sola declaración de return .
  • Las declaraciones de política están separadas del encadenamiento.
  • Todos y cada uno de los retrocesos pueden desencadenarse por diferentes condiciones
  • La lógica de selección primaria está separada de las alternativas

Contras

  • El código es muy repetitivo.
  • No puede crear una cadena de respaldo utilizando una API fluida
  • Necesita usar una biblioteca de terceros

sin polly

Si no desea usar una biblioteca de terceros solo para definir y usar una cadena de respaldo, haga algo como esto:

 public ScoreStrategy GetStrategyBasedOnQuestion(Question question) { var fallbackChain = new List<Func<ScoreStrategy>> { () => GetApplicableQuestionScoreStrategy(question), () => GetApplicableDifficultyScoreStrategy(question), () => defaultStrategy, () => FallbackStrategy }; ScoreStrategy selectedStrategy = null; foreach (var strategySelector in fallbackChain) { selectedStrategy = strategySelector(); if (selectedStrategy is not null) break; } return selectedStrategy; }

ventajas

  • Hay una sola declaración de return .
  • La declaración y la evaluación de la cadena de reserva están separadas
  • es sencillo y conciso

Contras

  • Es menos flexible: cada selección alternativa se activa por la misma condición
  • La selección primaria no está separada de las alternativas
over 4 years ago · Santiago Trujillo Report

0

Puede ordenar la secuencia de ScoringMethods según su prioridad.

Primero ordena si str is QuestionScoreStrategy y str.Question.Id == question.Id .

Luego ordena si str is DifficultyScoreStrategy y str.Question.Difficulty == question.Difficulty .

(Tenga en cuenta que dado que false viene antes que true , tendrá que invertir las condiciones)

Entonces puedes hacer FirstOrDefault() ?? defaultStrategy .

Ejemplo:

 var defaultStrategy = new() { Correct = 1, Incorrect = 0, Unattempted = 0 }; var selectedStrategy = Strategies.OrderBy(str => !(str is QuestionScoreStrategy questionStrat && questionStrat.Question.Id == question.Id) ).ThenBy(str => !(str is DifficultyScoreStrategy difficultySrat && difficultySrat.Difficulty == question.Difficulty) ).FirstOrDefault() ?? defaultStrategy;

Puede agregar fácilmente más "niveles" a esto agregando más cláusulas ThenBy .

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!