I'm not sure if the question is correct, but wilI have a model that doesn't contain all the fields because, in addition to the model, I will get value from a relational service, so how do I add an additional field to view?
SelectedHikingDestination exists today only as int in the model, but assumes it's best to remove the field since the same value is also stored in RelationService.
Repository class, petapoco table (NodeId and MemberId is the same):
public static IList<HikingDestinationViewModel> GetAllByNodeId(int NodeId)
{
UmbracoDatabase db = Umbraco.Core.ApplicationContext.Current.DatabaseContext.Database;
return db.Fetch<HikingDestinationViewModel>("SELECT * FROM HikingDestinations WHERE NodeId = @0 ORDER BY StartDate ", NodeId);
}
SurfaceController (simplefied, NodeId and MemberId is the same)::
IEnumerable<HikingDestinationViewModel> models = new List<HikingDestinationViewModel>();
var db = ApplicationContext.DatabaseContext.Database;
this.HikingDestinations = new HikingDestinations();
models = HikingDestinations.GetAllByNodeId(Members.GetCurrentMemberId());
var relMember = Services.RelationService.GetByParentId(Members.GetCurrentMemberId()).Where(r => r.RelationType.Alias == "memberHikingDestinations");
var docItems = Services.RelationService.GetChildEntitiesFromRelations(relMember);
var results = docItems.Select(x => new { x.Id, x.Name, x.Path });
I'm not sure if I need all the last lines, but the most important question now, is how to add an additional field to models so that I can retrieve the text or path to SelectedHikingDestination to appear in view since there is no add method directly to IEnumerable < HikingDestinationViewModel > ?
I'm not sure I'm following, but if you want to expand your model then I'd create a new model that has the extra properties. You can then make one of those properties your IEnumerable<HikingDestinationViewModel> and add any other properties you like. Something like:
public class MyHikingModel
{
public IEnumerable<HikingDestinationViewModel> Destinations { get; set; }
public string SomeOtherValue { get; set; }
public int SomeIntValue { get; set; }
}
Then in your controller you can populate your Destinations collection plus fill in any other properties you have added. You then just change your controller to return that model and your view to inherit from it:
Thanks for you answer, but do I need an extra class (or I could expand the current HikingDestinationViewModel with an additional property?), when the name or the path I want to list out in view already exists somwhere in the Umbraco database since it's published pages?
(Note: When generating a database table from a petapoco class, should each property in the model match a field in the database table, or can I have more features in the model, than fields in the database table? Is this common practice?)
I already have this two properties in the HikingDestinationViewModel, where I use ListOfHikingDestinations to a dropdownlist before saving, and SelectedHikingDestination for collecting the value of the selected HikingDestination whitch I add to the RelationService (RelationService - which I don't even really know what to use for yet, but it seem like an advantage to use).
public IEnumerable<SelectListItem> ListOfHikingDestinations { get; set; }
public int SelectedHikingDestination { get; set; }
It depends if you want the property once per Hiking Destination (so you'll have many) or once per view. I assumed the latter. But if you need it on HikingDestinationViewModel then yes, by all means add an extra property. You can populate this from your controller.
I'm pretty sure PetaPoco will ignore properties that aren't mapped from a table or query when selecting. PetaPoco also has [Ignore] attribute you can decorate a property with that tells it to definitely ignore the property.
NB. Is there a reason you don't store this data in Umbraco rather than using a custom database? It seems like it would be simpler to do so...
I just try to do best practice, and focus on learning different techniques, such as CRUD from front-end with use of Membership and Model and Surfacecontroller, and integrating my own solutions by expanding the umbraco database with petapoco tables. This, I understand may be appropriate where I have saved all member properties in Umbraco (also all the custom properties) where I have one-to-many connections to many hiking destinations for each member instead of associating all the tourist destinations directly to the Umbraco nodes. Is that what you mean?
Umbraco is flexible, so there is room for many ways of doing things.
I don't know all your circumstances, but normally I would store as much as I can in Umbraco. If I wanted a one to many relationship between, say, a member and a hiking destination I'd either:
a) Create a hiking destination page and give it a members property that uses a member picker to associate the members
or
b) Add a hiking destination picker to a member
The advantage of storing content in Umbraco is you already have an easy way of editing the content set-up and you can use the same content APIs for all your queries. You can then use things like Examine for searching. You also get versioning, audit trail, easier deployment etc. out-of-the-box.
The disadvantage is that you can't enforce referential integrity as easily as you can in a database. But there are ways of tapping into events that make this possible, if required.
Thanks for the feedback. It's retrieving data that sometime seems somewhat complex (like now), but expecting this may come more into use in the future, so I'll don't get stucked when the time comes. It also gives me more training in MVC .Net development. It has sometimes been a time-consuming process to learn different Umbraco APIs, as I find it sometimes difficult to find relevant documentation.
Do you know the benefits of using RelationService to link member to nodes?
I've made a schema for creating hiking destinations that works fine where the innlogged member must add one hiking destination from a dropdown. It's the listing of several hiking destinations under the registration form I'm currently working on now (turmål = hiking destination).
I will try further and retrieve the name or the path of the published page each hiking destination refers to into the surfacecontroller tomorrow, and then I'll start with extending the model:
[Ignore] <= No Petapoco db. Used for dropdownlist
public IEnumerable<SelectListItem> ListOfHikingDestinations { get; set; }
[Ignore] <= No Petapoco db. Used for setting the RelationService
public int SelectedHikingDestination { get; set; }
[Ignore] <= No Petapoco db. Used for viewing IPublishedContent
public string HikingDestination { get; set; }
The last thing I have to do, is to find the best way to matching values in two different IEnumerable<T> (There must be a better method than compare values in two nested foreach loop, I hope). Se above:
IEnumerable<HikingDestinationViewModel> models <= Custom Umbraco db
IEnumerable<IPublishedContent> results <= Standard Umbraco db
I've not used the Relationship API much, but it's basic advantage is that it creates the relationship at the database level, so it is quicker to query.
The last thing I have to do, is to find the best way to matching
values in two different IEnumerable
What are you matching on? Do you have the Node ID stored in your HikingDestinationViewModel?
Yes the nodeId is stored in HikingDestinationViewModel, and is the same as nodeId in dbtable cmsMember.
[Column("nodeId")]
public int nodeId { get; set; }
So I want to list all records from custom dbtable HikingDestinations for each member. Listing from one table is easy with nodeId, as it's the same as members id (se belove the image):
As an example, for member id 1293, I've got three hit with this in the surfacecontroller (note that same hikingdestination are saved twice):
IEnumerable<HikingDestinationViewModel> models = new List<HikingDestinationViewModel>();
In addition to Title (preliminary not included in picture of schema above), StartDate and HikingCode, I would like to retrieve a field with the name or path of the hikingdestination (only one hiking destination per post can be saved), which now goes through RelationService and point to the published document - or maybe directly)?
As an example, for member id 1293 (parentId), I've got two hit with this (two different childId) in the surfacecontroller:
The parentId is the same as nodeId (also the members id), and the childId in dbtable umbracoRelation is the documents node where I want to find the Name or Path.
Of course, the easiest thing would be to expand the HikingDestinationViewModel model with an additional field where I save the Name or Path when I save each hikingdestination, but then the need for RelationService disappears, and I still don't know what to use RelationService for, but I want to learn me how this could be done. And double storageing is anyway a bad practice. Better to retrieve values that already have been saved, than to save them again somewhere else.
Another alternative is to dynamically return it when a property is accessed. This would require passing the Umbraco helper object into your class via the constructor. Something like:
public class HikingDestinationViewModel
{
private UmbracoHelper umbHelper;
public HikingDestinationViewModel(UmbracoHelper umbHelper)
{
this.umbHelper = umbHelper;
}
public int NodeId { get; set; }
public string HikingDestination
{
get
{
return umbHelper.TypedContent(this.NodeId).Name;
}
}
}
Obviously you'd want to do some null checks and maybe cache the result in a variable.
Yes, that's what I'm thinking of (I probably need a little more LINQ experience). Mapped: models with their three hits in the example (Id, 40, 41, 42 / nodeId 1293), and results have two hits (parentId 1293 / childId 1127, 1140), but I have not figured out how to use the var mapped (with it's three hits).
In view, I get the list with the following method, currently:
But I'm trying to figure out how to assign HikingDestination a value to each hit (three hits in this example) in Surfacecontroller when a foreach loop can't be used (var model in models {model.HikingDestination = ... })
HikingDestination should also be HikingDestination.Name and HikingDestination.Url so I can list links to the published pages they refer to (inside an a-tag, or maybe a Html helper?). Maybe I change public string HikingDestination {get; set; } to public IPublishedContent HikingDestination {get; set; } in HikingDestinationViewModel ( but then the var mapped must be changed ), and assign them with Umbraco.TypedContent(id) in the surfacecontroller in a loop so I can retrieve .Name and .Url from HikingDestination, if I find out how.
Thank you for all the tips, and Method 2 looks very interesting and advanced (since I have not used this method in the model class before), but I will first try to get the LINQ solution you have here.
I have changed HikingDestinationViewModel to public IPublishedContent HikingDestination {get; set; }. Now only the three int i need in var mapped, and the correct numbers come in this order: 1127, 1140, 1127, and the links will work all fine in view. I only need to find them and replace the number 0000 with these three numbers in that order, but I haven't figured out where I find the numbers in var mapped. Had to slightly change var mapped, but it doesn't seem to work properly in Visual Studio (no active Step Into button in VS). Almost there..
IEnumerable<HikingDestinationViewModel> models = HikingDestinations.GetAllByNodeId(Members.GetCurrentMemberId());
IEnumerable<IPublishedContent> hikingDestinations = GetRelations(Members.GetCurrentMemberId(), "memberHikingDestinations");
var mapped = models.Select(m => m.HikingDestination = hikingDestinations.First(r => r.Id == m.nodeId));
foreach (var model in models)
{
// 0000 = the three values in var mapped (loop running three times).
model.HikingDestination = Umbraco.TypedContent(0000);
}
IEnumerable<HikingDestinationViewModel> models = HikingDestinations.GetAllByNodeId(Members.GetCurrentMemberId());
public static IList<HikingDestinationViewModel> GetAllByNodeId(int NodeId)
{
UmbracoDatabase db = Umbraco.Core.ApplicationContext.Current.DatabaseContext.Database;
return db.Fetch<HikingDestinationViewModel>("SELECT * FROM HikingDestinations WHERE nodeId = @0 ORDER BY StartDate", NodeId);
}
Two unique hits, id 13, 14:
IEnumerable<IPublishedContent> hikingDestinations = GetRelations(Members.GetCurrentMemberId(), "memberHikingDestinations");
public IEnumerable<IPublishedContent> GetRelations(int memberId, string relationTypeAlias)
{
var rs = Services.RelationService;
var relType = rs.GetRelationTypeByAlias(relationTypeAlias);
var relationList = new List<IPublishedContent>();
if (memberId > 0)
{
var relations = rs.GetByParentId(memberId).Where(r => r.RelationType.Alias == relationTypeAlias);
foreach (var relation in relations)
{
relationList.Add(UmbracoContext.Current.ContentCache.GetById(relation.ChildId));
}
}
return relationList;
}
Unfortunately, hikingDestinations.First(h => h.Id == model.Id).Id still does not work since I get an error message saying "Sequence contains no matching element", (same error when I tested and modify model.Id to model.nodeId). I believe the hits should matching like this:
named: models named: hikingDestinations
HikingDestination umbracoRelations (don't store same valuepair twice)
id nodeId id parentId childId
40 1293 -> 13 1293 1127
41 1293 -> 14 1293 1140
43 1293 -> 13 1293 1127
foreach (var model in models)
{
// Three hits in the loop example
model.HikingDestination = Umbraco.TypedContent(childId);
}
The logic seems simple: "Get hikingDestinations:childId and put it into the loop where (models:nodeId = hikingDestinations:parentId)"
I find the one model.nodeId in the loop, but don't know how to find the one hikingDestination.childId and hikingDestination.parentId.
Now I think there may be no good method to find hits from umbracoRelation table to HikingDestinations table without inserting ID from the umbracoRelation table (new column in HikingDestinations table: rsId), because where can I find correct connections when RelationalService only saves new combination (if the linkage doesn't already exist), or maybe there is underlying methods in RelationalService I don't know about?
What I think I should have done from the beginning, was to insert id to umbracoRelation as a column in table HikingDestinations instead of nodeId, because the same value is also added to umbracoRelations as parentId.
I think I should insert only the members nodeId in column parentId, and nodeId to PublishedContent in column childId in table umbracoRelation - then read id of table umbracoRelation, and insert the newly created id in table HikingDestination table along with the rest of the values from the HikingDestination form. Thus, the LINQ queries would work (and that the parentId / childId combination would likely be easier to use).
But now I have chosen to come up with a simpler solution: I put parentId / childId (member id / PublishedContent id) directly in table HikingDestinations instead, and now choose not to use RelationalService most for convenience, and because I provisional don't see any benefits with RelationalService so far in this project.
How to add extra field to model into view?
I'm not sure if the question is correct, but wilI have a model that doesn't contain all the fields because, in addition to the model, I will get value from a relational service, so how do I add an additional field to view?
View (simplefied):
SelectedHikingDestination exists today only as int in the model, but assumes it's best to remove the field since the same value is also stored in RelationService.
Repository class, petapoco table (NodeId and MemberId is the same):
SurfaceController (simplefied, NodeId and MemberId is the same)::
I'm not sure if I need all the last lines, but the most important question now, is how to add an additional field to models so that I can retrieve the text or path to SelectedHikingDestination to appear in view since there is no add method directly to IEnumerable < HikingDestinationViewModel > ?
I'm not sure I'm following, but if you want to expand your model then I'd create a new model that has the extra properties. You can then make one of those properties your
IEnumerable<HikingDestinationViewModel>
and add any other properties you like. Something like:Then in your controller you can populate your Destinations collection plus fill in any other properties you have added. You then just change your controller to return that model and your view to inherit from it:
Thanks for you answer, but do I need an extra class (or I could expand the current HikingDestinationViewModel with an additional property?), when the name or the path I want to list out in view already exists somwhere in the Umbraco database since it's published pages?
(Note: When generating a database table from a petapoco class, should each property in the model match a field in the database table, or can I have more features in the model, than fields in the database table? Is this common practice?)
I already have this two properties in the HikingDestinationViewModel, where I use ListOfHikingDestinations to a dropdownlist before saving, and SelectedHikingDestination for collecting the value of the selected HikingDestination whitch I add to the RelationService (RelationService - which I don't even really know what to use for yet, but it seem like an advantage to use).
It depends if you want the property once per Hiking Destination (so you'll have many) or once per view. I assumed the latter. But if you need it on
HikingDestinationViewModel
then yes, by all means add an extra property. You can populate this from your controller.I'm pretty sure PetaPoco will ignore properties that aren't mapped from a table or query when selecting. PetaPoco also has
[Ignore]
attribute you can decorate a property with that tells it to definitely ignore the property.NB. Is there a reason you don't store this data in Umbraco rather than using a custom database? It seems like it would be simpler to do so...
Thanks, and the
[Ignore]
was a pretty good tip.I just try to do best practice, and focus on learning different techniques, such as CRUD from front-end with use of Membership and Model and Surfacecontroller, and integrating my own solutions by expanding the umbraco database with petapoco tables. This, I understand may be appropriate where I have saved all member properties in Umbraco (also all the custom properties) where I have one-to-many connections to many hiking destinations for each member instead of associating all the tourist destinations directly to the Umbraco nodes. Is that what you mean?
Umbraco is flexible, so there is room for many ways of doing things.
I don't know all your circumstances, but normally I would store as much as I can in Umbraco. If I wanted a one to many relationship between, say, a member and a hiking destination I'd either:
a) Create a hiking destination page and give it a members property that uses a member picker to associate the members or b) Add a hiking destination picker to a member
The advantage of storing content in Umbraco is you already have an easy way of editing the content set-up and you can use the same content APIs for all your queries. You can then use things like Examine for searching. You also get versioning, audit trail, easier deployment etc. out-of-the-box.
The disadvantage is that you can't enforce referential integrity as easily as you can in a database. But there are ways of tapping into events that make this possible, if required.
Thanks for the feedback. It's retrieving data that sometime seems somewhat complex (like now), but expecting this may come more into use in the future, so I'll don't get stucked when the time comes. It also gives me more training in MVC .Net development. It has sometimes been a time-consuming process to learn different Umbraco APIs, as I find it sometimes difficult to find relevant documentation.
Do you know the benefits of using RelationService to link member to nodes?
I've made a schema for creating hiking destinations that works fine where the innlogged member must add one hiking destination from a dropdown. It's the listing of several hiking destinations under the registration form I'm currently working on now (turmål = hiking destination).
I will try further and retrieve the name or the path of the published page each hiking destination refers to into the surfacecontroller tomorrow, and then I'll start with extending the model:
The last thing I have to do, is to find the best way to matching values in two different
IEnumerable<T>
(There must be a better method than compare values in two nested foreach loop, I hope). Se above:I've not used the Relationship API much, but it's basic advantage is that it creates the relationship at the database level, so it is quicker to query.
What are you matching on? Do you have the Node ID stored in your
HikingDestinationViewModel
?Yes the nodeId is stored in
HikingDestinationViewModel
, and is the same as nodeId in dbtable cmsMember.So I want to list all records from custom dbtable HikingDestinations for each member. Listing from one table is easy with nodeId, as it's the same as members id (se belove the image):
As an example, for member id 1293, I've got three hit with this in the surfacecontroller (note that same hikingdestination are saved twice):
In addition to
Title
(preliminary not included in picture of schema above),StartDate
andHikingCode
, I would like to retrieve a field with the name or path of the hikingdestination (only one hiking destination per post can be saved), which now goes through RelationService and point to the published document - or maybe directly)?As an example, for member id 1293 (parentId), I've got two hit with this (two different childId) in the surfacecontroller:
The
parentId
is the same as nodeId (also the members id), and thechildId
in dbtable umbracoRelation is the documents node where I want to find the Name or Path.Of course, the easiest thing would be to expand the
HikingDestinationViewModel
model with an additional field where I save the Name or Path when I save each hikingdestination, but then the need for RelationService disappears, and I still don't know what to use RelationService for, but I want to learn me how this could be done. And double storageing is anyway a bad practice. Better to retrieve values that already have been saved, than to save them again somewhere else.You might be able to use LINQ to update your view models with the published content names. Something like:
Another alternative is to dynamically return it when a property is accessed. This would require passing the Umbraco helper object into your class via the constructor. Something like:
Obviously you'd want to do some null checks and maybe cache the result in a variable.
Yes, that's what I'm thinking of (I probably need a little more LINQ experience). Mapped:
models
with their three hits in the example (Id, 40, 41, 42 / nodeId 1293), andresults
have two hits (parentId 1293 / childId 1127, 1140), but I have not figured out how to use thevar mapped
(with it's three hits).In view, I get the list with the following method, currently:
But I'm trying to figure out how to assign HikingDestination a value to each hit (three hits in this example) in Surfacecontroller when a foreach loop can't be used (var model in models {model.HikingDestination = ... })
HikingDestination should also be HikingDestination.Name and HikingDestination.Url so I can list links to the published pages they refer to (inside an a-tag, or maybe a Html helper?). Maybe I change
public string HikingDestination {get; set; }
topublic IPublishedContent HikingDestination {get; set; }
inHikingDestinationViewModel
( but then thevar mapped
must be changed ), and assign them withUmbraco.TypedContent(id)
in the surfacecontroller in a loop so I can retrieve .Name and .Url from HikingDestination, if I find out how.Thank you for all the tips, and Method 2 looks very interesting and advanced (since I have not used this method in the model class before), but I will first try to get the LINQ solution you have here.
I have changed
HikingDestinationViewModel
topublic IPublishedContent HikingDestination {get; set; }
. Now only the three int i need invar mapped
, and the correct numbers come in this order: 1127, 1140, 1127, and the links will work all fine in view. I only need to find them and replace the number 0000 with these three numbers in that order, but I haven't figured out where I find the numbers invar mapped
. Had to slightly changevar mapped
, but it doesn't seem to work properly in Visual Studio (no active Step Into button in VS). Almost there..Yeah, I was going to suggest just returning IPublishedContent if you need to access multiple properties.
If you are using a loop your probably don't need "mapped. I think you can just do:
The methods I use look like this.
Three hits, id 40, 41, 43:
Two unique hits, id 13, 14:
Unfortunately,
hikingDestinations.First(h => h.Id == model.Id).Id
still does not work since I get an error message saying "Sequence contains no matching element", (same error when I tested and modify model.Id to model.nodeId). I believe the hits should matching like this:The logic seems simple: "Get hikingDestinations:childId and put it into the loop where (models:nodeId = hikingDestinations:parentId)"
I find the one model.nodeId in the loop, but don't know how to find the one hikingDestination.childId and hikingDestination.parentId.
This may seem like a simple phrase to write, but I still have no idea how this should be written. Does anyone know how to write out childId?
Now I think there may be no good method to find hits from umbracoRelation table to HikingDestinations table without inserting ID from the umbracoRelation table (new column in HikingDestinations table: rsId), because where can I find correct connections when RelationalService only saves new combination (if the linkage doesn't already exist), or maybe there is underlying methods in RelationalService I don't know about?
What I think I should have done from the beginning, was to insert
id
to umbracoRelation as a column in table HikingDestinations instead of nodeId, because the same value is also added to umbracoRelations as parentId.I think I should insert only the members nodeId in column parentId, and nodeId to PublishedContent in column childId in table umbracoRelation - then read
id
of table umbracoRelation, and insert the newly createdid
in table HikingDestination table along with the rest of the values from the HikingDestination form. Thus, the LINQ queries would work (and that the parentId / childId combination would likely be easier to use).But now I have chosen to come up with a simpler solution: I put parentId / childId (member id / PublishedContent id) directly in table HikingDestinations instead, and now choose not to use RelationalService most for convenience, and because I provisional don't see any benefits with RelationalService so far in this project.
is working on a reply...