Show Name Property on a foreignkey relationship - foreign-keys

I have a selectList of foreignkey (like a parent). Currently, the ID are binded for the value and item name. I want to change that for my Name property:
Here's my models:
public class Genus
{
public int GenusID { get; set; }
public EnumCategory Category { get; set; }
public string Name { get; set; }
public List<Species> Species { get; set; }
}
public class Species
{
public int SpeciesID { get; set; }
public int GenusID { get; set; }
public virtual Genus Genus { get; set; }
public string Name { get; set; }
}
On my Create and Edit Species page I have this code:
<select asp-for="Species.GenusID" class ="form-control" asp-items="ViewBag.GenusID"></select>
This code are generated by default when We Add Razor Page Scaffold. Well, the result are on the line 1, and what I want of result on the line 2:
<option selected="selected" value="1">1</option>
<option selected="selected" value="1">NameProperty</option> <!-- Species.Genus.Name -->
Do you have an idea to make that right ?
Thanks per advance

Looking at the code for the NavigationMetadata class in the scaffolding repo (https://github.com/aspnet/Scaffolding) I have found the following commented code which describe the behaviour.
// The default for the display property is the primary key of the navigation.
DisplayPropertyName = PrimaryKeyNames[0];
// If there is a non nullable string property in the navigation's target type, we use that instead.
var displayPropertyCandidate = navigation
.GetTargetType()
.GetProperties()
.FirstOrDefault(p => !p.IsNullable && p.ClrType == typeof(string));
if (displayPropertyCandidate != null)
{
DisplayPropertyName = displayPropertyCandidate.Name;
}
So if you want the Name property to show by default instead of the ID, then ensure it is the first string property in the model (yours already is), and then make sure it's not nullable.
protected override void OnModelCreating(ModelBuilder builder)
{
// ...
builder.Entity<Genus>().Property(l => l.Name).IsRequired();
// ...
}

Related

asp net core unit test model validator not covered non required fields

I have added model validator to validate to model. it's covered only required fields but not others.
public static class TestModelHelper
{
public static IList<ValidationResult> Validate(object model)
{
var results = new List<ValidationResult>();
var validationContext = new ValidationContext(model, null, null);
Validator.TryValidateObject(model, validationContext, results, true);
if (model is IValidatableObject)
{
(model as IValidatableObject).Validate(validationContext);
}
return results;
}
}
public class Employee
{
[Key]
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[Required]
[JsonProperty("emailId")]
public string EmailId { get; set; }
}
using below command to generate the code coverage report.
dotnet test --collect:"XPlat Code Coverageā€
reportgenerator "-reports:./TestResults/{testresultsId}/coverage.cobertura.xml" "-targetdir:coveragereport" "-"reporttypes:Html"
in this model emailId only covered in code coverage. id and name are not covered.
According to your codes, I don't found any validate attribute for the Name and Id, if you want to test validate result, you should put some validate attributes for them.
More details, you could refer to below codes and try again.
public class Employee
{
[JsonProperty("id")]
[Range(0, 999.99)]
public int Id { get; set; }
[StringLength(100)]
[JsonProperty("name")]
public string Name { get; set; }
[Required]
[JsonProperty("emailId")]
public string EmailId { get; set; }
}

Pass multiple list from controller to view

I MVC c# application which has model
public class lstSearchCriteria
{
public List<lstCampaign> cmpList { get; set; }
public List<lstAgent> agentList { get; set; }
}
public class lstCampaign
{
public string campaignName { get; set; }
}
public class lstAgent
{
public string agentShortName { get; set; }
public string agentFullName { get; set; }
}
& controller which returns lstSearchCriteria. I need to display lstCampaign & lstAgent in dropdown list.
In view I am doing
#using QAApplication.Models
#model QAApplication.Models.lstSearchCriteria
<select id="lstCampaigns" multiple="multiple">
#foreach (var item in Model.cmpList)
{
<option >#item.campaignName</option>
}
</select>
<div id="divlstAgents">
<select id="lstAgents" multiple="multiple">
#foreach (var item in Model.agentList)
{
<option >#item.agentShortName</option>
}
</select>
</div>
I am getting below error :The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[QAApplication.Models.lstSearchCriteria]', but this dictionary requires a model item of type 'QAApplication.Models.lstSearchCriteria'.
what could be the best way to pass multiple list to view from controller. Thanks in advance
You are passing List<lstSearchCriteria>, While Views expected model lstSearchCriteria only.

MVC custom serialization of List<X> parameter for GET request?

I'm wanting to change a request to be GET instead of POST so users can share the generated URLs.
The current system uses a normal form submit and takes advantage of the automatic serialization between the form submit and the MVC ActionResult for a List of custom objects.
e.g.
<form action="/MyPage">
<input type="hidden" id="MyThings_0__Value" name="MyThings[0].Value">
<input type="hidden" id="MyThings_0__Flag" name="MyThings[0].Flag">
<input type="hidden" id="MyThings_1__Value" name="MyThings[1].Value">
<input type="hidden" id="MyThings_1__Flag" name="MyThings[1].Flag">
</form>
However doing it this way causes the GET string generated to be overly long and complicated. This is bad because the MyThings list can be up to 10 items long.
http://myurl.com/MyPage?MyThings%5B0%5D.Value=ThisIsValue1&MyThings%5B0%5D.Flag=1&MyThings%5B1%5D.Value=ThisIsValue2&MyThings%5B1%5D.Flag=2
I was hoping for the string to appear more user-friendly. Something like:
http://myurl.com/MyPage?MyThings=ThisIsValue1-1,ThisIsValue2-2
Can this be done with custom serialization? And if so, how would I go about implementing it?
My Model and ActionResult:
namespace MyNamespace {
public class MyThing {
public string Value { get; set; }
public int Flag { get; set; }
}
public class Filter {
public string CustomAttribute1 { get; set; }
public string CustomAttribute2 { get; set; }
public string CustomAttribute3 { get; set; }
public List<MyThing> MyThings { get; set; } = new List<MyThing>();
}
public ActionResult MyPage(Filter filter) {
MyModel model = StaticMethod.GetMyModel(filter);
return View(model);
}
}
In the end I decided against using custom URL serialization and used helper methods to convert to string/class backward and forwards within the C# model.
public class Filter {
public string MyThing { get; set; }
public List<MyThingClass> MyThings {
get {
if (this._myThings == null) { // Default to Query string
this._myThings = ToQueryList(this.MyThing);
}
return this._myThings;
}
set {
this.MyThing = ToQueryString(value); // Automatically assign QueryString to serialized QueryItems on set
this._myThings = value;
}
}
private List<MyThingClass> _myThings { get; set; }
public static List<MyThingClass> ToQueryList(string queryString) {
return queryString.Split(',').Select(x => MyThingClass.FromString(x)).ToList();
}
public static string ToQueryString(List<MyThingClass> myThings) {
return string.Join(",", myThings.Select(x => x.ToString()));
}
}
public class MyThingClass {
public string Value { get; set; }
public int Flag { get; set; }
/// <summary>Converts a QueryItem object to a serialized object ready for the QueryString.</summary>
public override string ToString() {
return string.Concat(this.Value, "-", this.Flag);
}
public static MyThingClass FromString(string value) {
var v = value.Split('-');
return new MyThingClass() {
Value = v[0],
Flag = Convert.ToInt32(v[1])
};
}
}

How to map DropList in Sitecore Glass.Mapper

I am mapping Sitecore Items using GlassMapper v5 in Sitecore.
We implemented the following classes with GlassMapper.
However, although the value of the field is acquired for the ItemTemplate item, the value of the Droplist field (CategoryTemplate) created in the ItemTemplate has been returned as NULL and it can not be acquired.
[SitecoreType(TemplateId = "9876...", AutoMap = true)]
public class ItemTemplate
{
[SitecoreParent]
public virtual Common Parent { get; set; }
[SitecoreField(FieldName = "Category", FieldType = SitecoreFieldType.Droplist)]
public virtual CategoryTemplate Category { get; set; }
}
[SitecoreType(TemplateId = "1234...", AutoMap = true, TemplateName = "CategoryTemplate")]
public class CategoryTemplate
{
[SitecoreField(FieldName = "Id")]
public virtual string CategoryId { get; set; }
[SitecoreField(FieldName = "Name")]
public virtual string CategoryName { get; set; }
}
Environment information:
- Sitecore 9.0.2
- GlassMapper 5.0.6.0
What am I missing, please?
Try SitecoreFieldType.DropLink. The DropList type stores string value. Your template need to change to droplink too.

Sitecore Load all items into an MVC model?

I have created a bunch of custom templates to store items (such as Industries, Subindustries, etc.) in Sitecore. I now want to go about loading these into my Sitecore MVC model.
The lists are located in sitecore > Content > Lists. For example inside the Lists folder there is a folder called Country. I want to get back all the items within the Country folder and populate them as unordered list in my view.
UPDATE: I implemented the Glass.Mapper.Sc method suggested below. It is fully operational now.
This is what my working model looks like now:
using Glass.Mapper.Sc.Configuration;
using Glass.Mapper.Sc.Configuration.Attributes;
using Sitecore.Data.Items;
using Sitecore.Mvc.Presentation;
using System;
using System.Collections.Generic;
namespace Sitecore.Web.Models
{
public class Registration: IRenderingModel
{
public Rendering Rendering { get; set; }
public Item Item { get; set; }
public Item PageItem { get; set; }
public IEnumerable<CountryChildItem> CountryList { get; set; }
[SitecoreType(AutoMap = true)]
public class CountryItem
{
public virtual IEnumerable<CountryChildItem> Children { get; set; }
}
[SitecoreType(AutoMap = true)]
public class CountryChildItem
{
[SitecoreId]
public virtual Guid Id { get; set; }
[SitecoreInfo(SitecoreInfoType.Path)]
public virtual string Path { get; set; }
[SitecoreField]
public virtual string DisplayName { get; set; }
[SitecoreField]
public virtual string Abbreviation { get; set; }
}
public void Initialize(Rendering rendering)
{
Rendering = rendering;
Item = rendering.Item;
PageItem = PageContext.Current.Item;
}
}
}
and this is what my working contoller looks like:
using Glass.Mapper.Sc;
using Sitecore.Web.Models;
using System.Web.Mvc;
namespace Sitecore.Web.Controllers
{
public class RegistrationController : Controller
{
Registration registrationModel = new Registration();
public ActionResult Index()
{
ISitecoreContext sitecoreContext = new SitecoreContext();
ISitecoreService service = new SitecoreService(sitecoreContext.Database);
Registration.CountryItem countryItem = service.GetItem<Registration.CountryItem>("/sitecore/content/Lists/Country");
registrationModel.CountryList = countryItem.Children;
return View(registrationModel);
}
}
}
and a snippet of my working view:
<ul class="select-menu-options dropdown-menu">
#foreach (var country in Model.CountryList)
{
<li>#country.DisplayName</li>
}
</ul>
If I were in your position I'd look into Glassmapper for Sitecore.
It's a fairly lightweight ORM for Sitecore.
http://www.glass.lu/Mapper/Sc
I'd also suggest moving the lists located in
sitecore > Templates > User Defined > Lists > Content
to some where under either
sitecore > Content
or
sitecore > System
(whichever makes more sence)
UPDATE:
Try adding this above your class:
[SitecoreType(AutoMap = true)]
public class CountryItem
{
//...
}
If you change your CountryItem and other model classes to inherit from SearchResultItem like that:
[PredefinedQuery("TemplateID", ComparisonType.Equal, "{ID-OF-CountryItem-TEMPLATE}", typeof(ID))]
public class CountryItem : Sitecore.ContentSearch.SearchTypes.SearchResultItem
{
[IndexField("_displayname")]
public virtual string DisplayName { get; set; }
[IndexField("abbreviation")]
public string Abbreviation { get; set; }
}
You should be able to use Sitecore indexes to retrieve all the countries and other lists like that:
private static string IndexName
{
get
{
return string.Format("sitecore_{0}_index", (Context.ContentDatabase ?? Context.Database).Name);
}
}
private static string Language { get { return Context.Language.Name; } }
public IEnumerable<CountryItem> GetCountries()
{
using (var context = ContentSearchManager.GetIndex(IndexName).CreateSearchContext())
{
IQueryable<CountryItem> queryable = context.GetQueryable<CountryItem>();
queryable = queryable.Where(i => i.Language == Language);
queryable = queryable.Where(i => i.LatestVersion);
// ... maybe excluding standard values or some other filters
var searchResults = queryable.GetResults();
return queryable.ToList();
}
}
Please be aware that this is just an example. You need to test it and most probably adapt to your solution.
And as Dar Brett mentioned, you should not keep any data items under the Templates node.