MVC Grid get value from another table using id - c#

I am using Telerik MVC Grid, have two tables:
public partial class Sleep
{
public int ID { get; set; }
public string PHN { get; set; }
public System.DateTime Day { get; set; }
public Nullable<int> SleepLevelId { get; set; }
public Nullable<int> SDuration { get; set; }
public string Comment { get; set; }
public virtual SleepLevel SleepLevel { get; set; }
}
public partial class SleepLevel
{
public SleepLevel()
{
this.Sleep = new HashSet<Sleep>();
}
public int SleepLevelId { get; set; }
public string TheLevel { get; set; }
public virtual ICollection<Sleep> Sleep { get; set; }
}
}
I use the second model for dropdownlist. Here how I generate the Grid Model:
private IEnumerable<Sleep> GePB ()
{
....
var sleep = db.Sleep.Include(s => s.SleepLevel).Where(d => d.PHN == phn && d.Day <= toDate);
return sleep;
}
[GridAction]
public ActionResult _AjaxBinding ()
{
return View(new GridModel<Sleep> { Data = GePB() });
}
In the Grid I use:
#(Html.Telerik().Grid<Sleep>()
.Name("Grid")
.DataKeys(keys => keys.Add(p => p.ID))
.ToolBar(commands => commands.Insert().ButtonType(GridButtonType.ImageAndText).ImageHtmlAttributes(new
{
style = "margin-left:0;"
}))
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_SelectAjaxEditing", "Sleep")
.Insert("_InsertAjaxEditing", "Sleep")
.Update("_SaveAjaxEditing", "Sleep")
.Delete("_DeleteAjaxEditing", "Sleep"))
.Columns(columns =>
{
columns.Bound(p => p.Day).Width(70).Format("{0:d}");
columns.Bound(o => o.SleepLevel.TheLevel).Width(70);
columns.Bound(o => o.SDuration).Width(70);
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Image);
commands.Delete().ButtonType(GridButtonType.Image);
}).Width(50).Title("Edit");
})
.Editable(editing => editing.Mode(GridEditMode.InLine))
.Pageable()
.Scrollable()
.Sortable().Filterable()
)
I get the following error:
Unable to get property 'TheLevel' of undefined or null reference
Would appreciate your suggestions.

There is no property in the Sleep class that is a StressLevel type. So I'll address the error message as best as possible.
The error
Unable to get property 'TheLevel' of undefined or null reference
is because this line
columns.Bound(o => o.SleepLevel.TheLevel).Width(70);
is trying to access a property of the SleepLevel class called TheLevel that most probably does not exist in your Sleep class.

Related

.NET Core API - Unknown Column in field list

