I'm having trouble making a dynamic menu using ASP.NET MVC where the menu is organized according to your Groups in Active Directory. To cut through useless details, I have retrieved two lists: one gives me all the folders of the menu, the other gives me all the files. How would one go about recursively creating the menu, knowing that folders can have subfolders, and subsubfolders and so on? In my project they are called I have two models DocumentModel and CategoryModel as follows :
//Files
public class DocumentModel
{
public long iDDocument { get; set; }
public long iDCategory { get; set; }
public string docName { get; set; }
public string link { get; set; }
public string type { get; set; }
}
//Folders
public class CategoryModel
{
public long iDCategory { get; set; }
public string nom { get; set; }
public int iDParentCategory { get; set; }
}
I then pass them to my view using a bigger model:
public class CatDocViewModel
{
public IEnumerable<CategoryModel> catModel { get; set; }
public IEnumerable<DocumentModel> docModel { get; set; }
}
So my idea is to make a recursive method in a partial view. Using Razor helpers I can make a recursive method, but I'm having a hard time with these kinds of methods.
Another problem arising is that I use IEnumerable so I have to loop through them, will that be too costly for the algorithm since I will recurse and loop again etc ...? But I also cannot make it without recursion since the depth of folders is unknown, so I have to recurse until I hit the end and then roll back up. But How?
I don't think anyone can make the algorithm but if you had some directions to point me in so I can maybe delve into recursivity and understand how to do it?
EDIT As suggested in the marked answer, here is what I would do to then build then Child lists:
public List<CategoryModel> getDocumentChilren(List<CategoryModel> categories, List<DocumentModel> documents)
{
foreach(var cat in categories)
{
if (cat.idParentCategory == 0)
continue;
foreach(var nextCat in categories)
{
if (nextCat.idParentCategory == cat.idParentCategory && nextCat.idCategory != cat.idCategory)
cat.childCategories.Add(nextCat);
}
foreach (var nextDoc in documents)
{
if (nextDoc.idCategory == cat.idCategory)
cat.childDocuments.Add(nextDoc);
}
}
return categories;
}
This is O(n^2) I believe so is it ok? If I were to make an estimation, I'd have about 20 entries per loop, so that wouldn't be too greedy?
I would organize the folders in a class more suitable for a tree structure like this:
public class CategoryModel
{
public long iDCategory { get; set; }
public string nom { get; set; }
public List<CategoryModel> ChildCategories {get;set;}
public List<DocumentModel> ChildDocuments {get;set;}
}
Then, instead of using CatDocViewModel , you could use List<CategoryModel> as the view model for the menu.
Related
I have a treelist that contains folders. Each folder can have files or other folders inside.
I need a function in C# that if I change the visibility of the folder, automatically they have to change the visibility of the folders and files under it.
Could you help me out?
Thanks in advance.
I'm not sure what you are rendering your list in and depending on the UI and/or framework you are using there may be other ways to achieve this but the simple approach to walking your folder structure would be using some simple recursion. An example:
public class Resource
{
public string Name { get; set; }
public string Type { get; set; }
public bool Visible { get; set; }
public List<Resource> Children { get; set; }
}
public class Form
{
public void ChangeVisibility(Resource resource)
{
if (resource.Type != "Folder") return;
resource.Visible = !resource.Visible;
if (resource.Children.Any())
{
resource.Children.ForEach(ChangeVisibility);
}
}
}
Over the past two years I developed apps for the CF .NET 3.5 to be runned on warehouse's portable device(windows mobile).
From the beginning I just jumped into the process and made a lot of mistakes that I'm gradually correcting. What has came out are apps made in this way:
a main form to start the whole process which automatically creates a data-form, that will stay alive for the whole time. This data-form will keep all the datas that the user will insert or request from the server. The other forms are basically views of the data with methods to manipulate them.
It works but...am I doing this in the right way? Or maybe am I missing something really fundamental?
So, you created a data form, and you are using it like RAM. You never display the data, you simply store it there to access.
If someone ever has to take over your job (like you leave the company or die), they are going to hate you so bad.
A better technique would be to create a Class that houses all of this data.
The good part is, since you already have a data form, you probably already know how everything is organized!
Now, just use that knowledge of your data to create your class that you can read and write to.
If you have groups of similar items, create other classes that your main class will contain.
If you have several of these similar items, create publically accessible Lists of these items.
Make it as dead simple or as complex as you'd like!
Consider these classes, which are all generic enough to modify however you would need and demonstrate some extras added:
public class DataForm {
private GroupedItem m_item2;
public event EventHandler Item2Changed;
public DataForm() { // this is your constructor
Item1 = new GroupedItem();
Item2 = new GroupedItem();
ItemCollection = new GroupCollectionItems("Group1");
}
public float Value1 { get; set; }
public float Value2 { get; set; }
public GroupedItem Item1 { get; set; }
public GroupedItem Item2 {
get { return m_item2; }
set {
if (m_item2 != value) {
m_item2 = value;
if (Item2Changed != null) {
Item2Changed(this, EventArgs.Empty); // notify whoever is listening for the change
}
}
}
}
public GroupCollectionItems ItemCollection { get; set; }
}
public class GroupedItem {
public GroupedItem() { // this is your constructor
}
public string Name { get; set; }
public object Value { get; set; }
}
public class GroupCollectionItem {
private GroupCollectionItem() { // this is your constructor
}
public static GroupCollectionItem Create(string groupName, string itemName, object itemValue) {
var item = new GroupCollectionItem() {
Group = groupName,
Name = itemName,
Value = itemValue
};
return item;
}
public string Group { get; private set; }
public string Name { get; private set; }
public object Value { get; set; }
}
public class GroupCollectionItems : List<GroupCollectionItem> {
public GroupCollectionItems(string name) { // this is your constructor
Name = name;
}
public string Name { get; private set; }
}
I have 3 classes inheriting from OwnableSpaceObject that has 2 navigation properties to their children via ICollections.
From the OwnableSpaceObject I want to be able to have a class like AddChild which will add one of the child classes to the ICollection and save it to the database.
Here is the OwnableSpaceObject base class:
public abstract class OwnableSpaceObject : SpaceObject
{
public int? UserId { get; set; }
public int? ResourcesId { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile User { get; set; }
[ForeignKey("ResourcesId")]
public virtual Resources Resources { get; set; }
public virtual ICollection<Structure> Structures { get; set; }
public virtual ICollection<Ship> Ships { get; set; }
}
Here is the method I'm trying to use:
public Structure CheckOrAddChild(StructureType _structureType)
{
using (ChronosContext db = new ChronosContext())
{
var structure = Structures != null ? Structures.FirstOrDefault(x => x.StructureTypeId == _structureType.Id) : null;
if (structure == null)
{
Structure newStructure = new Structure(_structureType.Id);
Structures.Add(newStructure); //this should add the Ship to the database and link it to the parent OwnableSpaceObject right? It errors out right here saying that Structures is null
db.SaveChanges();
structure = newStructure;
}
return structure;
}
}
And similarly an overloaded CheckOrAddChild for adding Ships:
public virtual Ship CheckOrAddChild(ShipType _shipType)
{
using (ChronosContext db = new ChronosContext())
{
var ship = Ships != null ? Ships.FirstOrDefault(x => x.ShipTypeId == _shipType.Id) : null;
if (ship == null)
{
Ship newShip = new Ship(_shipType.Id);
Ships.Add(newShip); //this should add the Ship to the database and link it to the parent OwnableSpaceObject right? It errors out right here saying that Ships is null
db.SaveChanges();
ship = newShip;
}
return ship;
}
}
Here is basically what the Ships and Structures class looks like:
public class Ship
{
public int Id { get; set; }
public int CurrentAmount { get; set; }
public int BuildingAmount { get; set; }
public int ShipTypeId { get; set; }
[ForeignKey("ShipTypeId")]
public virtual ShipType ShipType { get; set; }
}
The Ship/Structure class does not have a navigation property to OwnableSpaceObject because it then crate one huge table for all of my Fleets/Asteroids/Planets because they all inherit from OwnableSpaceObject. I want to keep Fleets/Asteroids/Planets separate in the tables but be able to still attach Ships and Structures to them. Currently EF is making 3 columns in the Ships/Structures tables named "Asteroid_Id", "Planet_Id", and "Fleet_Id" respectively. Should I just make a navigation property for each of these and manually link them up myself? I was trying to avoid this in order to avoid repetitive code.
Anyone have any ideas for this? I've been researching for the past 2 days and I'm about to have a nervous breakdown!
I have two classes which contain the same fields, however one inherits some properties from somewhere else and the other does not.
I have created a generic list using the class "ZEUS_ResearchStocksHistory" , but then I need to clone all of the fields over to the other list "ZEUS_ResearchStocksHistoryWithExcel". I don't want to have to loop through each field in one list and populate the other, or write some sort of linq join, there must be a faster way?
The reason I can't use the same class in both instances is that when inheriting the ExcelReport function it adds additional fields which I do not want when I display this list in a data grid.
internal class ZEUS_ResearchStocksHistory
{
public String Amendment { get; set; }
public String AmendedBy { get; set; }
public String Sedol { get; set; }
public String Date { get; set; }
}
internal class ZEUS_ResearchStocksHistoryWithExcel : ExcelReport
{
public String Amendment { get; set; }
public String AmendedBy { get; set; }
public String Sedol { get; set; }
public String Date { get; set; }
}
Is this possible?
Thanks
Did you have a look at automapper?
example from codeproject:
CustomerViewItem customerViewItem =
Mapper.Map<Customer, CustomerViewItem>(customer);
Check out Automapper, which is designed to do exactly this. Automapper is up on NuGet.
http://lostechies.com/jimmybogard/2009/01/23/automapper-the-object-object-mapper/
You could do something as simple as:
Mapper.CreateMap<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>();
var newObject = Mapper.Map<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>(oldObject);
Or, since you said you have a list, you could do:
var newList = oldList.Select(x => Mapper.Map<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>(x));
I need to create an XML file using C#.
I am using a class that inherits List that represents a list of computers and later initialize it with values but the serializer doesn't get the attributes for this class, only for its descendants.
this is the class:
public class Computers : List<Computer>
{
[XmlAttribute("StorageType")]
public int StorageType { get; set; }
[XmlAttribute("StorageName")]
public string StorageName { get; set; }
}
public class Computer
{
[XmlAttribute("StorageType")]
public int StorageType { get; set; }
[XmlAttribute("StorageName")]
public string StorageName { get; set; }
public string IPAddress { get; set; }
public string Name { get; set; }
}
The result should look something like this:
<fpc4:Computers StorageName="Computers" StorageType="1">
<fpc4:Computer StorageName="{D37291CA-D1A7-4F34-87E4-8D84F1397BEA}" StorageType="1">
<fpc4:IPAddress dt:dt="string">127.0.0.1</fpc4:IPAddress>
<fpc4:Name dt:dt="string">Computer1</fpc4:Name>
</fpc4:Computer>
<fpc4:Computer StorageName="{AFE5707C-EA71-4442-9CA8-2A6264EAA814}" StorageType="1">
<fpc4:IPAddress dt:dt="string">127.0.0.1</fpc4:IPAddress>
<fpc4:Name dt:dt="string">Computer2</fpc4:Name>
</fpc4:Computer>
But what I get so far is this:
<fpc4:Computers>
<fpc4:Computer StorageType="1" StorageName="{7297fc09-3142-4284-b2e9-d6ea2fb1be78}">
<fpc4:IPAddress>127.0.0.1</fpc4:IPAddress>
<fpc4:Name>Computer1</fpc4:Name>
</fpc4:Computer>
<fpc4:Computer StorageType="1" StorageName="{eab517f6-aca9-4d01-a58b-143f2e3211e7}">
<fpc4:IPAddress>127.0.0.1</fpc4:IPAddress>
<fpc4:Name>Computer2</fpc4:Name>
</fpc4:Computer>
</fpc4:Computers>
As you can see the Computers node which is the parent node doesn't get the attributes.
Do you guys have a solution?
XmlSerializer treats lists completely separate to leaf nodes; properties on lists do not exist - it is just a collection of the contained data. A better approach would be:
public class Computers {
private readonly List<Computer> items = new List<Computer>();
[XmlElement("Computer")]
public List<Computer> Items { get { return items; } }
[XmlAttribute("StorageType")]
public int StorageType { get; set; }
[XmlAttribute("StorageName")]
public string StorageName { get; set; }
}
This is an object that has a set of computers and has two attributes - but is not a list itself. The use of XmlElementAttribute for the list flattens the nesting as desired. Note that I have omitted namespaces for convenience.
Inheriting from a list (with an aim of adding members) will not work well, not just for XmlSerlaizer, but for a wide range of serializers and binding frameworks.