List in a List (Associations) - c#

Trying to further my own understanding, I'm replication a simple database - and having trouble understanding the following;
I have 2 classes Town and People. A town owns many instances of People and are set like this;
public class Town
{
List<People> collectionOfPeople;
public string townName { get; set; }
public Town()
{
townName = "Cardiff";
collectionOfPeople = new List<People>();
collectionOfPeople.Add(new People("Daniel Smith"));
}
}
public class People
{
public string name { get; set; }
public People(string tmp_name)
{
name = tmp_name;
}
}
Assuming what I've done is correct, Town has 1 value (Cardiff) and People also has one (Daniel Smith) or .. Daniel lives in Cardiff.
I am trying to display the names of People living within the Town.. to later cycle through them. (** = problem I think)
private List<Town> townList;
private List<Town.People> peopleList; **
private void ShowData()
{
// Add to Text Box based on current Record
txt_town.Text = townList[0]).townName;
txt_name.Text = peopleList[0].name; **
}

Here are my changes. Provide public access modifier for collectionOfPeople in Town class.
public class Town
{
public List<People> collectionOfPeople;
public string townName { get; set; }
}
After that, you can access People instance within Town. Something like this:
private List<Town> townList = new List<Town>();
private void ShowData()
{
// Add to Text Box based on current Record
txt_town.Text = townList[0].townName;
txt_name.Text = townList[0].collectionOfPeople[0].name
}

You haven't said what, if any, error messages you're getting but I believe in order to have
Town.People
you need to create a property of your Town class called People. I don't see that in your code.
Also, there's an extra parenthesis in your line:
txt_town.Text = townList[0].townName: //no ) after [0]

Related

DDD modelling of articles, variants and their parameters

