I want to use EF code first to create a column into a table Task, which is an array. How?
public class Task
{
// my presumption code
public string[] Attempts { get; set; }
The Attempts has
AttemptsMetadata---maybe string
Time ---DataTime
Answered ---bool
Create a property to be used in the code (and mark as ignore) and other property to be used in code.
EDITED
public class Task
{
[Ignore]
public string[] Attempts { get; set; }
public string AttemptsMetadata
{
get
{
return Attempts != null && Attempts.Any()
? Attempts.Aggregate((ac, i) => ";" + ac + i).Substring(1)
: null;
}
set { Attempts = value.Split(';'); }
}
}
PS:
This strategy has a one flaw. When you use repository expressions you cannot use the ignore property. But I never find another way to do so.
Related
I have a class ScoreStrategy that describes how to calculate points for a quiz:
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; }
}
Three properties Correct, Incorrect and Unattempted describe how many points to be assigned for a response. These points can also be negative. The score strategy applies to all questions in the quiz, thus there can only be one ScoreStrategy per quiz.
I have two subclasses:
public class DifficultyScoreStrategy : ScoreStrategy
{
public QuestionDifficulty Difficulty { get; set; }
}
public class QuestionScoreStrategy : ScoreStrategy
{
[Required]
public Question Question { get; set; }
}
My questions have three difficulty levels(Easy, Medium, Hard; QuestionDifficulty is an enum). The DifficultyScoreStrategy specifies if points for questions of a specific difficulty need to be assigned differently. This overrides the base ScoreStrategy that applies to the entire quiz. There can be one instance per difficulty level.
Thirdly, I have a QuestionScoreStrategy class that specifies if points for a specific question have to be awarded differently. This overrides both the quiz-wide ScoreStrategy and the difficulty-wide DifficultyStrategy. There can be one instance per question.
While evaluating the responses of the quiz, I want to implement a level-by-level fallback mechanism:
For each question:
Check if there is a QuestionScoreStrategy for the question and return the strategy if one is found.
If not, fallback to DifficultyScoreStrategy and check if there is a strategy for the difficulty level of the question being evaluated
and return it if a strategy is found.
If not, fallback to the quiz-wide ScoreStrategy and check if one exists and return it if it does,
If there is no ScoreStrategy either, use default as { Correct = 1, Incorrect = 0, Unattempted = 0 }(It would be great if I can make this configurable as well, something much like the .NET's elegant way:
options => {
options.UseFallbackStrategy(
correct: 1,
incorrect: 0,
unattempted: 0
);
}
).
Summary
I've summarized the above info in a table:
Strategy Type
Priority
Maximum instances per quiz
QuestionScoreStrategy
1st (highest)
As many as there are questions in the quiz
DifficultyScoreStrategy
2nd
4, one for each difficulty level
ScoreStrategy
3rd
Only one
Fallback strategy (Default { Correct = 1, Incorrect = 0, Unattempted = 0})
4th (lowest)
Configured for the entire app. Shared by all quizzes
I have a container class called EvaluationStrategy that holds these score strategies among other evaluation info:
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; }
}
What I have tried:
I have added a method called GetStrategyByQuestion() to the same EvaluationStrategy class above(note it is declared as partial) that implements this fallback behavior and also a companion indexer that in turn calls this method. I have declared two HashSets of types DifficultyScoreStrategy and QuestionScoreStrategy and an Initialize() method instantiates them. All the score strategies are then switched by type and added to the appropriate HashSet, there can only be one ScoreStrategy per quiz, which will be stored in 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;
}
}
This method seems a bit clumsy and doesn't quite feel right to me. Using a partial class and adding to EvalutationStrategy doesn't seem right either. How do I implement this level-by-level fallback behavior? Is there a design pattern/principle I can use here? I know many things in the .NET framework fallback to default conventions if not configured. I need something similar. Or can someone simply recommend a cleaner and elegant solution with better maintainability?
NOTE/ADDITIONAL INFO: The ScoreStrategys and EvaluationStrategy for all quizzes are stored in a database managed by EF Core(.NET 5) with TPH mapping:
modelBuilder.Entity<ScoreStrategy>()
.ToTable("ScoreStrategy")
.HasDiscriminator<int>("StrategyType")
.HasValue<ScoreStrategy>(0)
.HasValue<DifficultyScoreStrategy>(1)
.HasValue<QuestionScoreStrategy>(2)
;
modelBuilder.Entity<EvaluationStrategy>().ToTable("EvaluationStrategy");
I have a single base DbSet<ScoreStrategy> ScoreStrategies and another DbSet<EvaluationStrategy> EvaluationStrategies. Since EvaluationStrategy is an EF Core class, I'm a bit skeptical about adding logic(GetStrategyByQuestion()) to it as well.
With Polly
There is a 3rd party library called Polly which defines a policy called Fallback.
With this approach you can easily define a fallback chain like this:
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));
}
I've extracted the strategy selection logic for QuestionScoreStrategy and DifficultyScoreStrategy like this:
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);
Pros
There is a single return statement
The policy declarations are separated from chaining
Each and every fallback can be triggered by different conditions
Primary selection logic is separated from the fallbacks
Cons
The code is really repetitive
You can't create a fallback chain by utilizing a fluent API
You need to use a 3rd party library
Without Polly
If you don't want to use a 3rd party library just to define and use a fallback chain you do something like this:
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;
}
Pros
There is a single return statement
The fallback chain declaration and evaluation are separated
It is simple and concise
Cons
It is less flexible: each fallback selection is triggered by the same condition
Primary selection is not separated from fallbacks
You can sort the sequence of ScoringMethods by your priority.
First you sort by whether str is QuestionScoreStrategy and str.Question.Id == question.Id.
Then you sort by whether str is DifficultyScoreStrategy and str.Question.Difficulty == question.Difficulty.
(Note that since false comes before true, you'll have to invert the conditions)
Then you can just do FirstOrDefault() ?? defaultStrategy.
Example:
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;
You can easily add more "levels" to this by adding more ThenBy clauses.
I imagine that all data (questions, strategies, quizes is stored in database). Then I would expect such ways of getting each strategy:
Question strategy
var questionStrategy = dbContext.ScoreStrategies.SingleOrDefault(ss => ss.QuesionId == question.Id);
Difficulty strategy:
var difficultyStrategy = dbContext.ScoreStrategies.SingleOrDefault(ss => ss.Difficulty == question.Difficulty);
Default strategy for quiz:
var quizStrategy = dbContext.ScoreStrategies.SingleOrDefault(ss => ss.QuizId == question.QuizId)
Building on this and what you already provided, strategy is just three numbers: points for correct answer, points for incorrect and unattempted answer.
So this makes perfect candidate for abstract class, which would serve for base class for three entities - three types of strategy - those will be three tables, because each has different relations:
public abstract class ScoreStrategy
{
public double Correct { get; set; }
public double Incorrect { get; set; }
public double Unattempted { get; set; }
}
// Table with FK relation to Questions table
public class QuestionScoreStrategy : ScoreStrategy
{
public Question { get; set; }
public int QuestionId { get; set; }
}
// If you have table with difficulties, there should be FK relation to it.
// If you do not have table - it's worth consideration, you could then
// easily add more difficulties.
public class DifficultyStrategy : ScoreStrategy
{
public QuestionDifficulty Difficulty { get; set; }
}
// FK relation to Quizes table
public class QuizScoreStrategy : ScoreStrategy
{
public Quiz { get; set; }
public int QuizId { get; set; }
}
This way you end up with well grained tables that stores only relevant data.
Then, usage would become:
// Ideally, this method should be in some repoistory (look at repository design pattern) in data access layer
// and should leverage usage of async / await as well.
public ScoreStrategy GetScoreStrategy(Question question)
{
return dbContext.QuestionScoreStrategies.SingleOrDefault(qs => qs.QuestionId == question.Id)
?? dbContext.DifficultyStrategies.SingleOrDefault(ds => ds.Difficulty == question.Difficulty)
?? dbContext.QuizScoreStrategies.SingleOrDefault(qs => qs.QuizId == question.QuizId);
}
Then you could use this method in such way:
// This should be outside data access layer. Here you perform logic of getting question.
// This could be some ScoringManager class which should be singleton (one instance only).
// Then you could define fallback in private fields:
private readonly double FALLBACK_CORRECT_SCORE;
private readonly double FALLBACK_INCORRECT_SCORE;
private readonly double FALLBACK_UNATTEMPTED_SCORE;
// private constructor, as this should be singleton
private ScoringManager(double correctScore, double incorrectScore, double unattemptedScore)
=> (FALLBACK_CORRECT_SCORE, FALLBACK_INCORRECT_SCORE, FALLBACK_UNATTEMPTED_SCORE) =
(correctScore, incorrectScore, unattemptedScore);
public double CalcScoreForQuestion(Question question)
{
var scoreStrategy = GetScoreStrategy(question);
if (question answered correctly)
return scoreStrategy?.Correct ?? FALLBACK_CORRECT_SCORE;
if (question answered incorrectly)
return scoreStrategy?.Incorrect ?? FALLBACK_INCORRECT_SCORE;
if (question unattempted)
return scoreStrategy?.Unattempted ?? FALLBACK_UNATTEMPTED_SCORE;
}
NOTE
This is just the draft how I would organize things and most probably when writing code I would come up with improvements, but I think this is direction to go. For example ScoringManager could have ConfigureFallbackScore method, which would allow dynamically changing fallback scores (this would require making respective fields not readonly).
UPDATE
Define fallback strategy, in order to do that define enum:
public enum FallbackLevel
{
Difficulty,
Question,
Quiz,
}
Then scoring manager could have method to configure strategy (together with backing fields):
private FallbackLevel _highPrecedence;
private FallbackLevel _mediumPrecedence;
private FallbackLevel _lowPrecedence;
public void ConfigureFallbackStrategy(FallbackLevel highPrecedence, FallbackLevel mediumPrecedence, FallbackLevel lowPrecedence)
{
_highPrecedence = highPrecedence;
_mediumPrecedence = mediumPrecedence;
_lowPrecedence = lowPrecedence;
}
Then we would write getting strategy logic in manager:
public ScoreStrategy GetScoreStrategy(Question question)
{
var scoreStrategy = GetScoreStrategy(_highPrecedence, question)
?? GetScoreStrategy(_mediumPrecedence, question)
?? GetScoreStrategy(_lowPrecedence, question);
}
private ScoreStrategy GetScoreStrategy(FallbackLevel lvl, Question question) => lvl switch
{
FallbackLevel.Difficulty => dbContext.DifficultyStrategies.SingleOrDefault(ds => ds.Difficulty == question.Difficulty),
FallbackLevel.Question => dbContext.QuestionScoreStrategies.SingleOrDefault(qs => qs.QuestionId == question.Id),
FallbackLevel.Quiz => dbContext.QuizScoreStrategies.SingleOrDefault(qs => qs.QuizId == question.QuizId),
}
This way it is super easy to configure fallback strategy any way you want. Of course, there are some considerations still:
make sure that all fallback strategies are unique, so for example it is impossible to have high, medium and low startegy the same,
db context should be accessed only via repository pattern
add some more sanity checks (like nulls etc.)
I omitted those parts, as I focused on sheer functionality.
I've currently got a class that has multiple string properties. One or more of them may have a value. What I need to do is get the first non-empty property, based off some priority. Here is an example of what I envision the class would look like:
public class MyClass
{
[Priority(1)]
public string HighestPriority { get; set; }
[Priority(2)]
public string MiddlePriority { get; set; }
[Priority(3)]
public string LowestPriority { get; set; }
}
Or, maybe use another enum property that can be used to determine the highest one that is set?
public enum Priority
{
HighestPriority,
MiddlePriority,
LowestPriority
}
public class MyClass
{
private string highestPriority;
public string HighestPriority
{
get;
set
{
highestPriority = value;
HighestSetProperty = Priority.HighestPriority;
}
}
private string middlePriority;
public string MiddlePriority
{
get;
set
{
middlePriority = value;
if (HighestSetProperty != Priority.HighestPriority)
HighestSetProperty = Priority.MiddlePriority;
}
}
private string lowestPriority;
public string LowestPriority
{
get;
set
{
lowestPriority = value;
if (HighestSetProperty != Priority.HighestPriority || HighestSetProperty != Priority.MiddlePriority)
HighestSetProperty = Priority.LowestPriority;
}
}
public Priority HighestSetProperty { get; set; }
}
Then, use HighestSetProperty to set the string in a switch statement?
So far though, the only way I know of to find the first non-empty property, without using some form of priority attribute like above, is something like this:
string highestPriorityProp = String.IsNullOrWhitespace(HighestPriority) ? (String.IsNullOrWhitespace(MiddlePriority) ? LowestPriority : MiddlePriority) : HighestPriority;
Which is obviously messy and doesn't scale well if more properties are added.
So, what is the best way (in terms of readability, speed and scalability) of doing this? Thanks in advance.
EDIT: Let's go for cleanliness and scalability over speed.
EDIT: The duplicate question doesn't actually answer my question. As the properties may not be in the order of priority. Hence, the first one that is looped through may be a lower priority than the highest one set.
For instance:
public class MyClass
{
public string MiddlePriority { get; set; }
public string HighPriority { get; set; }
}
EDIT: Thanks for the help everyone. As mjwills and I have discussed in the comments, The accepted answer would suit my needs as I have only around 6 properties in my class. But, if there were more, the duplicate answer is probably the best way to go.
Normally you can do something like this with the coalesce operator:
var highest = high ?? medium ?? low;
But it seems you want to treat "null" and "empty string" the same. So write a function that converts empty strings to nulls:
Func<string,string> func = s => s == "" ? null : s;
Then coalesce over that:
var highest = func(HighestPriority) ?? func(MediumPriority) ?? func(LowPriority);
I recommend this over using attributes and reflection. Yes you'll have to modify the code when you add a new property, but you're already modifying the code to add a property itself-- there is little point is making only half the solution automatic. While using a "flexible" solution and Reflection is a common temptation, in my experience it is usually counterproductive in the long term.
If you are married to the idea of scaling and adsorbing more and more properties automatically, I suggest using a List or Dictionary and using something like First().
I would go with John Wus answer and use coalesce operator, but there are other possiblities to get yourself a null instead of an empty string:
Create a extensionmethods class and extend the 'string' class:
public static class ExtensionMethod
{
public static string ConvertEmptyToNull(this string str)
{
return string.IsNullOrEmpty(str) ? null : str;
}
}
Than use coalesce operator:
var highest = HighestPriority.ConvertEmptyToNull() ?? MediumPriority.ConvertEmptyToNull() ?? LowPriority.ConvertEmptyToNull();
But in your case i would rather implement the getter of your properties since you have private fields for your properties.
private string highestPriority;
public string HighestPriority
{
get
{
return string.IsNullOrEmpty(highestPriority) ? null : highestPriority;
}
set
{
highestPriority = value;
HighestSetProperty = Priority.HighestPriority;
}
}
Now your coalesce chain will look cleaner:
var highest = HighestPriority ?? MediumPriority ?? LowPriority;
New here, I've been learning c# for about a month.
Anyway, I've been searching StackOverflow for a couple of days now and couldn't find a specific answer to my problem...
//Here's my Class
public class Guy
{
public static int ID { get; set; }
public static int LifeExpectancy { get; set; }
public static bool Living { get; set; }
public Guy(int id, int lifeExpectancy, bool living)
{
ID = id;
LifeExpectancy = lifeExpectancy;
Living = living;
}
}
What I'm trying to do is create a specific number of "someGuy" objects to then put them into a public list using this method...
public static List<Guy> Guys = new List<Guy>();
public static void makeSomeGuys(int howManyGuys)
{
for (int i = 0, i <= howManyGuys; i++)
{
int id = i;
int lifeExpectancy = 80;
bool alive = true;
Guys.Add(New Guy(id, lifeExpectancy, alive));
Console.WriteLine("Made a new Guy {0}", id);
}
return;
}
Questions in order of importance:
How do I access a specific object as well as its parameters? (Accessing from the list "Guys".)
How do I access an object from this list in another class? (Not that I absolutely need to, I'm curious)
Can I search for an object in a list by using its parameters? (As opposed to doing something like... humanPopulation[number])
Should I create a new list for objects that have had their parameters modified? (As opposed to leaving it in the original list)
Is it possible to remove items from a list? (Just in general, is that a thing people do? if so, why?)
I really only need the first question answered. The rest of them are just a bonus. Thanks!
First you need to remove the static modifier from the properties of the Guy class, i.e.:
public int ID { get; set; }
public int LifeExpectancy { get; set; }
public bool Living { get; set; }
because static causes the property to be an attribute of the class itself, rather than the instances of the class (the individual 'guys').
To access life expectancy of the first guy (the zeroth):
Console.WriteLine(Guys[0].LifeExpectancy);
To access life expectancy of the fifth guy:
Console.WriteLine(Guys[4].LifeExpectancy);
using System;
using System.Collections.Generic;
using System.Linq;
namespace test
{
public class Guy
{
private int m_ID;
private int m_LifeExpectancy;
private bool m_Living;
public int ID
{
get { return m_ID; }
set { m_ID = value; }
}
public int LifeExpectancy
{
get { return m_LifeExpectancy; }
set { m_LifeExpectancy = value; }
}
public bool Living
{
get { return m_Living; }
set { m_Living = value; }
}
public Guy(int id, int lifeExpectancy, bool living)
{
ID = id;
LifeExpectancy = lifeExpectancy;
Living = living;
}
}
public class MyFactory
{
public IList<Guy> MakeSomeGuys(int howManyGuys)
{
IList<Guy> localGuys = new List<Guy>();
for (int i = 0; i <= howManyGuys; i++)
{
int id = i;
int lifeExpectancy = 80;
bool alive = true;
localGuys.Add(new Guy(id, lifeExpectancy, alive));
Console.WriteLine("Made a new Guy {0}", id);
}
return localGuys;
}
}
public class program
{
public void Main()
{
MyFactory mf = new MyFactory();
IList<Guy> guys = mf.MakeSomeGuys(5);
//How do I access a specific object as well as its parameters? (Accessing from the list "Guys".)
int GetFirstGuyId = guys.FirstOrDefault().ID; //LEARN LINQ
//How do I access an object from this list in another class? (Not that I absolutely need to, I'm curious)
//you need to learn about object oriented encapsulation for better understanding.
//Can I search for an object in a list by using its parameters? (As opposed to doing something like...humanPopulation[number])
Guy guyById = guys.Where(g => g.ID == 5).FirstOrDefault(); // returns the first match (need to learn lambda expression)
//Should I create a new list for objects that have had their parameters modified? (As opposed to leaving it in the original list)
// you need to learn about passing values by value / reference (by reference you already changing the original!).
//Is it possible to remove items from a list? (Just in general, is that a thing people do? if so, why?)
//yes
guys.Remove(guyById);
}
}
}
You're likely new to C# and OO programming, so I've included some good links in this answer.
Regarding question 1 only:
Firstly, your Guy class properties aren't properly encapsulated. Make sure you properly scope the ID, LifeExpectancy and Living properties like shown in this article.
If you'd like to access a specific item, that is, a Guy with a particular ID, you'd be better off using an associative container like Dictionary.
If you're happy with the List container, you need to use the Find method on Guys as shown in the example at the link. You'll notice the term Predicate in the documentation, this link will elaborate.
I'm using EF4.3 so I'm referring to entities, however it could apply to any class containing properties.
I'm trying to figure out if its possible to compare 2 entities. Each entity has properties that are assigned values for clarity let say the entity is 'Customer'.
public partial class Customer
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
The customer visits my website and types in some details 'TypedCustomer'. I check this against the database and if some of the data matches, I return a record from the database 'StoredCustomer'.
So at this point I've identified that its the same customer returning but I wan't to valid the rest of the data. I could check each property one by one, but there are a fair few to check. Is it possible to make this comparison at a higher level which takes into account the current values of each?
if(TypedCustomer == StoredCustomer)
{
.... do something
}
If you're storing these things in the database, it is logical to assume you'd also have a primary key called something like Id.
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
Then all you do is:
if(TypedCustomer.Id == StoredCustomer.Id)
{
}
UPDATE:
In my project, I have a comparer for these circumstances:
public sealed class POCOComparer<TPOCO> : IEqualityComparer<TPOCO> where TPOCO : class
{
public bool Equals(TPOCO poco1, TPOCO poco2)
{
if (poco1 != null && poco2 != null)
{
bool areSame = true;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object v1 = property.GetValue(poco1, null);
object v2 = property.GetValue(poco2, null);
if (!object.Equals(v1, v2))
{
areSame = false;
break;
}
});
return areSame;
}
return poco1 == poco2;
} // eo Equals
public int GetHashCode(TPOCO poco)
{
int hash = 0;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object val = property.GetValue(poco, null);
hash += (val == null ? 0 : val.GetHashCode());
});
return hash;
} // eo GetHashCode
} // eo class POCOComparer
Uses an extension method:
public static partial class TypeExtensionMethods
{
public static PropertyInfo[] GetPublicProperties(this Type self)
{
self.ThrowIfDefault("self");
return self.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where((property) => property.GetIndexParameters().Length == 0 && property.CanRead && property.CanWrite).ToArray();
} // eo GetPublicProperties
} // eo class TypeExtensionMethods
Most simple seems to use reflexion : get the properties and/or fields you want to compare, and loop through them to compare your two objects.
This will be done with getType(Customer).getProperties and getType(Customer).getFields, then using getValue on each field/property and comparing.
You might want to add custom informations to your fields/properties to define the ones that needs
comparing. This could be done by defining a AttributeUsageAttribute, that would inherit from FlagsAttribute for instance. You'll then have to retrieve and handle those attributes in your isEqualTo method.
I don't think there's much of a purpose to checking the entire object in this scenario - they'd have to type every property in perfectly exactly as they did before, and a simple "do they match" doesn't really tell you a lot. But assuming that's what you want, I can see a few ways of doing this:
1) Just bite the bullet and compare each field. You can do this by overriding the bool Equals method, or IEquatable<T>.Equals, or just with a custom method.
2) Reflection, looping through the properties - simple if your properties are simple data fields, but more complex if you've got complex types to worry about.
foreach (var prop in typeof(Customer).GetProperties()) {
// needs better property and value validation
bool propertyMatches = prop.GetValue(cust1, null)
.Equals(prop.GetValue(cust2, null));
}
3) Serialization - serialize both objects to XML or JSON, and compare the strings.
// JSON.NET
string s1 = JsonConvert.SerializeObject(cust1);
string s2 = JsonConvert.SerializeObject(cust2);
bool match = s1 == s2;
I have simple scenario where I have AnotherTest value based on Test value. This works fine most of the time so that whenever I provide Test I am sure to get AnotherTest easily.
public sealed class Transaction {
public string Test { get;set; }
public string AnotherTest{
get {
int indexLiteryS = Test.IndexOf("S");
return Test.Substring(indexLiteryS, 4);
}
}
}
However I wanted to be able to also set AnotherTest value and be able to read it without having to provide Test value. Is this possible? So kinda 2 types of get based which way it was set. I know I could create 3rdTest but I have some methods that use AnotherTest and other fields and I would have to write overloads of that methods.
Edit:
I read some file supplied by bank. I cut it in pieces put some stuff in Test value and every other field (AnotherTest and similar) of the Transaction gets filled automatically.
However later on I would like to read Transaction from SQL that is already in nice format so I don't need to provide Test to get the rest of the fields. I would like to set those fields with set and then be able to use get without setting Test value.
Yes, like so:
public string Test { get; set; }
public string AnotherTest
{
get
{
if(_anotherTest != null || Test == null)
return _anotherTest;
int indexLiteryS = Test.IndexOf("S")
return Test.Substring(indexLiteryS, 4);
}
set { _anotherTest = value; }
}
private string _anotherTest;
That getter could also be expressed as
return (_anotherTest != null || Test == null)
? _anotherTest
: Test.Substring(Test.IndexOf("S"), 4);
I think this would do what you want it to do:
public sealed class Transaction {
public string Test { get;set; }
public string AnotherTest{
get {
if (_anotherTest != null)
{
return _anotherTest;
}
else
{
int indexLiteryS = Test.IndexOf("S");
return Test.Substring(indexLiteryS, 4);
}
}
set {
_anotherTest = value;
}
}
private string _anotherTest = null;
}
I would suggest turning the problem over.
It sounds like you're dealing with a big field and subfields within it. Instead, how about promoting those subfields to fields and constructing/deconstructing the big field when it's accessed.