I'm new to C# / .NET Core and I'm trying to create a Web API. I have 2 models; Community & Rank
Community:
public class Community
{
public long Id { get; set; }
public long FrontId { get; set; }
public string Name { get; set; }
[ForeignKey("CommunityId")]
public virtual ICollection<Rank> Ranks { get; set; }
}
Rank:
public class Rank
{
public long Id { get; set; }
public long CommunityId { get; set; } [Required]
public string Name { get; set; } [Required]
public string Prefix { get; set; }
public virtual Community Community { get; set; }
}
I have my context set up like so:
public class CommunityContext : DbContext
{
public DbSet<Community> Communities { get; set; }
public DbSet<Rank> Ranks { get; set; }
public CommunityContext(DbContextOptions<CommunityContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
//Communities
builder.Entity<Community>(entity =>
{
entity.HasKey(c => c.Id);
entity.Property(c => c.Name).IsRequired();
entity.Property(c => c.FrontId).IsRequired();
// entity.Property(c => c.Ranks).IsRequired(false);
entity.HasMany(c => c.Ranks).WithOne().HasForeignKey(c => c.CommunityId).IsRequired();
});
//Ranks
builder.Entity<Rank>(entity =>
{
entity.HasKey(r => r.Id);
entity.Property(r => r.Name).IsRequired();
entity.Property(r => r.Prefix).IsRequired();
entity.Property(r => r.CommunityId).HasColumnName("CommunityId").IsRequired();
// entity.HasOne(r => r.Community).WithMany(s => s.Ranks);
});
}
}
I've done my database migrations etc, and when creating new entries, all works as expected currently, however trying to GET either communities or ranks returns the error:
MySql.Data.MySqlClient.MySqlException (0x80004005): Unknown column 'r.CommunityId1' in 'field list'
Any suggestions would be appreciated!
Thanks
Your Configurations should be like this:
...
//Communities
builder.Entity<Community>(entity =>
{
builder.ToTable("Community");
builder.HasKey(c => c.Id);
builder.Property(c => c.Id).HasColumnName("CommunityId").ValueGeneratedOnAdd();
entity.Property(c => c.Name).IsRequired();
entity.Property(c => c.FrontId).IsRequired();
// entity.Property(c => c.Ranks).IsRequired(false);
entity.HasMany(c => c.Ranks).WithOne().HasForeignKey(c => c.CommunityId).IsRequired();
});
//Ranks
builder.Entity<Rank>(entity =>
{
builder.ToTable("Rank");
builder.HasKey(r => r.Id);
builder.Property(r => r.Id).HasColumnName("RankId").ValueGeneratedOnAdd();
entity.Property(r => r.Name).IsRequired();
entity.Property(r => r.Prefix).IsRequired();
entity.Property(r => r.CommunityId).HasColumnName("CommunityId").IsRequired();
// entity.HasOne(r => r.Community).WithMany(s => s.Ranks);
});
...

AutoMapper Mapping a collection of strings to a property of a collection inside another collection

How to mapping IdContributors (collection of strings) to a collection (Contributors), inside a collection TAction, with a property (ContributorId) of string, using LINQ and AutoMapper ?
public ViewModelToDomainMappingProfile()
{
CreateMap<ActionViewModel, TAction>();
//.ForMember(d => d.Contributors, opt => opt.MapFrom(a => ids = a.IdContributors.Select(x => { })));
}
Models
Model TAction
public class TAction
{
public Guid Id {get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<TActionContributor> Contributors { get; set; }
}
public class TActionContributor
{
public Guid TActionId { get; set; }
[ForeignKey("TActionId")]
public TAction Action { get; set; }
public string ContributorId { get; set; }
[ForeignKey("ContributorId")]
public ApplicationUser Contributor { get; set; }
}
ActionViewModel
public class ActionViewModel
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<string> IdContributors { get; set; }
}
I can see two choices here, you'll choose the solution which better suits your needs:
1) Select() in from MapFrom()
CreateMap<ActionViewModel, TAction>()
.ForMember(
destination => destination.Contributors,
options => options.MapFrom((source, destination) =>
{
var contributors = source
.IdContributors
.Select(id => new TActionContributor
{
Action = destination,
Contributor = new ApplicationUser
{
Id = id,
},
ContributorId = id,
TActionId = source.Id,
})
.ToList();
contributors.ForEach(ac => ac.Contributor.Contributors = contributors);
return contributors;
}));
2) Custom ValueResolver
This is the same as the first solution, maybe more readable if you want to keep your mapping profiles clean and move the custom logic away when possible.
public class TActionContributorValueResolver : IValueResolver<ActionViewModel, TAction, ICollection<TActionContributor>>
{
public ICollection<TActionContributor> Resolve(
ActionViewModel source,
TAction destination,
ICollection<TActionContributor> destMember,
ResolutionContext context)
{
var contributors = source
.IdContributors
.Select(id => new TActionContributor
{
Action = destination,
Contributor = new ApplicationUser
{
Id = id,
},
ContributorId = id,
TActionId = source.Id,
})
.ToList();
contributors.ForEach(ac => ac.Contributor.Contributors = contributors);
return contributors;
}
}
Configuration:
CreateMap<ActionViewModel, TAction>()
.ForMember(
destination => destination.Contributors,
options => options.MapFrom<TActionContributorValueResolver>());
Final note:
contributors.ForEach(ac => ac.Contributor.Contributors = contributors);
Line above allows you to traverse endlessly from TAction through TActionContributor to ApplicationUser and the other way. If you don't need that feature, feel from to remove it and return the contributors list right away.