I am learning DDD and trying to model articles, its variants and parameters.
Article can be on it's own without variants
Variant must be child of an article
both article and variant can have some parameters (colors, brands, sizes...), physical quantities (width, length, some article-specific like inner length)
If you set some parameter on an article, it can be "synchronized" to it's children variants
you can override this in a variant by setting that parameter as "unlinked", then this variant would have different parameter value than article
some parameters can be set multiple times (color: red, blue), but some only once (brand)
those parameters are dynamically create, it's not a Color or Brand property but key-value selected from preconfigured values
I think my main aggregate roots will be Article and Variant.
My current code looks like this:
internal class Article : AggregateRoot<ArticleId>
{
private readonly ISet<VariantId> _variants = new HashSet<VariantId>();
private readonly ISet<AssignedParameter> _parameters = new HashSet<AssignedParameter>();
private readonly ISet<AssignedPhysicalQuantity> _physicalQuantities = new HashSet<AssignedPhysicalQuantity>();
public string Name { get; private set; }
public string Catalog { get; private set; }
public IReadOnlySet<VariantId> Variants => _variants.AsReadOnly();
public IReadOnlySet<AssignedParameter> Parameters => _parameters.AsReadOnly();
public IReadOnlySet<AssignedPhysicalQuantity> PhysicalQuantities => _physicalQuantities.AsReadOnly();
private Article(ArticleId id, string name, string catalog)
: base(id)
{
Name = name;
Catalog = catalog;
}
public static Article Register(ArticleId id, string name, string catalog)
{
var article = new Article(id, name, catalog);
article.AddEvent(new ArticleRegistered(article.Id, article.Name, article.Catalog));
return article;
}
public void AssignParameter(Parameter parameter, ParameterValue parameterValue, bool syncToVariants)
{
if (!parameter.CanBeAssignedMultipleTimes && _parameters.Any(p => p.ParameterId == parameter.Id))
{
throw new ParameterCanBeAssignedOnlyOnceException($"Parameter {parameter.Id} can by assigned only once.");
}
var assignedParameter = new AssignedParameter(parameter.Id, parameterValue.Id, syncToVariants);
if (!_parameters.Add(assignedParameter))
{
throw new ParameterIsAlreadyAssignedException($"Parameter {parameter.Id} with value {parameterValue.Id} is already assigned.");
}
AddEvent(new ArticleParameterAssigned(Id, assignedParameter.ParameterId, assignedParameter.ParameterValueId));
}
public void UnassignParameter(Parameter parameter, ParameterValue parameterValue)
{
var assignedParameter = _parameters.FirstOrDefault(p => p.ParameterId == parameter.Id && p.ParameterValueId == parameterValue.Id);
if (assignedParameter is null)
{
throw new ParameterIsNotAssignedException($"Parameter {parameter.Id} is not assigned.");
}
_parameters.Remove(assignedParameter);
AddEvent(new ArticleParameterUnassigned(Id, assignedParameter.ParameterId, assignedParameter.ParameterValueId));
}
// physical quantity assign / unassign are similar to parameters
}
internal class Variant : AggregateRoot<VariantId>
{
private readonly ISet<AssignedParameter> _parameters = new HashSet<AssignedParameter>();
private readonly ISet<AssignedPhysicalQuantity> _physicalQuantities = new HashSet<AssignedPhysicalQuantity>();
public string Name { get; private set; }
public string Catalog { get; private set; }
public EanCode Ean { get; private set; }
public decimal Weight { get; private set; }
public IReadOnlySet<AssignedParameter> Parameters => _parameters.AsReadOnly();
public IReadOnlySet<AssignedPhysicalQuantity> PhysicalQuantities => _physicalQuantities.AsReadOnly();
internal Variant(VariantId id, string name, string catalog, EanCode ean, decimal weight)
: base(id)
{
Name = name;
Catalog = catalog;
Ean = ean;
Weight = weight;
}
// parameter and physical quantity assignment methods
}
Parameters:
internal class Parameter : AggregateRoot<ParameterId>
{
private readonly ISet<ParameterValue> _values = new HashSet<ParameterValue>();
public string Code { get; private set; }
public string Name { get; private set; }
public bool CanBeAssignedMultipleTimes { get; private set; }
public IReadOnlySet<ParameterValue> Values => _values.AsReadOnly();
public Parameter(ParameterId id, string code, string name, bool canBeAssignedMultipleTimes)
: base(id)
{
Code = code;
Name = name;
CanBeAssignedMultipleTimes = canBeAssignedMultipleTimes;
}
}
internal class ParameterValue : Entity<ParameterValueId>
{
public string Code { get; private set; }
public string Name { get; private set; }
public Parameter Parameter { get; private init; } = null!;
public ParameterValue(ParameterValueId id, string code, string name)
: base(id)
{
Code = code;
Name = name;
}
}
Value objects:
// for Article, variant doesn't have SyncToVariants property and has some other
internal class AssignedParameter : ValueObject
{
public ParameterId ParameterId { get; private init; }
public ParameterValueId ParameterValueId { get; private init; }
public bool SyncToVariants { get; private init; }
public AssignedParameter(ParameterId parameterId, ParameterValueId parameterValueId, bool syncToVariants)
{
ParameterId = parameterId;
ParameterValueId = parameterValueId;
SyncToVariants = syncToVariants;
}
protected override IEnumerable<object> GetEqualityComponents()
{
yield return ParameterId;
yield return ParameterValueId;
}
}
internal class AssignedPhysicalQuantity : ValueObject { ... }
My questions:
What would be the best way to notify variants of the parameter change? I can think of two ways using events.
First would be using ArticleParameterChanged(ArticleId, parameter.Id, parameterValue.Id). I would handle this event and changed all variants at once in the handler - I don't think this is the way, but I wouldn't need to hold variants collection in article.
Second would be to loop through variant IDs and create ArticleVariantParameterChanged(ArticleId, VariantId, parameterId, parameterValueId) event. This seems more correct to me?
if (syncToVariants)
{
foreach (var variantId in _variants)
{
AddEvent(new ArticleVariantParameterChanged(Id, variantId, parameter.Id, parameterValue.Id);
}
}
How do I add new variant to article? The easiest way would be to create new variant and update the article in one transaction.
// Article method
public Variant RegisterVariant(VariantId variantId, ...)
{
var variant = new Variant(variantId, ...);
_variants.Add(variantId);
return variant;
}
// command handler? or domain service?
var article = await _articleRepo.GetAsync(articleId);
var variant = article.RegisterVariant(variantId, ...);
await _variantRepo.AddAsync(variant);
await _articleRepo.UpdateAsync(article);
Or using events?
// Article method
public Variant RegisterVariant(VariantId variantId, ...)
{
var variant = Variant.Register(variantId, this.Id, ...);
return variant;
}
// Variant static method
public Variant Register(VariantId variantId, ArticleId articleId, ...)
{
var variant = new Variant(variantId, articleId, ...);
variant.AddEvent(new VariantRegistered(variantId, articleId));
return variant;
}
// command handler
var variant = article.RegisterVariant(...);
await _variantRepo.AddAsync(variant);
// VariantRegisteredHandler
article.AddVariant(variantId);
However here it seems kind of confusing to me, article.RegisterVariant and article.AddVariant... Maybe it's just wrong naming?
Also here can occur condition race between adding new variant and assigning a new parameter, when someone adds new parameter before the VariantRegistered event was handled, so it wouldn't sync that parameter.
So I'm thinking, is it even good idea to store those shared parameters in each variant? Maybe it would be enough to just have variant specific parameters there and merge everything in the read model? However this would be harder to prevent duplications - if the article already has a parameter "color - red", assigning "color - red" to variant would need to check the article parameters too and there can be another race condition.
I read that entities without any domain business logic could be treated as CRUD, that means they wouldn't even inherit AggregateRoot and each of them would have own repository, right?
Let's say someone really wants to delete some parameter value, for example blue color. This wouldn't (hopefully) happen in my app, but I'm still curious how this would be handled. He confirms he really wants to delete it and I need to go through all articles and unassign it from them. How?
My idea would be either to have ParameterValueDeleted event and ParameterValueDeletedHandler would query for all articles and variants and unassign it one by one, this handler would take really long time to execute.
Or ParameterValueDeletedHandler would query for all IDs, create some event for them and that handler would unassign it later. However in the latter case I don't know how that event would be named to make sense. UnassignArticleParameter seems more like command than event and ArticleParameterUnassigned is something coming from article. Also I read that commands indicate something that can be rejected, so I would say command doesn't fit here.
Also I see a problem when someone deletes that parameter and someone else queries for an article which doesn't have it unassigned yet - database join would fail because it would join to non existent parameter (considering single database for read and write model).
If I wanted to have mandatory parameters, where would be the best place to validate that all of them are set? Move the article registration logic to ArticleFactory and check it there? And for variants maybe ArticleService or VariantFactory? This seems kinda inconsistent to me, but maybe it's right?
var article = await _articleRepo.GetAsync(articleId);
_articleService.RegisterVariant(article, /* variant creation data */);
_variantFactory.Register(article, /* variant creation data */);
I think this should be all, I hope I explained everything well.
I would appreciate any help with this!

I have created a form with a list inside of it, and I am trying to access that list in a new form

I am working on an assignment, where I need to have the user input the name and time of a competitor in a race. Once they input the form, they can go to a "View" screen (which is my new form) which will allow them to view the times.
Each race has 6 categories, so I created 6 lists in the main form (with the input). These lists are created based on an object called Competitor (which requires the name and time)
Now I need to take that list, and sort it in the "View" screen.
I am having trouble with this (I am very inexperienced with C#)
I tried referencing the input form, but to no avail. I know I am doing something wrong, but I don't know what it is.
public List<RaceCompute.Competitor> egg_adult_list = new List<RaceCompute.Competitor>();
public List<RaceCompute.Competitor> egg_teen_list = new List<RaceCompute.Competitor>();
public List<RaceCompute.Competitor> egg_kids_list = new List<RaceCompute.Competitor>();
public List<RaceCompute.Competitor> sack_adult_list = new List<RaceCompute.Competitor>();
public List<RaceCompute.Competitor> sack_teen_list = new List<RaceCompute.Competitor>();
public List<RaceCompute.Competitor> sack_kids_list = new List<RaceCompute.Competitor>();
public input_form()
{
InitializeComponent();
}
public class RaceCompute
{
public class Competitor
{
public string Name { get; set; }
public double Time { get; set; }
public Competitor(string name, double time)
{
Name = name;
Time = time;
}
}
}
// VIEW FORM //
input_form test2 = new input_form();
EDIT: Thanks a lot to the quick reply. I set the lists to public static and I can now access those lists. Again, I am very inexperienced but thanks for the help
The code will not look pretty at the end however you can sort the List using Where or FirstOrDefault.
egg_kids_list.FirstOrDefault(i=> i.Name = NameToSearchFor);
Either do this 6 times for all your lists and then add all the result Competitor objects into another List and pass it in to your new form as a parameter.
DisplayList.Add(egg_kids_list_item_returned_by_filter);
Or a better option might be passing your first Form as a parameter to your
second form
private class display_class
{
public display_class(data_class data)
{
//Here you can already access your lists
public List<RaceCompute.Competitor> egg_adult_list = data.egg_adult_list;
}
}

Create CSV with columns selected by the user

I have a little design problem. Let's say I have a project that contains a large number of people. I want to allow the user to export those people to a CSV file with the information he chooses.
For example, He could choose Id, Name, Phone number and according to his choice I would create the file.
Of course, there is a simple of way doing it like if(idCheckBox.Checked) getId(); etc.
I'm looking for something better. I don't want that for each new option I would like to add I would need to change the UI (e.g. New checkbox).
I thought of reading the possible options from a file, but that will only solved the UI problem. How would I know which values to get without using all those "if's" again?
You don't need a fancy design pattern for this task. However I understand you have identified a reason to change (added options in future). So you want to minimize amount of classes to be modified.
Your real problem is how to decouple CSV creation from the objects whose structure is going to change. You don't want your parsing logic to be affected whenever your Person class is changed.
In the following example the CSV object is truly decoupled from the objects it receives and parses. To achieve this, we are coding to an abstraction rather to an implementation. This way we are not even coupled to the Person object, but will welcome any objects that implement the AttributedObject interface. This dependency is being injected to our CSV parser.
I implemented this in PHP, but the idea is the same. C# is a static language, so fetching the attributes would be with a bit of change. You might use some kind of ArrayAccess interface.
interface AttributedObject {
public function getAttribute($attribute);
}
class Person implements AttributedObject {
protected $firstName;
protected $lastName;
protected $age;
protected $IQ;
public function __construct($firstName, $lastName, $age, $IQ)
{
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->age = $age;
$this->IQ = $IQ;
}
public function getAttribute($attribute)
{
if(property_exists($this, $attribute)) {
return $this->$attribute;
}
throw new \Exception("Invalid attribute");
}
}
class CSV {
protected $attributedObject = null;
protected $attributesToDisplay = null;
protected $csvRepresentation = null;
protected $delimiter = null;
public function __construct(AttributedObject $attributedObject, array $attributesToDisplay, $delimiter = '|')
{
$this->attributedObject = $attributedObject;
$this->attributesToDisplay = $attributesToDisplay;
$this->delimiter = $delimiter;
$this->generateCSV();
}
protected function generateCSV()
{
$tempCSV = null;
foreach ($this->attributesToDisplay as $attribute) {
$tempCSV[] = $this->attributedObject->getAttribute($attribute);
}
$this->csvRepresentation = $tempCSV;
}
public function storeCSV()
{
$file = fopen("tmp.csv", "w");
fputcsv($file, $this->csvRepresentation, $this->delimiter);
}
}
$person1 = new Person('John', 'Doe', 30, 0);
$csv = new CSV($person1, array('firstName', 'age', 'IQ'));
$csv->storeCSV();
You can build a mapping set of fields based what fields the user is allowed to select, and which fields are required. This data can be read from a file or database. Your import/export can be as flexible as needed.
Here is a conceivable data structure that could hold info for your import/export sets.
public class FieldDefinition
{
public FieldDataTypeEnum DataType { get; set; }
public string FieldName{get;set;}
public int MaxSize { get; set; }
public bool Required { get; set; }
public bool AllowNull { get; set; }
public int FieldIndex { get; set; }
public bool CompositeKey { get; set; }
}
public class BaseImportSet
{
private List<FieldDefinition> FieldDefinitions { get; set; }
protected virtual void PerformImportRecord(Fields selectedfields)
{
throw new ConfigurationException("Import set is not properly configured to import record.");
}
protected virtual void PerformExportRecord(Fields selectedfields)
{
throw new ConfigurationException("Export set is not properly configured to import record.");
}
public LoadFieldDefinitionsFromFile(string filename)
{
//Implement reading from file
}
}
public class UserImportSet : BaseImportSet
{
public override void PerformImportRecord(Fields selectedfields)
{
//read in data one record at a time based on a loop in base class
}
public override string PerformExportRecord(Fields selectedfields)
{
//read out data one record at a time based on a loop in base class
}
}

Edit DataGridView cells in Linq to Entities

When I use Linq-to-Entities to bind the data in my database to a DataGridView in a C# application I am making the DataGridView become Read-Only and can't be edited.
Is it possible to edit the data in the DataGridView and the changes saved in the database ?
This is the code where I bind the data to the DGV after applying some filtering to the first query:
private void ViewResults(IQueryable<Hero> viewResult)
{
dgdResult.DataSource = (from r in viewResult
select new
{
Name = r.Name,
Rarity = r.Rarity,
Speed = r.Speed,
Attack = r.Attack,
Target = r.Target
}).ToList();
}
That's interesting, I didn't realize it did that with anonymous types. Pretty easy to reproduce though.
The easiest thing is probably to create a private class with just the fields you need, inside the Form since you won't be using it anywhere else.
public partial class Form1 : Form
{
...
...
private void ViewResults(IQueryable<Hero> viewResult)
{
dgdResult.DataSource = (from r in viewResult
select new LittleHero
{
Name = r.Name,
Rarity = r.Rarity,
Speed = r.Speed,
Attack = r.Attack,
Target = r.Target
}).ToList();
}
private class LittleHero
{
public string Name { get; set; }
public string Rarity { get; set; }
public string Speed { get; set; }
public string Attack { get; set; }
public string Target { get; set; }
}
}
As for saving it, that's kinda broad and depends on what technologies you're using.
You can easily get the collection back from the DataGridView by casting the DataSource though.
var heroes = (List<LittleHero>)dgdResult.DataSource;
According to C# Documentation:
Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first.

C#: Bad design of class (first OOP)

This is homework!!! Please do not interpret this as me asking for someone to code for me.
My Program: http://pastebin.com/SZP2dS8D
This is my first OOP. The program works just fine without user input(UI), but the implementation of it renders my design partially ineffective. I am not using a List collection because of assignment restrictions. My main goal is to have everything running from the Transcript class. Here are some issues I am running into:
Allowing the user to add new course without having to create a new instance of Transcript
each time
Associating the Courses added to a specific Quarter
Here is some pseudo code to show what I am trying to accomplish. I have been experimenting with it, but have yet to succeed.
Please enter the quarter: (user input)
Would you like to add a course?
while (true)
Enter Course/Credits/Grade
//new Course information populated with user input
transcript.AddCourse.to specific Quarter((Fall 2013) new Course("Math 238", 5, 3.9));
transcript.AddCourse.to specific Quarter((Fall 2013) new Course("Phys 223", 5, 3.8));
transcript.AddCourse.to specific Quarter((Fall 2013) new Course("Chem 162", 5, 3.8));
MY QUESTION[S]: Should I keep the Transcript class, or discard it? With the current functionality of creating a new course, is it possible to keep it this way while using UI, or do I need to head back to the chalk board and reconfigure?
Hopefully this is coherent and not too broad. If clarification is needed please ask and I will me more than happy to provide more details.
I would consider the following compositon
public class Transcript
{
public Quarter[] Quarters{get;set;}
}
public class Quarter
{
public Course[] Courses{get;set;}
}
You only need one instance of the transcript class.
This will enable you to model n quarters (multiple years) with n courses per quarter.
In your input loop you can add new courses/quarters in response to user input
There are a lot of ways to model this problem and I think you're right to have a transcript class, but instead of thinking that a quarter has a set of courses I would suggest that which quarter a course is offered is a property of the course. For example:
public class Transcript
{
private List<Course> courses_ = new List<Course>();
public IEnumerable<Course> Courses {get { return courses_; }
public IEnumerable<Course> GetCoursesFor(int year, int quarter)
{
return courses_.Where(course => course.Year == year && course.Quarter == quarter);
}
public void AddCourse(Course course)
{
courses_.Add(course);
}
}
public class Course
{
public int Year {get; private set;}
public int Quarter {get; private set;}
// ... other members
}
you can try this
public enum Quarters
{
First,
Second,
Third,
Fourth
}
class Courses
{
private Quarters ThisQuarter { get; private set; }
private List<Tuple<Quarters, List<Courses>>> SchoolProgram = new List<Tuple<Quarters, List<Courses>>>();
public int year { get; private set; }
public string name { get; private set; }
private Courses()
{
//load list from database or xml
//each tuple has one quarters and a list
// of associated courses
//SchoolProgram.Add(new Tuple<Quarters, List<Courses>>(Quarters.First, new List<Courses>(){new Courses(2010,"Math",Quarters.First),
// new Courses(2010,"English",Quarters.First),
// new Courses(2010,"Physics",Quarters.First)}));
}
public Courses(int year,string name,Quarters q)
{
this.year = year;
this.name = name;
ThisQuarter = q;
}
public Courses GetCourse()
{
return SchoolProgram.Find(q => q.Item1 == ThisQuarter).Item2.Single(c => (c.year == this.year && c.name == this.name));
}
}
public class Transcript
{
private List<Courses> SchoolProgram = new List<Courses>();
public Transcript()
{
//maybe aditional logic here
}
public void AddCourse(int year,string name,Quarters q)
{
Courses c = new Courses(year, name, q);
SchoolProgram.Add(c.GetCourse());
}
}
you can add additional logic about the grades and other stuff....best wishes

Categories

Resources