I have the following model
class Entry
{
public int Id { get; set; }
public bool IsGroup { get; set; }
public int? ParentId { get; set; }
public List<YearCost> YearCost { get; set; } = new List<YearCost>();
}
class YearCost
{
public int Year { get; set; }
public decimal Cost { get; set; }
}
i have this sample list populated using the models above
static void Main(string[] args)
{
var entries = new List<Entry> {
new Entry
{
Id = 1,
ParentId = null,
IsGroup = true,
},
new Entry
{
Id = 2,
ParentId = 1,
IsGroup = false,
YearCost = new List<YearCost> {
new YearCost { Year = 2019, Cost = 10 },
new YearCost { Year = 2020, Cost = 10 }
}
},
new Entry
{
Id = 3,
ParentId = 1,
IsGroup = true
},
new Entry
{
Id = 4,
ParentId = 3,
IsGroup = true
},
new Entry
{
Id = 5,
ParentId = 4,
IsGroup = false,
YearCost = new List<YearCost> {
new YearCost { Year = 2019, Cost = 15 },
new YearCost { Year = 2020, Cost = 10 }
}
},
new Entry
{
Id = 6,
ParentId = 4,
IsGroup = false,
YearCost = new List<YearCost> {
new YearCost { Year = 2019, Cost = 15 },
new YearCost { Year = 2020, Cost = 10 }
}
},
new Entry
{
Id = 7,
ParentId = 3,
IsGroup = true
},
new Entry
{
Id = 8,
ParentId = 7,
IsGroup = false,
YearCost = new List<YearCost> {
new YearCost { Year = 2019, Cost = 30 },
new YearCost { Year = 2020, Cost = 30 }
}
},
new Entry
{
Id = 9,
ParentId = 7,
IsGroup = false,
YearCost = new List<YearCost> {
new YearCost { Year = 2019, Cost = 20 },
new YearCost { Year = 2020, Cost = 20 }
}
},
new Entry
{
Id = 10,
ParentId = 3,
IsGroup = false,
YearCost = new List<YearCost> {
new YearCost { Year = 2019, Cost = 5 },
new YearCost { Year = 2020, Cost = 5 }
}
},
};
Console.WriteLine(String.Format("{0,10}{1,10}{2,10}{3, 10}{4, 10}", "Id", "Group", "Parent Id", 2019, 2020));
Console.WriteLine(String.Format("{0,10}{1,10}{2,10}{3, 10}{4, 10}", "--", "-----", "---------", "----", "----"));
foreach (var entry in entries.OrderBy(x=>x.ParentId))
{
Console.Write(String.Format("{0,10}{1,10}{2,10}", entry.Id, entry.IsGroup ? "yes" : "no", entry.ParentId?.ToString() ?? "NULL", 2019, 2020));
foreach (var y in entry.YearCost)
Console.Write(String.Format("{0,10}", y.Cost));
Console.WriteLine("\n");
}
}
Rule #1: only entry which is not a group has cost values entered manually by user while the group entry cost is calculated
Rule #2: nesting of groups are allowed.
what i want is to do hierarchical summation for each group as shown in the table below the value inside the square brackets has to be calculated.
Id Group Parent Id 2019 2020
-- ----- --------- ---- ----
1 yes NULL [95] [85]
2 no 1 10 10
3 yes 1 [85] [75]
4 yes 3 [30] [20]
7 yes 3 [50] [50]
10 no 3 5 5
5 no 4 15 10
6 no 4 15 10
8 no 7 30 30
9 no 7 20 20
Thanks in Advance
I've managed finally to have the answer
first you need to group element by parent
var groups = entries.ToLookup(x => x.ParentId).ToDictionary(x => x.Key ?? 0, x
=> x.ToArray().Select(e => e.Id).ToList());
then get all children helpers
private List<int> GetAllChildren(int? parent, Dictionary<int, List<int>> groups)
{
List<int> children = new List<int>();
PopulateChildren(parent, children, groups);
return children;
}
private void PopulateChildren(int? parent, List<int> children, Dictionary<int, List<int>> groups)
{
List<int> myChildren;
if (groups.TryGetValue(parent.Value, out myChildren))
{
children.AddRange(myChildren);
foreach (int child in myChildren)
{
PopulateChildren(child, children, groups);
}
}
}
Then Iterate over the list to populate the totals
foreach (var item in entries)
{
if (item.IsGroup)
{
var children = GetAllChildren(item.Id, groups);
children.ForEach(c => {
var entry = entries.FirstOrDefault(x => x.Id == c);
if(entry != null)
{
if (!item.isGroup)
{
entry.YearCosts?.ForEach(y =>
{
if (item.YearCosts.FirstOrDefault(yx => yx.Year == y.Year) == null)
{
item.YearCosts.Add(new YearCost { Year = y.Year, Cost = 0 });
}
item.YearCosts.FirstOrDefault(yx => yx.Year == y.Year).Cost += y.Cost ?? 0;
item.SubTotal += y.Cost ?? 0;
});
}
}
});
}
}
Related
Using xamarin I would like to add a few pins to map to make a route.
My API for adding pins:
{"Id":1,"X":1.0,"Y":2.0,"RouteId":1,"Route":null}
My API for adding routes:
{"Id":1,"Name":"dd","Description":"fff"}
"RouteId:1" is associated with "Id:1"
I would like to create a route by pressing the button(OnNewRouteClicked)
My code:
public partial class CreatorPage : ContentPage
{
private CustomPin pin;
public CreatorPage()
{
InitializeComponent();
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(53.010281, 18.604922), Distance.FromMiles(1.0)));
}
private void OnClearClicked(object sender, EventArgs e)
{
customMap.Pins.Clear();
customMap.MapElements.Clear();
}
private async void OnMapClicked(object sender, MapClickedEventArgs e)
{
if (String.IsNullOrWhiteSpace(nazwaEntry.Text))
{
await DisplayAlert("Błąd", "Podaj nazwę punktu", "Ok");
return;
}
CustomPin pin = new CustomPin
{
Type = PinType.SavedPin,
Position = new Position(e.Position.Latitude, e.Position.Longitude),
Label = nazwaEntry.Text,
Address = opisEntry.Text,
Name = "Xamarin",
Url = "http://xamarin.com/about/",
Question = zagadkaEntry.Text,
Answer = odpowiedzEntry.Text
};
pin.MarkerClicked += async (s, args) =>
{
args.HideInfoWindow = true;
string pinName = ((CustomPin)s).Label;
// string pytanie = ((CustomPin)s).Question;
string opis = ((CustomPin)s).Address;
// string odpowiedz = ((CustomPin)s).Answer;
await DisplayAlert($"{pinName}", $"{opis}", "Quiz");
// await DisplayAlert("Quiz", $"{pytanie}", "Przejdź do odpowiedzi");
await Navigation.PushAsync(new QuestionPage(new Question()));
};
customMap.CustomPins = new List<CustomPin> { pin };
customMap.Pins.Add(pin);
var json = JsonConvert.SerializeObject(new { X = pin.Position.Latitude, Y = pin.Position.Longitude });
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
var result = await client.PostAsync("URL to points", content);
if (result.StatusCode == HttpStatusCode.Created)
{
await DisplayAlert("Komunikat", "Dodanie puntku przebiegło pomyślnie", "Anuluj");
}
}
private void OnNewRouteClicked(object sender, EventArgs e)
{
}
}
I wrote an example and hope it will help you. In the OnNewRouteClicked method, get all the points which routeId = 1 and draw a polyline with them:
public partial class MainPage : ContentPage
{
List<Points> myPoints { get; set; }
Routes myRoute { get; set; }
public MainPage()
{
InitializeComponent();
//Some data you get from your apis
myRoute = new Routes() { Id = 1 };
myPoints = new List<Points>();
myPoints.Add(new Points() { Id = 1, X = 55.6666, Y = 66.4444, RouteId = 1 }); ;
myPoints.Add(new Points() { Id = 2, X = 52.6666, Y = 68.4444, RouteId = 1 }); ;
myPoints.Add(new Points() { Id = 3, X = 53.6666, Y = 62.4444, RouteId = 1 }); ;
myPoints.Add(new Points() { Id = 1, X = 55.6666, Y = 61.4444, RouteId = 2 }); ;
myPoints.Add(new Points() { Id = 2, X = 51.6666, Y = 65.4444, RouteId = 2 }); ;
myPoints.Add(new Points() { Id = 4, X = 54.6666, Y = 67.4444, RouteId = 1 }); ;
myPoints.Add(new Points() { Id = 5, X = 59.6666, Y = 69.4444, RouteId = 1 }); ;
}
private void OnNewRouteClicked(object sender, EventArgs e)
{
List<Position> positions = new List<Position>();
//Get all the points which RouteId = 1
foreach (var item in myPoints)
{
Points tempPoint = item as Points;
if (tempPoint.RouteId == myRoute.Id)
{
Position tempPosition = new Position(tempPoint.X,tempPoint.Y);
positions.Add(tempPosition);
}
}
//your map
Map map = new Map
{
// ...
};
// instantiate a polyline
Polyline polyline = new Polyline
{
StrokeColor = Color.Blue,
StrokeWidth = 12,
};
//add your positions to polyline.Geopath
foreach (var item in positions)
{
polyline.Geopath.Add(item);
}
// add the polyline to the map's MapElements collection
map.MapElements.Add(polyline);
}
}
public class Routes
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class Points
{
public int Id { get; set; }
public double X { get; set; }
public double Y { get; set; }
public int RouteId { get; set; }
}
Hi there i would like to let customers type a category name and get some search results. Currently when you type a category name it says no results.
public ActionResult AdvanceSearch(SearchModel model, CatalogPagingFilteringModel command)
{
//'Continue shopping' URL
_genericAttributeService.SaveAttribute(_workContext.CurrentCustomer,
SystemCustomerAttributeNames.LastContinueShoppingPage,
_webHelper.GetThisPageUrl(false),
_storeContext.CurrentStore.Id);
if (model == null)
model = new SearchModel();
var searchTerms = model.q;
if (searchTerms == null)
searchTerms = "";
searchTerms = searchTerms.Trim();
//sorting
PrepareSortingOptions(model.PagingFilteringContext, command);
//view mode
PrepareViewModes(model.PagingFilteringContext, command);
//page size
PreparePageSizeOptions(model.PagingFilteringContext, command,
_catalogSettings.SearchPageAllowCustomersToSelectPageSize,
_catalogSettings.SearchPagePageSizeOptions,
_catalogSettings.SearchPageProductsPerPage);
string cacheKey = string.Format(ModelCacheEventConsumer.SEARCH_CATEGORIES_MODEL_KEY,
_workContext.WorkingLanguage.Id,
string.Join(",", _workContext.CurrentCustomer.GetCustomerRoleIds()),
_storeContext.CurrentStore.Id);
var categories = _cacheManager.Get(cacheKey, () =>
{
var categoriesModel = new List<SearchModel.CategoryModel>();
//all categories
var allCategories = _categoryService.GetAllCategories(storeId: _storeContext.CurrentStore.Id);
foreach (var c in allCategories)
{
//generate full category name (breadcrumb)
string categoryBreadcrumb = "";
var breadcrumb = c.GetCategoryBreadCrumb(allCategories, _aclService, _storeMappingService);
for (int i = 0; i <= breadcrumb.Count - 1; i++)
{
categoryBreadcrumb += breadcrumb[i].GetLocalized(x => x.Name);
if (i != breadcrumb.Count - 1)
categoryBreadcrumb += " >> ";
}
categoriesModel.Add(new SearchModel.CategoryModel
{
Id = c.Id,
Breadcrumb = categoryBreadcrumb
});
}
return categoriesModel;
});
if (categories.Any())
{
//first empty entry
model.AvailableCategories.Add(new SelectListItem
{
Value = "0",
Text = _localizationService.GetResource("Common.All")
});
//all other categories
foreach (var c in categories)
{
model.AvailableCategories.Add(new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Breadcrumb,
Selected = model.cid == c.Id
});
}
}
IPagedList<Product> products = new PagedList<Product>(new List<Product>(), 0, 1);
// only search if query string search keyword is set (used to avoid searching or displaying search term min length error message on /search page load)
if (Request.Params["q"] != null)
{
if (searchTerms.Length < _catalogSettings.ProductSearchTermMinimumLength)
{
model.Warning = string.Format(_localizationService.GetResource("Search.SearchTermMinimumLengthIsNCharacters"), _catalogSettings.ProductSearchTermMinimumLength);
}
else
{
var categoryIds = new List<int>();
int manufacturerId = 0;
decimal? minPriceConverted = null;
decimal? maxPriceConverted = null;
bool searchInDescriptions = false;
int vendorId = 0;
if (model.adv)
{
//advanced search
var categoryId = model.cid;
if (categoryId > 0)
{
categoryIds.Add(categoryId);
if (model.isc)
{
//include subcategories
categoryIds.AddRange(GetChildCategoryIds(categoryId));
}
}
manufacturerId = model.mid;
//min price
if (!string.IsNullOrEmpty(model.pf))
{
decimal minPrice;
if (decimal.TryParse(model.pf, out minPrice))
minPriceConverted = _currencyService.ConvertToPrimaryStoreCurrency(minPrice, _workContext.WorkingCurrency);
}
//max price
if (!string.IsNullOrEmpty(model.pt))
{
decimal maxPrice;
if (decimal.TryParse(model.pt, out maxPrice))
maxPriceConverted = _currencyService.ConvertToPrimaryStoreCurrency(maxPrice, _workContext.WorkingCurrency);
}
if (model.asv)
vendorId = model.vid;
searchInDescriptions = model.sid;
}
//var searchInProductTags = false;
var searchInProductTags = searchInDescriptions;
//products
products = _productService.SearchProducts(
categoryIds: categoryIds,
manufacturerId: manufacturerId,
storeId: _storeContext.CurrentStore.Id,
visibleIndividuallyOnly: true,
priceMin: minPriceConverted,
priceMax: maxPriceConverted,
keywords: searchTerms,
//searchDescriptions: searchInDescriptions,
searchProductTags: searchInProductTags,
searchSku: true,
languageId: _workContext.WorkingLanguage.Id,
orderBy: (ProductSortingEnum)command.OrderBy,
pageIndex: command.PageNumber - 1,
pageSize: command.PageSize,
vendorId: vendorId);
model.Products = PrepareProductOverviewModels(products).ToList();
model.NoResults = !model.Products.Any();
//search term statistics
if (!String.IsNullOrEmpty(searchTerms))
{
var searchTerm = _searchTermService.GetSearchTermByKeyword(searchTerms, _storeContext.CurrentStore.Id);
if (searchTerm != null)
{
searchTerm.Count++;
_searchTermService.UpdateSearchTerm(searchTerm);
}
else
{
searchTerm = new SearchTerm
{
Keyword = searchTerms,
StoreId = _storeContext.CurrentStore.Id,
Count = 1
};
_searchTermService.InsertSearchTerm(searchTerm);
}
}
//event
_eventPublisher.Publish(new ProductSearchEvent
{
SearchTerm = searchTerms,
SearchInDescriptions = searchInDescriptions,
CategoryIds = categoryIds,
ManufacturerId = manufacturerId,
WorkingLanguageId = _workContext.WorkingLanguage.Id,
VendorId = vendorId
});
}
}
model.PagingFilteringContext.LoadPagedList(products);
return View(model);
}
A public action method 'AdvanceSearch' was not found on controller 'Nop.Web.Controllers.CatalogController'.
I want to insert 3 value of radio button into Database using MVC.
User need to select one material for each categories(Walls,Roof,Floor)
Currently the user can only select one value (may need to do grouping).But when I do grouping only the structInfo value is inserted into database. I need all the 3 value inserted into database.
This is how the database design look like
the struct inf(walls,roof,floor) the materialinfo is (bricks.concrete,woods, etc)
So can I make all the 3 value choosed by user save into database?
This is my view
#foreach (var structIN in Model.structInfo)
{
if (structIN.structId.Equals(1))
{
#Html.Label(structIN.structNm) #:
foreach (var material in Model.materialInfo)
{
if (material.materialId.Equals(1) || material.materialId.Equals(2) || material.materialId.Equals(3))
{
#Html.RadioButtonFor(model => model.buildInfo.materialId, material.materialId)#Html.Label(material.materialNm)
#Html.HiddenFor(model => model.buildInfo.structId, new { Value = structIN.structId })
}
}
}
else if(structIN.structId.Equals(2))
{
<br />
#Html.Label(structIN.structNm) #:
foreach (var material2 in Model.materialInfo)
{
if (material2.materialId.Equals(2) || material2.materialId.Equals(4) || material2.materialId.Equals(5))
{
#Html.RadioButtonFor(model2 => model2.buildInfo.materialId, material2.materialId)#Html.Label(material2.materialNm)
#Html.HiddenFor(model2 => model2.buildInfo.structId, new { Value = structIN.structId })
}
}
}
else if (structIN.structId.Equals(3))
{
<br />
#Html.Label(structIN.structNm) #:
foreach (var material3 in Model.materialInfo)
{
if (material3.materialId.Equals(6) || material3.materialId.Equals(3))
{
#Html.RadioButtonFor(model3 => model3.buildInfo.materialId, material3.materialId) #Html.Label(material3.materialNm)
#Html.HiddenFor(model3 => model3.buildInfo.structId, new { Value = structIN.structId })
}
}
}
}
my GET method
[HttpGet]
public ActionResult RegisterForm()
{
PopulateStructMaterialData();
using (var dataBase = new TMXEntities())
{
var model = new RegisterInfoPA()
{
//OTHER CODES
};
return View(model);
}
}
Populating Data
private void PopulateStructMaterialData()
{
var list = new List<strucMaterial>
{
new strucMaterial{ifOthers = "", materialId = 1, materialNm = "Bricks", structId = 1, structNm = "Walls", insuranceReqId = 0, isSelected = false},
new strucMaterial{ifOthers = "", materialId = 2, materialNm = "Concrete", structId = 1, structNm = "Walls", insuranceReqId = 0, isSelected = false},
new strucMaterial{ifOthers = "", materialId = 3, materialNm = "Woods", structId = 1, structNm = "Walls", insuranceReqId = 0, isSelected = false},
new strucMaterial{ifOthers = "", materialId = 2, materialNm = "Concrete", structId = 2, structNm = "Roof", insuranceReqId = 0, isSelected = false},
new strucMaterial{ifOthers = "", materialId = 4, materialNm = "Tiles", structId = 2, structNm = "Roof", insuranceReqId = 0, isSelected = false},
new strucMaterial{ifOthers = "", materialId = 5, materialNm = "Zinc", structId = 2, structNm = "Roof", insuranceReqId = 0, isSelected = false},
new strucMaterial{ifOthers = "", materialId = 3, materialNm = "Woods", structId = 3, structNm = "Floor", insuranceReqId = 0, isSelected = false},
new strucMaterial{ifOthers = "", materialId = 6, materialNm = "Reinforced Concrete", structId = 3, structNm = "Floor", insuranceReqId = 0, isSelected = false},
};
ViewBag.populatebuilding = list;
}
In View
List<Insurance.ViewModels.strucMaterial> viewModelSM = ViewBag.populatebuilding;
for(int i=0; i<viewModelSM.Count; i++)
{
#Html.DisplayFor(m => viewModelSM[i].structNm)
#Html.HiddenFor(m => viewModelSM[i].structId)
#Html.CheckBoxFor(m => viewModelSM[i].isSelected)
#Html.HiddenFor(m => viewModelSM[i].materialId)
#Html.Label(viewModelSM[i].materialNm)
}
My POST method
[HttpPost]
public ActionResult RegisterForm(RegisterInfoPA viewmodel, List<strucMaterial> list)
{
using (var dataBase = new TMXEntities())
{
var model = new RegisterInfoPA()
{
//OTHER CODES
};
if (ModelState.IsValid)
{
//OTHER CODES
var register = viewmodel.reg;
var personalinfo = viewmodel.pinfo;
//Save Register
db.registers.Add(register);
db.SaveChanges();
//Retriving required Id's
var getid = register.registrationId;
var getRegTypeID = register.regisTypeId;
//SAVE PERSONAL INFO
personalinfo.registrationId = getid;
db.personalInfoes.Add(personalinfo);
db.SaveChanges();
//---HOW SHOULD I SAVE THE CHECKBOX HERE?-----------
**> tHIS IS MY CODE, BUT IT IS NOT WORKING**
foreach(var item in list) (<< error starts here)
{
buildingInfo.materialId = item.materialId;
buildingInfo.structId = item.structId;
buildingInfo.insuranceReqId = item.insuranceReqId;
db.buildingInfoes.Add(buildingInfo);
}
db.SaveChanges();
}
}
}
I am always getting this error
System.NullReferenceException: Object reference not set to an instance of an object.
How the proper code should look like? Thank you.
I have following data & need to know if this can be displayed with line chart or not.
Data :
VerNo | Start Date | End Date
1.1 | 01-Jan-2013 | 31-Jan-2013
1.2 | 01-Feb-2013 | 31-Dec-2099
2.1 | 10-Jan-2013 | 25-Jan-2013
2.2 | 26-Jan-2013 | 16-Feb-2013
3.1 | 16-Mar-2013 | 30-Apr-2013
I need a line chart with dates in X-axis & VerNo in Y-axis & horizontal line should display start & end date of each version.
Thanks!!!
With my little knowledge of the Chart control I tried something.
First I bind the data with Id=0 on Y-axis to get the dates (can probably done better)
Then I looped over the data and made a serie per row.
Every serie I made a random color, but sometimes the color is too white, so it doesn't show.
public class VersionData
{
public int Id { get; set; }
public double VersionNo { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
string dateFormat = "yyyy MMM dd";
List<VersionData> version = new List<VersionData>();
version.Add(new VersionData() { Id=0, VersionNo = 1.1, StartDate = new DateTime(2013, 1, 1), EndDate = new DateTime(2013, 1, 31) });
version.Add(new VersionData() { Id=0, VersionNo = 1.2, StartDate = new DateTime(2013, 2, 1), EndDate = new DateTime(2013, 12, 31) });
version.Add(new VersionData() {Id=0, VersionNo = 2.1, StartDate = new DateTime(2013, 1, 10), EndDate = new DateTime(2013, 1, 25) });
version.Add(new VersionData() {Id=0, VersionNo = 2.2, StartDate = new DateTime(2013, 1, 26), EndDate = new DateTime(2013, 2, 16) });
version.Add(new VersionData() { Id=0, VersionNo = 3.1, StartDate = new DateTime(2013, 3, 16), EndDate = new DateTime(2013, 4, 30) });
Chart1.Series[0].YValueMembers = "Id";
Chart1.DataSource = version;
Random randomGen = new Random();
KnownColor[] names = (KnownColor[])Enum.GetValues(typeof(KnownColor));
for (int i = 0; i < version.Count; i++)
{
Series s = new Series("s" + i.ToString());
s.ChartType = SeriesChartType.Line;
s.Color = Color.FromKnownColor(names[randomGen.Next(names.Length)]);
s.BorderWidth = 4;
Chart1.Series.Add(s);
DataPoint p = new DataPoint();
p.SetValueXY(version[i].StartDate, version[i].VersionNo);
s.Points.Add(p);
DataPoint p2 = new DataPoint();
p2.SetValueXY(version[i].EndDate, version[i].VersionNo);
s.Points.Add(p2);
}
Chart1.Series[0].XValueMember = "StartDate";
Chart1.ChartAreas[0].AxisX.Interval = 1;
Chart1.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Months;
Chart1.ChartAreas[0].AxisX.LabelStyle.Format = dateFormat;
Chart1.ChartAreas[0].AxisX.LabelStyle.Angle = -90;
Chart1.ChartAreas[0].AxisY.Interval = 0.5;
Chart1.ChartAreas[0].AxisY.LabelStyle.Format = "0.0";
}
I'm trying to create a self referencing object using linqTOsql mapping. So far, I am definitely in over my head. Here's the code I have:
[Table]
public class Category
{
[Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
public Int64 catID { get; set; }
public Int64 parentCatID { get; set; }
public string catName { get; set; }
public string catDescription { get; set; }
internal EntityRef<IEnumerable<Category>> _category;
[Association(ThisKey = "parentCatID", Storage = "_category")]
public IEnumerable<Category> category {
get { return _category.Entity; }
set { _category.Entity = value; }
}
}
My fakeRepository is defined like this:
// Fake hardcoded list of categories
private static IQueryable<Category> fakeCategories = new List<Category> {
new Category { catID = 1, parentCatID = 0, catName = "Root", catDescription = "" },
new Category { catID = 2, parentCatID = 1, catName = "Category w/subs", catDescription = "" },
new Category { catID = 3, parentCatID = 1, catName = "Category no subs but now has subs", catDescription = "" },
new Category { catID = 4, parentCatID = 2, catName = "Zub Cat", catDescription = "" },
new Category { catID = 5, parentCatID = 2, catName = "Sub Cat", catDescription = "" },
new Category { catID = 6, parentCatID = 0, catName = "Another Root", catDescription = "" },
new Category { catID = 7, parentCatID = 0, catName = "Ze German Root", catDescription = "" },
new Category { catID = 8, parentCatID = 3, catName = "Brand new cats", catDescription = "" },
new Category { catID = 9, parentCatID = 8, catName = "Brand new cats sub", catDescription = "" },
}.AsQueryable();
I pass Category to the view like this:
public ActionResult CategoryTree()
{
IQueryable<Category> cats = genesisRepository.Category
.Where(x => x.parentCatID == 0)
.OrderBy(x => x.catName);
return View(cats);
}
The problem that I'm running into is that all of this compiles, but I don't get anything beyond the root categories. Model[0].category is returning null.
What is wrong with my self-referencing object?
Edit
I wonder if it's not working because I don't have a real linq-to-sql data context in my fakeRepository. If that's the case, is there a way around that? Can I can get this to work without a connection to a database?
Yeah, you hit the nail on the head. It's not working because you're using a fake repository.
Linq-to-Sql does all the wiring up for you and sets the related collections based on the properties (& their attributes) that you setup in your model.
I don't know how to accomplish this without a connection to the database because internal EntityRef<IEnumerable<Category>> _category; is completely foreign to me - I'm more of a POCO model type of guy.
After a quick google, I found this - How to: Map Database Relationships (LINQ to SQL)
Could you change your model to read:
[Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
public Int64 CatId { get; set; }
[Column]
public Int64 ParentCatId { get; set; }
[Column]
public string CatName { get; set; }
[Column]
public string CatDescription { get; set; }
private EntitySet<Category> _ChildCategories;
[Association(Storage = "_ChildCategories", OtherKey = "ParentCatId")]
public EntitySet<Category> ChildCategories
{
get { return this._ChildCategories; }
set { this._ChildCategories.Assign(value); }
}
private EntityRef<Category> _ParentCategory;
[Association(Storage = "_ParentCategory", ThisKey = "ParentCatId")]
public Category ParentCategory
{
get { return this._ParentCategory.Entity; }
set { this._ParentCategory.Entity = value; }
}
Now because your ChildCategories is of type EntitySet<Category> (which inherits from IList<T>) you should be able to wire fake relationships up yourself.
So you could do something like this:
private static IQueryable<Category> GetFakeCategories()
{
var categories = new List<Category> {
new Category { CatId = 1, ParentCatId = 0, CatName = "Root", CatDescription = "" },
new Category { CatId = 2, ParentCatId = 1, CatName = "Category w/subs", CatDescription = "" },
//Blah
new Category { CatId = 8, ParentCatId = 3, CatName = "Brand new cats", CatDescription = "" },
new Category { CatId = 9, ParentCatId = 8, CatName = "Brand new cats sub", CatDescription = "" }
};
//Loop over the categories to fake the relationships
foreach (var category in categories)
{
category.ChildCategories = new EntitySet<Category>(); //new up the collection
foreach (var subLoopCategory in categories)
{
if (category.ParentCatId == subLoopCategory.CatId)
category.ParentCategory = subLoopCategory;
if (category.Id == subLoopCategory.ParentCatId)
category.ChildCategories.Add(subLoopCategory);
}
}
return categoies.AsQueryable();
}
It works in my head at least... :-)
HTHs,
Charles
EDIT: Re: Comment below about a null reference on _childCategories.
You could change the model to look like:
private EntitySet<Category> _ChildCategories = new EntitySet<Category>();
It is supposed to be null. You are getting all categories where the ParentId = 0 ... and you don't have a child with an Id of 0. So that seems right to me.
It is not showing any subcategories because it has no subcategories to show. Try this:
IQueryable<Category> cats = genesisRepository.Category
.Where(x => x.parentCatID != 0)
.OrderBy(x => x.catName);
The parentCatId needs to point to a valid CatId for it to be a subcategory. This query should get you all the categories that are subcategories.