How to Store a list of Design-Time data - c#

I have the following structure in my data:
Category0
-SubCategory0
-SubCategory1
-SubCategoryN
Category1
-SubCategory1_0
-SubCategory1_1
-SubCategory1_N
A category will have a NAME, a Description and a Unique Integer ID
e.g.
Category = Ford Description = "USA Car" Id = 12345678
-SubCategory: Name = Mondeo Description = "Some text" Id = 12324
-SubCategory: Name = Fiesta Description = "Some text" Id = 9999
-SubCategory: Name = Orion Description = "Some text" Id = 123456
-SubCategory: Name = Focus Description = "Some text"Id = 8799
The list is known at design time and I need to bind to the listview. I'd like to bind the Description as the Display Text on each line of the listview and the values(an object or an enum with the Name and Id) as the corresponding valuemember.
What is the best method to store this info? Should I create a large number of enumerations? Or should I bind directly to the listview in designer mode using delimited strings such as "Ford:Mondeo:Some Text: 12324" and then parse and extract as needed. Perhaps it would be better to have the data stored strongly typed enums with custom attributes for the id/description values e.g bind to a dictionary where string is a description and CarType is a class with properties: Make(Ford):enum, Model(Modeo):enum and Id(12324):int?

Typically you would model this with two classes:
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class Manufacturer
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Model> Models { get; set; }
}
If you are concerned about performance in the comparisons, and you know exactly all the manufacturer and model names, you should consider changing the names into enums.
Also, if you will be accessing these items by name, you should consider keeping them in a dictionary with the name as the key.

This sounds like a perfect use for XML. You can add / remove categories, change the values of the name & description etc. Parse it into a simple class structure...
public class ParentCategory : Category
{
public List<Category> SubCategories { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
And then you simply bind these classes to your view.
Just because it's known at design time is not a good enough reason to go and create tons of duplicated, redundant code. It makes your program so much harder to maintain. Yes, it's simpler to look at for someone uncomfortable with XML (or any data file) but if something changes - you need to add another property to the categories, for example - you'll need to go and change every single class / enum. Messy and tedious.
edit: Just to clarify, when I say XML that's just my preferred format. You can also store your data as text, CSV, whatever your favourite format. I prefer XML as it's easier to work with.
edit2:
I see your concern (if(carListView.SelectedValue == "Mondeo")). Without knowing (or wanting to know) your whole system or what you're trying to do, I'd prefer to work in a more generic, object focused fashion.
So you'll need an if statement for each type of car? Why not just get the car to do its own work?
public class ParentCategory : Category
{
public List<Category> SubCategories { get; set; }
public void DoThings()
{
// look at SubCategories, print something, etc
}
}
// and then get the item to do things!
((ParentCategory)carListView.SelectedValue).DoThings();
This way there's no looping through whole lists. Again, keep your number of lines down.

Related

LINQ - Compare two lists of my model that share a property who's value is not equal

I have a model that contains string properties and in my application I've created two collections of those models by selecting files and reading data in them. I have a Source collection and a Target collection of type LayerModel. Each layermodel represents a layer from the drawing file selected by the user, and has properties for each of the pertinent settings I am trying to play with.
The model looks like this:
public class LayerModel : IEquatable<LayerModel>
{
public string Path { get; set; }
public string Name { get; set; }
public string OnOff { get; set; }
public string Freeze { get; set; }
public string Color { get; set; }
public string Linetype { get; set; }
public string Lineweight { get; set; }
public string Transparency { get; set; }
public string Plot { get; set; }
}
I would like to use LINQ to compare those models to produce a list of objects from the target collection who's particular property value is not equal to the source collection.
I have List<LayerModel> SourceDrawingLayers and List<LayerModel> TargetDrawingLayers. The other day I had a similar question regarding this application and I'm able to produce a collection where every single model with any difference at all is added, and that's great, but now I need to drill down and get specific differences.
This is what I've tried. The first line I am posting here returns nothing, where OnOff is the property I am trying to compare:
List<LayerModel> onOffMismatch = TargetDrawingLayers.Where(c => !SourceDrawingLayers.Any(d => c.OnOff == d.OnOff)).ToList();
I thought I had an issue with the logic so I tried to reverse it and changed the method to:
List<LayerModel> onOffMismatch = TargetDrawingLayers.Where(c => SourceDrawingLayers.Any(d => c.OnOff != d.OnOff)).ToList();
This returns every single layer from the TargetDrawingLayers collection.
I realize now that I need to run the following comparison:
Look at all the layers in the TargetDrawingLayers list, find any SourceDrawingLayer whose Name property has the same value, then compare the OnOff property. If the property is different, I will add it to a List. As a bonus, if a Target layer is not found in the source colleciton, add that to separate ExtraLayers List.
Can anyone help out with that?

Are there "Records" in C#?

I'm looking to store some customer data in memory, and I figure the best way to do that would be to use an array of records. I'm not sure if that's what its called in C#, but basically I would be able to call Customer(i).Name and have the customers name returned as a string. In turing, its done like this:
type customers :
record
ID : string
Name, Address, Phone, Cell, Email : string
//Etc...
end record
I've searched, but I can't seem to find an equivalent for C#. Could someone point me in the right direction?
Thanks! :)
Okay, well that would be defined in a class in C#, so it might look like this:
public class Customer
{
public string ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string Cell { get; set; }
public string Email { get; set; }
}
Then you could have a List<T> of those:
var customers = new List<Customer>();
customers.Add(new Customer
{
ID = "Some value",
Name = "Some value",
...
});
and then you could access those by index if you wanted:
var name = customers[i].Name;
UPDATE: as stated by psibernetic, the Record class in F# provides field level equality out of the gate rather than referential equality. This is a very important distinction. To get that same equality operation in C# you'd need to make this class a struct and then produce the operators necessary for equality; a great example is found as an answer on this question What needs to be overridden in a struct to ensure equality operates properly?.
A class or a struct would work here.
class Customer
{
string Name
{
get;
set;
}
string Email
{
get;
set;
}
}
Customer[] customers = new Customer[50];
//after initializing the array elements, you could do
//assuming a for loop with i as index
Customer currentCustomer = customers[i];
currentCustomer.Name = "This";
It appears that the "type" you are looking for is actually a Class.
class Customer {
string id, name, phone, cell, email;
}
List<Customer> customerList = new List<Customer>();
Check this link for more detail on classes... you may want to do a bit of research, reading and learning :-)
http://msdn.microsoft.com/en-us/library/vstudio/x9afc042.aspx
Assuming you have a class which models your customers, you can simply use a List of customers.
var c = new List<Customers>()
string name = c[i].Name

