I have two entity look like this.
public class Post{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "")]
[MaxLength(100, ErrorMessage = "")]
public string Name { get; set; }
[ForeignKey("ParentPost")]
public int? ParentPostId { get; set; }
public virtual Post ParentPost { get; set; }
public ICollection<Post> SubPosts { get; set; }
}
public class User{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "")]
[MaxLength(100, ErrorMessage = "")]
public string Name { get; set; }
[ForeignKey("Post")]
public int PostId { get; set; }
public virtual Post Post { get; set; }
}
I have a list of Users, I want sort Users with level of Post.
for example :
ID---------Name------ParentID
1----------1-----------NULL
2----------1.1----------1
3----------2-----------NULL
4----------1.1.1--------2
5----------1.2----------3
Sort By Level.
1
2
1.1
1.2
1.1.1
UPDATE :
Here is an alternative to my previous answer that don't need you to add properties in Post. Create extension method for Post entity to count level and number based on Post's Name. I assume you give it a consistent naming.
public static class ExtensionMethods
{
public static int Level(this Post post)
{
var name = post.Name;
var level = 0;
level = name.Count(o => o == '.');
return level;
}
public static int number(this Post post)
{
var name = post.Name;
var number = 0;
var split = name.Split('.');
return split.Length > 1 ? int.Parse(split[split.Length - 1]) : int.Parse(name);
}
}
Then you can sort using linq like:
var posts = new List<Post>();
posts.Add(new Post { Name = "1.2" });
posts.Add(new Post { Name = "1.1.1" });
posts.Add(new Post { Name = "2" });
posts.Add(new Post { Name = "1.1" });
posts.Add(new Post { Name = "1" });
foreach (var post in posts)
{
Console.WriteLine(post.Name);
}
Console.WriteLine("\n\nAfter sorting");
var sorted = posts.OrderBy(p => p.Level()).ThenBy(p => p.number());
foreach (var post in sorted)
{
Console.WriteLine(post.Name);
}
PREVIOUS ANSWER :
I can't think of proper solution with current properties of Post entity. I thought of some logic but those aren't perfect, will break in some conditions.
The proper solution I can think is require adding two more properties in Post. Level property to store Post level, and Number property to store the number after last point in Name (1 for post name 1.1.1, 10 for post name 2.1.10, etc..). With those two additional properties you can sort Post like following:
Posts.OrderBy(p => p.Level)
.ThenBy(p => p.Number);
Related
So I am trying to create my first block. The idea of this block is to get latest news from an api end point and then show it on different pages on the website.
What I have understood is this
Create a block type, something like this
public class NewsBlock : BlockData
{
[CultureSpecific]
[Display(
Name = "Heading",
Description = "Add a heading.",
GroupName = SystemTabNames.Content,
Order = 1)]
public virtual String Heading { get; set; }
}
Then I create a model for my Block
public class LatestNewsViewModel
{
public NewsBlock NewsBlock { get; private set; }
public IEnumerable<dynamic> LatestNews { get; set; }
public LatestNewsViewModel(NewsBlock latestNewsBlock, IEnumerable<dynamic> latestNews)
{
NewsBlock = latestNewsBlock;
LatestNews = latestNews;
}
}
Then I creata a block controller and in the index action I get data from my api and fill the block container data
Then I create a partial view and then from controller pass data into the view
Then from the dashboard I can add my block where ever I want on the site
Is this the way to do it? Or am I missing something?
That seem about correct. Please note there are many ways and opinions on how to get your data from the content model through the controller to the actual view. The example below is just the most simple scenario I can come up with.
public class NewsBlock : BlockData
{
[CultureSpecific]
[Display(
Name = "Heading",
Description = "Add a heading.",
GroupName = SystemTabNames.Content,
Order = 1)]
public virtual String Heading { get; set; }
}
The controller
public class NewsBlockController : BlockController<NewsBlock>
{
// GET: NewsBlock
public override ActionResult Index(NewsBlock currentBlock)
{
// apistuff
ApiModelWhatever returnFromApi = "whatever";
var model = new LatestNewsViewModel(currentBlock, returnFromApi);
return PartialView(model);
}
}
ViewModel
public class LatestNewsViewModel
{
public string Heading { get; private set; }
public ApiModelWhatever ReturnFromApi { get; private set; }
public LatestNewsViewModel(NewsBlock latestNewsBlock, ApiModelWhatever returnFromApi)
{
Heading = latestNewsBlock.Heading;
ReturnFromApi = returnFromApi;
}
}
View
#model LatestNewsViewModel
<h2>#Html.PropertyFor(model => model.Heading)</h2>
I have the following classes
public class Device
{
public int Id { get; set; }
public string MacAddress { get; set; }
public List<Input> Inputs { get; set; }
}
public abstract class Input
{
public int Id { get; set; }
public bool Active { get; set; }
}
public class InputCounter : Input
{
public bool Incremental { get; set; }
}
public class InputState : Input
{
public List<State> PossibleStates { get; set; }
}
public class State
{
public int Id { get; set; }
public string Description { get; set; }
}
and need to get a Device (with all its InputCounters and InputStates with its respectives States) by the MacAddress.
I'm querying the database this way
return _context.Devices.Select(device =>
new Device()
{
Id = device.Id,
MacAddress = _myMacAddress,
Inputs = device.Inputs.Where(input => input is InputCounter).Select(input =>
new InputCounter()
{
Id = input.Id,
Active = input.Active,
Incremental = (input as InputCounter).Incremental
} as Input
)
.Union(
device.Inputs.Where(input => input is InputState).Select(input =>
new InputState()
{
Id = input.Id,
Active = input.Active,
PossibleStates = (input as InputState).PossibleStates
}
)
).ToList<Input>()
}
).FirstOrDefault();
but it causes an ArgumentNullException.
What is the right way to do this query?
Obs: If I comment the line PossibleStates = (input as InputState).PossibleStates, the error doesn't happen, so I think the problem may be related with typecast. However, the line Incremental = (input as InputCounter).Incremental don't cause any problem.
In Database, I have:
Device table with Id and MacAddress fields;
Input table, with Id, DeviceId, Active, Incremental and Discriminator fields;
State table, with Id, InputStateId and Description fields.
I know this has been asked millions of times and I've had it myself hundreds of times, but for some reason I can't fix this one.
I get the well known error:
The UPDATE statement conflicted with the FOREIGN KEY constraint ...
All my tables in my database are cascaded when an insert or delete is done.
Now on to the error:
I want to update an admins table (administrator accounts) that is linked to a cultures table (for languages).
Everything is filled in correctly. and thus we get to the following code:
[HttpPost]
public ActionResult Edit(Admins admins)
{
if (!ModelState.IsValid)
{
return View(admins);
}
admins.cultures_id = admins.Cultures.id;
_unitOfWork.AdminsRepository.Update(admins);
_unitOfWork.Save();
return RedirectToAction("Index", "Overview", new { area = "Admin" });
}
I first set the cultures id of my admin object/entity equal to that of the id in the cultures table that is linked:
admins.cultures_id = admins.Cultures.id;
I then fill update the table:
_unitOfWork.AdminsRepository.Update(admins);
The method update holds this code:
public virtual void Update(TEntity entityToUpdate)
{
DbSet.Attach(entityToUpdate);
ArtWebShopEntity.Entry(entityToUpdate).State = EntityState.Modified;
}
So far so good, but then, when I actually want to save the admin:
_unitOfWork.Save();
That save method holds this code:
public void Save() {
try
{
_artWebshopEntity.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", validationErrors.Entry.Entity.GetType().Name, validationErrors.Entry.State);
foreach (var validationError in validationErrors.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"", validationError.PropertyName, validationError.ErrorMessage);
}
}
throw; // Will do something here later on...
}
}
And at the SaveCHanges method I get the error. I know what it means but I can't seem to fix it. I've tried all the things I know that could cause it.
Edit
I only want to update the admin values, so I don't want to update the culture values.
This is the query:
update [dbo].[Admins]
set [login] = 'Herve' /* #0 */,
[password] = null,
[salt] = null,
[email] = 'xxxxx.xxx#glevin.be' /* #1 */,
[permissions] = 'administrator' /* #2 */,
[attempts] = 4 /* #3 */,
[locked] = 0 /* #4 */,
[cultures_id] = 0 /* #5 */
where ([id] = 1 /* #6 */)
So, the cultures_id is the issue. I've now did the following:
var updateAdmin = new Admins
{
attempts = admins.attempts,
cultures_id = admins.cultures_id,
email = admins.email,
locked = admins.locked,
login = admins.login,
id = admins.id,
password = admins.password,
permissions = admins.permissions,
salt = admins.salt,
};
And that works, but the moment I add the Cultures object to the mix, it crashes and gives me the reference error. So it boils down to, how the frack do I update a table with a foreign key to another table to also needs to be updated?
Edit II
My admin and cultures entity (database first), also image of database in sql management studio:
Admin class:
public partial class Admins
{
public int id { get; set; }
public string login { get; set; }
public string password { get; set; }
public string salt { get; set; }
public string email { get; set; }
public string permissions { get; set; }
public Nullable<int> attempts { get; set; }
public Nullable<bool> locked { get; set; }
public Nullable<int> cultures_id { get; set; }
public virtual Cultures Cultures { get; set; }
}
Cultures class:
public partial class Cultures
{
public Cultures()
{
this.Categories_local = new HashSet<Categories_local>();
this.Menu_items_local = new HashSet<Menu_items_local>();
this.Products_local = new HashSet<Products_local>();
this.Subcategories_local = new HashSet<Subcategories_local>();
this.Webpages_local = new HashSet<Webpages_local>();
this.Admins = new HashSet<Admins>();
}
public int id { get; set; }
public string name { get; set; }
public string display_name { get; set; }
public virtual ICollection<Categories_local> Categories_local { get; set; }
public virtual ICollection<Menu_items_local> Menu_items_local { get; set; }
public virtual ICollection<Products_local> Products_local { get; set; }
public virtual ICollection<Subcategories_local> Subcategories_local { get; set; }
public virtual ICollection<Webpages_local> Webpages_local { get; set; }
public virtual ICollection<Admins> Admins { get; set; }
}
I've gotten it to work!
The problem was that in the edit page the final field was the field that showed the name of the culture that corresponded with the id of the admin.
In other words I did the following:
#Html.EditorFor(model => model.Cultures.name)
But this wasn't the correct way.
In order to show the name of the culture but in the code pass the culture id, I used a #Html.DropDownListFor()-element.
The problem with this however was that my original model, Admins, didn't have a IEnumerable object that I could pass to the dropdownlist element in my view. I had to create a new model which I named CreateAdminModel, The new model looks like this:
public class CreateAdminModel
{
public CreateAdminModel() { }
public CreateAdminModel(IEnumerable<SelectListItem> cultures) { Cultures = cultures; }
public CreateAdminModel(Admins admin) { Admin = admin; }
public CreateAdminModel(IEnumerable<SelectListItem> cultures, Admins admin)
{
Cultures = cultures;
Admin = admin;
}
public IEnumerable<SelectListItem> Cultures { get; set; }
public Admins Admin { get; internal set; }
}
It has an Admin object created by the entity framework (database first).
With that new model I created the following method:
private CreateAdminModel CreateAdminWithcultureDetails(Admins admin = null)
{
var cultureItems = (_unitOfWork.CulturesRepository.Get()).ToArray();
var cultureList = new List<SelectListItem>();
for (int i = 0; i < cultureItems.Count(); i++) cultureList.Add(new SelectListItem { Text = cultureItems[i].name, Value = cultureItems[i].id.ToString() });
return admin != null ? new CreateAdminModel(cultureList, admin) : new CreateAdminModel(cultureList);
}
This fills the dropdown list with the cultures and depending on whether or not an admin object was passed also adds an admin object.
Now I can use this model in the view and correctly fill both the dropdown list and the admin if necessary.
I'm going to do the same for the other things that have to use CRUD.
In order to create a more elegant solution I'm curios to know your suggestion about a solution to persist a collection.
I've a collection stored on DB.
This collection go to a webpage in a viewmodel.
When the go back from the webpage to the controller I need to persist the modified collection to the same DB.
The simple solution is to delete the stored collection and recreate all rows.
I need a more elegant solution to mix the collections and delete not present record, update similar records ad insert new rows.
this is my Models and ViewModels.
public class CustomerModel
{
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<PreferredAirportModel> PreferedAirports { get; set; }
}
public class AirportModel
{
public virtual string Id { get; set; }
public virtual string AirportName { get; set; }
}
public class PreferredAirportModel
{
public virtual AirportModel Airport { get; set; }
public virtual int CheckInMinutes { get; set; }
}
// ViewModels
public class CustomerViewModel
{
[Required]
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<PreferredAirporViewtModel> PreferedAirports { get; set; }
}
public class PreferredAirporViewtModel
{
[Required]
public virtual string AirportId { get; set; }
[Required]
public virtual int CheckInMinutes { get; set; }
}
And this is the controller with not elegant solution.
public class CustomerController
{
public ActionResult Save(string id, CustomerViewModel viewModel)
{
var session = SessionFactory.CurrentSession;
var customer = session.Query<CustomerModel>().SingleOrDefault(el => el.Id == id);
customer.Name = viewModel.Name;
// How can I Merge collections handling delete, update and inserts ?
var modifiedPreferedAirports = new List<PreferredAirportModel>();
var modifiedPreferedAirportsVm = new List<PreferredAirporViewtModel>();
// Update every common Airport
foreach (var airport in viewModel.PreferedAirports)
{
foreach (var custPa in customer.PreferedAirports)
{
if (custPa.Airport.Id == airport.AirportId)
{
modifiedPreferedAirports.Add(custPa);
modifiedPreferedAirportsVm.Add(airport);
custPa.CheckInMinutes = airport.CheckInMinutes;
}
}
}
// Remove common airports from ViewModel
modifiedPreferedAirportsVm.ForEach(el => viewModel.PreferedAirports.Remove(el));
// Remove deleted airports from model
var toDelete = customer.PreferedAirports.Except(modifiedPreferedAirports);
toDelete.ForEach(el => customer.PreferedAirports.Remove(el));
// Add new Airports
var toAdd = viewModel.PreferedAirports.Select(el => new PreferredAirportModel
{
Airport =
session.Query<AirportModel>().
SingleOrDefault(a => a.Id == el.AirportId),
CheckInMinutes = el.CheckInMinutes
});
toAdd.ForEach(el => customer.PreferedAirports.Add(el));
session.Save(customer);
return View();
}
}
My environment is ASP.NET MVC 4, nHibernate, Automapper, SQL Server.
Well, if "elegant" is just "don't clear and recreate all" (untested) :
var airports = customer.PreferedAirports;
var viewModelAirports = viewModel.PreferredAirports;
foreach (var airport in airports) {
//modify common airports
var viewModelAirport = viewModelAirports.FirstOrDefault(m => m.AirportId == airport.AirportId);
if (viewModelAirport != null) {
airport.X = viewModelAirport.X;
airport.Z = viewModelAirport.Z;
//remove commonAirports from List
viewModelAirports.Remove(viewModelAirport);
continue;
}
//delete airports not present in ViewModel
customer.PreferedAirports.Remove(airport);
}
//add new airports
foreach (var viewModelAirport in viewModelAirports) {
customer.PreferedAirports.Add(new PreferredAirportModel {
Airport = session.Query<AirportModel>().SingleOrDefault(a => a.Id == el.AirportId),
CheckInMinutes = el.CheckInMinutes
});
}
session.Save(customer);
I'm just learning ASP.NET MVC 3, And recently I tried a lot of times to pass arrays/lists/ICollections etc. but couldn't. everytime the list was empty.
For example, the current project:
Model:
public class Video
{
public int VideoID { get; set; }
public string Name { get; set; }
public ICollection<string> Tags { get; set; }
}
Initializer - Seed:
protected override void Seed(DatabaseContext context)
{
var videos = new List<Video>
{
new Video {
Name = "Video01",
Tags = new List<string> { "tag1", "tag2" },
};
videos.ForEach(s => context.Videos.Add(s));
context.SaveChanges();
base.Seed(context);
}
In the view: I do get the Name property, but the Tags are completely empty.
In the debug I get Tags - Count: 0.
This is not the first time it happens to me, to be honest it happens every single time when I try to pass those kind of stuff. a bit of info about the project:
ASP.NET MVC 3, Entity-Framework:Code First, SqlServerCe.4.0.
Crean an entity Tag
public class Video
{
public int VideoID { get; set; }
public string Name { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public int VideoId { get; set; }
public string TagText { get; set; }
}
or store tags to one field separated with comma /semicolon or whatever fits for your solution
By default Entity Framework doesn't load associations of an entity, you need to specify it explicitly:
var videos = context.Videos.Include("Tags");