C# and MongoDriver - How to get values from foreign collection (aggregate + lookup)?

I've got a problem with getting values from foreign collection in C#.
In this case I can easily get values from list:
var gamesList = gamesCollection.Find(_ => true).ToList();
foreach (var item in gamesList)
{
Console.WriteLine($"{item.Title}");
}
But when I'm using aggregate with lookup function, I can not access to values from foreign collection.
Here are my two collections which I try to join:
public class GameModel
{
[BsonId]
public ObjectId Id { get; set; }
public string Title { get; set; }
public List<String> Type { get; set; }
public string GameMode { get; set; }
public List<String> Platform { get; set; }
public string Production { get; set; }
}
public class FavouriteGameModel
{
[BsonId]
public ObjectId Id { get; set; }
public ObjectId UserID { get; set; }
public ObjectId GameID { get; set; }
}
And here's the part of problematic code:
var joinedFavGamesList = favouriteGamesCollection.Aggregate().Match(x => x.UserID == loggedUser[0].Id).//ToList();
Lookup("Games", "GameID", "_id", #as: ("myAlias")).
Project(
new BsonDocument { { "_id", 0 }, { "myAlias.Title", 1 } }
).ToList();
Is there any way to invoke to myAlias.Title? I want only this value to display, but i get:
{ "myAlias" : [{ "Title" : "Some Game" }] }
I will be greatful if someone could look at this and tell me what I'm doing wrong. Thanks
my choice would be to join/lookup using the AsQueryable interface like so:
var favGames = favCollection.AsQueryable()
.Where(fg=> fg.UserID== "xxxxxxxxxxx")
.Join(gameCollection.AsQueryable(), //foreign collection
fg => fg.GameID, //local field
gm => gm.ID, //foreign field
(fg, gm) => new { gm.Title }) //projection
.ToList();
with aggregate interface:
public class JoinedGameModel
{
public GameModel[] Results { get; set; }
}
var favGames = favGameCollection.Aggregate()
.Match(fg => fg.UserID == "xxxxxxxxxxxx")
.Lookup<FavouriteGameModel, GameModel, JoinedGameModel>(
gameCollection,
fg => fg.GameID,
gm => gm.ID,
jgm => jgm.Results)
.ReplaceRoot(jgm => jgm.Results[0])
.Project(gm => new { gm.Title })
.ToList();

Selecting TreeView values based on a many to many relationships in a razor pages model

I have a Razor pages project that is trying to populate a Kendo TreeView (or any other TreeView) from a Database created with a Data Model.
The page I am working on contains apps that are in use, and the tree is reading a self referencing Organisations list so that we know what apps each organisation or department etc has access to.
I'm working on the Edit page in a razor app, so ~Pages\Apps\Edit.cshtml and ~Pages\Apps\Edit.cshtml.cs, with associated model pages as shown below.
These are the models involved, ignore RoleApps for this issue:
namespace FliveRetry.Models
{
public class Org
{
public int ID { get; set; }
public string OrgName { get; set; }
public int? ParentID { get; set; }
public bool? HasChildren { get; set; }
}
}
And
namespace FliveRetry.Models
{
public class App
{
public int ID { get; set; }
public string AppName { get; set; }
public string AppDescription { get; set; }
public int? DisplayOrder { get; set; }
public bool? Archived { get; set; }
public DateTime? Saved { get; set; }
public int? SavedBy { get; set; }
public ICollection<OrgAppJoin> OrgAppJoins { get; set; }
public ICollection<RoleAppJoin> RoleAppJoins { get; set; }
}
}
and the index model:
{
public class AppIndexData
{
public IEnumerable<App> Apps { get; set; }
public IEnumerable<Role> Roles { get; set; }
public IEnumerable<Org> Orgs { get; set; }
public IEnumerable<RoleAppJoin> RoleAppJoins { get; set; }
public IEnumerable<OrgAppJoin> OrgAppJoins { get; set; }
}
public class AssignedAppOrgData
{
public int OrgID { get; set; }
public string Org { get; set; }
public int? ParentID { get; set; }
public bool Assigned { get; set; }
public bool? HasChildren { get; set; }
}
public class SelectedAppOrgs
{
public int OrgID { get; set; }
}
public class SelectedAppOrgNames
{
public string OrgName { get; set; }
}
I have a page model to populate selected items into lists called selectedOrgs or selectedOrgNames that I can use in the view.
public class AppSelectPageModel : PageModel
{
//Load list for Selecting Orgs for Apps
public List<AssignedAppOrgData> AssignedAppOrgDataList;
public List<SelectedAppOrgs> selectedOrgs;
public List<SelectedAppOrgNames> selectedOrgNames;
public void PopulateAssignedAppOrgData(FliveRetryContext context, App app)
{
var allOrgs = context.Org;
var appOrgs = new HashSet<int>(
app.OrgAppJoins.Select(c => c.OrgID));
AssignedAppOrgDataList = new List<AssignedAppOrgData>();
selectedOrgs = new List<SelectedAppOrgs>();
selectedOrgNames = new List<SelectedAppOrgNames>();
foreach (var org in allOrgs)
{
AssignedAppOrgDataList.Add(new AssignedAppOrgData
{
OrgID = org.ID,
Org = org.OrgName,
Assigned = appOrgs.Contains(org.ID)
});
if (appOrgs.Contains(org.ID))
{
selectedOrgs.Add(new SelectedAppOrgs
{
OrgID = org.ID
});
selectedOrgNames.Add(new SelectedAppOrgNames
{
OrgName = org.OrgName
});
}
}
}
public void UpdateAppOrgs(FliveRetryContext context,
string[] selectedOrgs, App appToUpdate)
{
if (selectedOrgs == null)
{
appToUpdate.OrgAppJoins = new List<OrgAppJoin>();
return;
}
var selectedOrgsHS = new HashSet<string>(selectedOrgs);
var appOrgs = new HashSet<int>
(appToUpdate.OrgAppJoins.Select(c => c.Org.ID));
foreach (var org in context.Org)
{
if (selectedOrgsHS.Contains(org.OrgName.ToString()))
{
if (!appOrgs.Contains(org.ID))
{
appToUpdate.OrgAppJoins.Add(
new OrgAppJoin
{
AppID = appToUpdate.ID,
OrgID = org.ID
});
}
}
else
{
if (appOrgs.Contains(org.ID))
{
OrgAppJoin orgToRemove
= appToUpdate
.OrgAppJoins
.SingleOrDefault(i => i.OrgID == org.ID);
context.Remove(orgToRemove);
}
}
}
}
I then process them in OnGetAsync in Edit.cshtml.cs:
public async Task<IActionResult> OnGetAsync(int? id)
{
this.TreeData = GetOrgTreeData();
if (id == null)
{
return NotFound();
}
App = await _context.App
.Include(i => i.OrgAppJoins).ThenInclude(i => i.Org)
.Include(i => i.RoleAppJoins).ThenInclude(i => i.Role)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (App == null)
{
return NotFound();
}
PopulateAssignedAppRoleData(_context, App);
PopulateAssignedAppOrgData(_context, App);
return Page();
}
and OnPostAsync
public async Task<IActionResult> OnPostAsync(int? id, string[] selectedOrgs, string[] selectedRoles)
{
if (!ModelState.IsValid)
{
return Page();
}
var appToUpdate = await _context.App
.Include(i => i.OrgAppJoins).ThenInclude(i => i.Org)
.Include(i => i.RoleAppJoins).ThenInclude(i => i.Role)
.FirstOrDefaultAsync(s => s.ID == id);
if (await TryUpdateModelAsync<App>(
appToUpdate,
"app", // Prefix for form value.
c => c.AppName, c => c.AppDescription, c => c.DisplayOrder))
{
UpdateAppOrgs(_context, selectedOrgs, appToUpdate);
UpdateAppRoles(_context, selectedRoles, appToUpdate);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
UpdateAppOrgs(_context, selectedOrgs, appToUpdate);
UpdateAppRoles(_context, selectedRoles, appToUpdate);
PopulateAssignedAppOrgData(_context, App);
PopulateAssignedAppRoleData(_context, App);
return Page();
}
This works fine when using multiselect dropdowns and reads and writes correctly to the many to many join tables.
I am using kendo controls at the moment, but I'm happy to use generic if I can find a solution to my problem.
I need to have a treeview for the Org model to display as a multi select, so I have two examples I am trying to get working, that behave differently, the DropDownTreeview is not essential but in some pages it will be handy, the TreeView is essential.
The first is the TreeView:
#(Html.Kendo().TreeView()
.Name("selectedOrgNames")
.DataTextField("OrgName")
.Checkboxes(checkboxes => checkboxes
.Name("ischecked")
.CheckChildren(true))
.HtmlAttributes(new { style = "width:100%" })
.DataSource(d => d
.Read(read =>
read.Url("/Apps/Edit?handler=Read")
)
)
)
The second is the DropDownTreeview:
#(Html.Kendo().DropDownTree()
.Placeholder("Select ...")
.Name("selectedOrgs")
.DataTextField("OrgName")
.DataValueField("ID")
.Checkboxes(checkboxes => checkboxes
.Name("ischecked")
.CheckChildren(true))
.AutoClose(false)
.Value(Model.selectedOrgNames)
.HtmlAttributes(new { style = "width:100%" })
.DataSource(d => d
.Read(read =>
read.Url("/Apps/Edit?handler=Read")
)
)
)
Both examples read the datasource from here in edit.cshtml.cs:
public IActionResult OnGetRead(int? id)
{
var result = from e in _context.Org
where id.HasValue ? e.ParentID == id : e.ParentID == null
select new
{
id = e.ID,
hasChildren = (from q in _context.Org
where (q.ParentID == e.ID)
select q
).Count() > 0,
OrgName = e.OrgName,
selected = (from s in _context.OrgAppJoin
where (s.OrgID == e.ID) && (s.AppID == 2)// <--this works, this doesn't--> (s.AppID == app.ID)
select s
).Count() > 0,
ischecked = (from s in _context.OrgAppJoin
where (s.OrgID == e.ID) && (s.AppID == 2)// <--this doesn't work, this doesn't either-->
(s.AppID == app.ID)
select s
).Count() > 0
};
return new JsonResult(result);
}
My first issue is probably very simple, I'm new to this platform: I can't seem to find a way to get the value of the AppID from the page into the OnGetRead Module ( i have hard coded s.AppID == 2 as an test example to see if it works) I have tried all sorts of variables and other methods.
The id passed into OnPostAsync and OnGetAsync is the id of the App, but the id passed into the OnGetRead is the id of the Org, which is correct and works, but how do I use the AppID from the page to replace the number two in this line? where (s.OrgID == e.ID) && (s.AppID == 2)?
My second issue is getting the checkboxes to read and write.
The DropDownTree above writes to database correctly, but doesn't read and populate checkboxes.
The Treeview doesn't populate checkboxes or write to the database, however it DOES read the selected value from the join table for app number 2 (or any other number I manually insert) in OnGetRead, and displays different font colours etc for the correct items as selected, but not as checked (aschecked may not even be a valid call theere, but I can't find a reference for that).
I feel like I'm close but I have tried for so long to get this to work to no avail, including to and fro with telerik who have tried help but they don't really help much with modelling to your own code, just with static data, and they seem to be light on for a Razor app knowledgebase and keep giving examples of controllers with hard coded data.
Please advise if I need to split this into two questions or more but any help will be much appreciated
Thanks

EF code first FLUENT: many to many with additional field in jointable

I'm building a wiki, which has articles that consists of subarticles. An article can consist of multiple subarticles, and a subarticle can be attached to multiple articles. In the jointable there is a sortorder that defines the display of subarticles for a particular article.
The parent-article only consists of a title (and metadata), no text, all text is done through subarticles.
This sortorder in the jointable though, is where I'm stuck atm, I can't access it from my query. Hopefully someone can point me in the right direction.
Sidenote: I'm quite new in the whole MVC/EF world, even c#/vb and .NET is something I've only been working on since a few months and in my spare time.
I have these classes:
Article:
public class Article : BaseEntity
{
private ICollection<Category> _categories;
private ICollection<ArticleSubarticle> _subarticles;
public string Title { get; set; }
public int AuthorId { get; set; }
public DateTime CreationDate { get; set; }
public DateTime ?PublishDate { get; set; }
public DateTime ?ChangeDate { get; set; }
public bool Published { get; set; }
public virtual ICollection<Category> Categories
{
get { return _categories ?? (_categories = new List<Category>()); }
protected set { _categories = value; }
}
public virtual ICollection<ArticleSubarticle> Subarticles
{
get { return _subarticles ?? (_subarticles = new List<ArticleSubarticle>()); }
protected set { _subarticles = value; }
}
}
Subarticle
public class Subarticle : Article
{
private ICollection<Attachment> _attachments;
public string ArticleText { get; set; }
public int OriginalArticle { get; set; }
public bool Active { get; set; }
public virtual ICollection<Attachment> Attachments
{
get { return _attachments ?? (_attachments = new List<Attachment>()); }
protected set { _attachments = value; }
}
}
Jointable:
public class ArticleSubarticle : BaseEntity
{
public int ParentId { get; set; }
public int ChildId { get; set; }
public int SortOrder { get; set; }
public virtual Article Parent { get; set; }
public virtual Subarticle Child { get; set; }
}
They are mapped as follows:
Article
public ArticleMap () {
ToTable("Wiki_Article");
HasKey(a => a.Id);
Property(a => a.Title).HasColumnType("VARCHAR").HasMaxLength(250);
Property(a => a.AuthorId);
Property(a => a.PublishDate).IsOptional();
Property(a => a.ChangeDate).IsOptional();
HasMany(a => a.Categories)
.WithMany()
.Map(a => a.ToTable("Wiki_Article_Category_Mapping"));
}
Subarticle
public SubarticleMap()
{
ToTable("Wiki_Subarticle");
HasKey(sa => sa.Id);
Property(a => a.ArticleText)
.IsOptional()
.HasColumnType("TEXT");
Property(a => a.OriginalArticle)
.IsOptional();
HasMany(a => a.Attachments)
.WithMany()
.Map(a => a.ToTable("Wiki_Subarticle_Attachment_Mapping"));
}
Jointable
public ArticleSubarticleMap()
{
ToTable("Wiki_Article_Subarticle_Mapping");
HasKey(asa => new { asa.ParentId, asa.ChildId });
HasRequired(asa => asa.Parent)
.WithMany(asa => asa.Subarticles)
.HasForeignKey(asa => asa.ParentId);
}
This gets me the database as expected.
Now I want an article with its subarticles, that are ordered by the sortorder.
This query gets me the article with its subarticles, but I can't seem to figure out how to reach this sortorder in the Wiki_Article_Subarticle_Mapping table.
public IList<Article> getArticleByIdWithSortedSubarticles(int ArticleId)
{
var query = _articleRepository.Table;
query = query.Where(a => ArticleId == a.Id)
.Select(a => a);
var subarticles = query.ToList();
return subarticles;
}
Any ideas?
Thanks in advance!
Your query is not loading subarticles currently, so I guess they are lazily loaded. Try loading them explicitly like this:
public IList<Article> getArticleByIdWithSortedSubarticles(int ArticleId)
{
var query = _articleRepository.Table;
query = query.Where(a => ArticleId == a.Id)
.Select(a => new { article = a, subs = a.SubArticles.OrderBy(s => s.SortOrder) });
return query.AsEnumerable().Select(m => m.article).ToList();
}

Categories