EF Code First not generating table for ICollection<string>

I would like the below ICollection property in one of my data classes (let's call it "Foo")
public class Foo
{
[Key]
public int FooId { get; set; }
public string SomeValueOrOther { get; set; }
public virtual ICollection<string> AllowedBars { get; set; }
}
I can add the string values when using the entity context, but they don't "go anywhere". In other words, no table is generated to represent this relationship and therefore no values are saved. What I would expect is a table with two columns, one for "FooId" and one for "AllowedBar" and for EF to map this to the collection automatically (as it does in with complex types).
As this doesn't happen, I've had to create a class called "FooAllowedBar" with the two properties I've described above.
This displeases me because it's the only "join" type class I have in the entire project. It works, of course, so one box is ticked, but does anybody know of a way to get EF to generate a table for the string collection relationship? (Or int, or datetime etc etc)
It may well be, from the little info that's out there on EF (still!) that this type of functionality is not (yet) supported. By I'd just like to get close to a definitive answer.
Many thanks in advance,
Rob
EF can only work with entity classes. Each entity class must have defined primary key so the minimum in your scenario is:
public class StringData
{
public int Id { get; set; }
public string Data { get; set; }
}
or worse
public class StringData
{
[Key]
public string Data { get; set; }
}
Now you can define collection of StringData to map it as related table:
public virtual ICollection<StringData> AllowedBars { get; set; }
I know this is not best practice in all cases, but I think there are cases where storing a comma seperated list of your array in a column is a good way to solve this problem.
Conditions include:
The list is not going to be long
You don't need to search for entities based on the values in that list
It could also be a good idea if one entity has multiple string lists in it that would create lots of joins.
In those cases I would solve it by having two properties for the list. One being the comma seperated list used by EF and the other a list that you can use when accessing the items in the list like this:
[NotMapped]
public List<String> AllowedBars { get; set; }
/// <summary>
/// Comma seperated list of AllowedBars
/// </summary>
public String AllowedBarsList
{
get { return String.Join(",", AllowedBars); }
set
{
if (String.IsNullOrWhiteSpace(value))
{
AllowedBars.Clear();
}
else
{
AllowedBars = value.Split(',').ToList();
}
}
}
You need to initialise AllowedBars to be an empty list in the constructor.
You don't need to use the [NotMapped] attribute as this collection won't be used anyway, but I think it makes the intent clearer.
This won't work. The reason for this is, that with relational databases, you can't really save arrays or a collection of things in fields. And since every property in your class will be mapped to a database-field, the only way to collections is via a one to many relationship. So you need the join. So it's not really a limitation of EF, but of relational databases.
There are people that solve that by saving XML or CSV to string fields in the table. But this is considered very bad style, so don't do it. I recommend you just have to accept the join. It's not hurting anyone anyway.
You have not define your class tables appropriately. Suppose you have two Tables Foo and FooBar. And there is a one-to-many relationship b/w Foo and FooBar. Then you will define the classes as below.
Foo
public class Foo
{
public int FooId { get; set; }
public string SomeValue { get; set; }
public virtual ICollection<FooBar> FooBars { get; set; }
}
FooBar
public class FooBar
{
public int FooBarId { get; set; }
public string SomeProperty { get; set; }
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
}
This will create two tables with Foo having two columns and FooBar having 3 columns including the FooId depicting one-to-many between Foo and FooBars

How to synchronize changes in nosql db (ravendb)

I've started learning NoSQL on an example of RavenDB. I've started with a simplest model, let's say we have topics that were created by users:
public class Topic
{
public string Id { get; protected set; }
public string Title { get; set; }
public string Text { get; set; }
public DenormalizedUser User { get; set; }
}
public class DenormalizedUser
{
public string Id { get; set; }
public string Name { get; set; }
}
public class User
{
public string Id { get; protected set; }
public string Name { get; set; }
public DateTime Birthdate { get; set; }
//some other fields
}
We don't need the whole User for displaying a Topic, so I've denormalized it to DenormalizedUser, containing an Id and a Name.
So, here are the questions:
1) Is this approach correct for NoSQL?
2) How to handle cases when User changes the Name? Do I need to manually update all the Name fields in denormalized classes?
Shaddix you can use the Raven DB Include function to load the User using the UserId from your topic.
var topic = _session.Load<Topic>(topicId)
.Customize(x => x.Include<Topic>(y => y.UserId));
var user = _session.Load<User>(topic.UserId);
The Load for Topic will 'preload' the User and both Loads will only result in one GET request. (I couldn't reply directly to your response to Ayende due to my reputation).
You also use the alternative (and probably clearer) .Include() function without Customize().
http://docs.ravendb.net/consumer/querying/handling-document-relationships.html
shaddix,
You don't need to denormalize, you can hold a reference to the id and then Include that when you load from the server
1) Yes, this approach works fine and the result is, that you only need to load the topic-document when you want to display it along with the name of its user. However, as Ayende states, the perfomance will be nearly the same as if you didn't denormalize the user and just include it when needed. If you don't worry about multiple-server deployment I recommend that approach.
2) If you really want to denormalize the user, then you can update all topics referencing this user simply with a set based operation. Look at this: http://ravendb.net/faq/denormalized-updates

How can I group on a nested property as well as having the same parent in LINQ?

Merged with How can I group on a nested property as well as having the same parent in LINQ?.
I'm new to LINQ, and I'm having trouble organizing this query to return what I want. First off, some background. I'm working on a music game, which supports custom notecharts. Notecharts contain metadata specific to a collection of notes, such as number of notes, difficulty, and BPM. One or more notecharts can be in a simfile, sort of a container of notecharts. A simfile has its own "simfile-level" metadata as well, such as a path to a song file, song title, song artist, etc. So, I have classes of the following form:
class Notechart {
public List<Note> Notes { get; }
public uint BPM { get; set; }
public uint Difficulty { get; set; }
}
class Simfile {
public List<Notechart> Notecharts { get; }
public string SongPath { get; set; }
public string SongTitle { get; set; }
public string SongArtist { get; set; }
// Other "album-specific" fields...
}
For a given List<Simfile>, I'd like to get a list of all Notecharts contained in all the Simfiles grouped by the following criteria (in order of precedence):
1) The value of Notechart.<Value>, where <Value> is any of the notechart-specific fields or a transformation of a notechart-specific field (e.g. any BPMs between 130-150)
2) Being in the same Simfile (since if two notecharts within a simfile have the same criteria as above, I would want to display them together with information from the simfile)
Is there anyway to represent this in LINQ? MyElement and MyElementCollection don't implement any custom equality checking, so I don't believe testing if it is in the list will work. Thanks!

Categories

Resources