How to replace null collections in input model with empty ones in ASP.NET core - asp.net-core

I have an input model from body:
public class User
{
public string Name { get; set; }
public List<string> Books { get; set; }
}
When user sends
{
"name": "John",
"books": null
}
I Books property is null, but I want it to be empty list. How to do it once for all the controllers and models?

ASP.NET Core knows how to bind the the parameters to the constructor.
This is not the best approach, but it is the one I decided to go with:
public class User
{
public User(string name, List<string> books)
{
Name = name;
Books = books ?? new List<string>();
}
public string Name { get; set; }
public List<string> Books { get; set; }
}

You can also use private field:
public class User
{
private List<string> _books;
public string Name { get; set; }
public List<string> Books
{
get => _books ?? new List<string>();
set { _books = value; }
}
}

Related

How to bind custom model class in mvc

I am new in MVC. I am working on a project where i have created a model class and also context class which is working good if i view the record in normal view.
but if i try to get the data in group by "Series_Name" and bind it into same model class it gives error. here is my code
Here is Model class and DBContextClass
[Table("tblvideo")]
public class TVSerial
{
[Key]
public Int64 Video_ID { get; set; }
public string Series_Name { get; set; }
public string Season_No { get; set; }
public string Episode_No { get; set; }
public string Episode_Name { get; set; }
public string Time_Duration { get; set; }
public string File_Url_480p { get; set; }
public string File_Url_720p { get; set; }
public string Description { get; set; }
public bool Is_Active { get; set; }
public string Image_Url_Small { get; set; }
public string Image_Url_Big { get; set; }
}
public class TvSerialContext : DbContext
{
public DbSet<TVSerial> TvSerials { get; set; }
}
Here is controller class:
public class TvSerialController : Controller
{
public ActionResult ListAllTvSerial()
{
try
{
TvSerialContext tvContext = new TvSerialContext();
List<TVSerial> tv = tvContext.TvSerials.ToList();
return View(tv);
}
catch (Exception ex)
{
return Content(ex.Message);
}
}
}
Above code works as expected, but if i am doing this :
public ActionResult ListAllSeason(string serial)
{
try
{
TvSerialContext tvContext = new TvSerialContext();
List<TVSerial> tv = tvContext.TvSerials.Where(tvs => tvs.Series_Name == serial).Distinct().ToList();
return View(tv);
}
catch (Exception ex)
{
return Content(ex.Message);
}
}
it return all rows , i just want single row from every series_name and custom field "Series_Name,Season_No,Image_Url_Big"
i don't know how to achieve this.
getting result :
Expected result:-
You could do this by creating a view model and using a .GroupBy() clause
public class TVSerialVM
{
public string SeriesName { get; set; }
public string SeasonNo { get; set; }
public string ImageUrl { get; set; }
}
and the query to project into your view model
List<TVSerialVM> model = tvContext.TvSerials.Where(t => t.Series_Name == serial)
.GroupBy(t => new { t.Series_Name, t.Season_No, t.Image_Url_Big })
.Select(t => new TVSerialVM
{
SeriesName = t.Key.Series_Name,
SeasonNo = t.Key.Season_No,
ImageUrl = t.Key.Image_Url_Big
}).ToList();
Side note: Your duplicating data in the database (the season number and the image url). You should consider moving the image urls to another table with a relationship to the season number.
The reason you are getting multiple values even though you are using distinct is the Distinct method does not know what "equal" is for TVSerial.
You can use Distinct with IEqualityComparer.
https://msdn.microsoft.com/en-us/library/vstudio/bb338049(v=vs.100).aspx
Distinct is not guaranteed to on custom objects it doesn't know what to compare. I have used this SO in the past to make my custom object work with Distinct.
Creating a distinct list of custom type in C#

New records inserted in foreign key table when inserting in parent table

I am new to Asp.net MVC and working on a simple blog application (Asp.Net MVC5, EF6) for learning.
I am using repository pattern for the solution architecture with EF Code first migration, Ninject for DI. On the client side, I am using jQuery Grid for Admin to manage Posts, Categories and Tags.
- Blog.Model: Post.cs, Category.cs, Tags.cs
public class Post
{
[Required(ErrorMessage = "Id is required")]
public int Id { get; set; }
[Required(ErrorMessage = "Title is required")]
[StringLength(500, ErrorMessage = "Title cannot be more than 500 characters long")]
public string Title { get; set; }
[Required(ErrorMessage = "Short description is required")]
public string ShortDescription { get; set; }
[Required(ErrorMessage = "Description is required")]
public string Description { get; set; }
public bool Published { get; set; }
[Required(ErrorMessage = "PostedOn date is required")]
public DateTime PostedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
[ForeignKey("Category")]
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual IList<Tag> Tags { get; set; }
}
public class Category
{
[Key]
public int CategoryId { get; set; }
[Required(ErrorMessage = "Category Name is required")]
[StringLength(500,ErrorMessage = "Category name length cannot exceed 500")]
public string Name { get; set; }
[Required(ErrorMessage = "Category Name is required")]
[StringLength(500, ErrorMessage = "Category name length cannot exceed 500")]
public string Description { get; set; }
[JsonIgnore]
public virtual IList<Post> Posts { get; set; }
}
public class Tag
{
public int Id { get; set; }
[Required(ErrorMessage = "Name is required")]
[StringLength(500, ErrorMessage = "Name length should not exceed 500 characters")]
public string Name { get; set; }
public string Description { get; set; }
[JsonIgnore]
public IList<Post> Posts { get; set; }
}
- Blog.Repository: BlogRepository, IBlogRepository, BlogContext
public interface IBlogRepository
{
int SavePost(Post post);
//Other methods...
}
public class BlogRepository : BlogContext, IBlogRepository
{
public BlogContext _db;
public BlogRepository(BlogContext db)
{
_db = db;
}
public int SavePost(Post post)
{
_db.Posts.Add(post);
_db.SaveChanges();
return post.Id;
}
//Other implementations...
}
public class BlogContext : DbContext, IDisposedTracker
{
public BlogContext() : base("BlogDbConnection") { }
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Category> Categories { get; set; }
public bool IsDisposed { get; set; }
protected override void Dispose(bool disposing)
{
IsDisposed = true;
base.Dispose(disposing);
}
- Blog.Web: AdminController.cs, NinjectWebCommon.cs
AdminController sends/consumes data in Json format.
public class AdminController : Controller
{
private readonly IBlogRepository _blogRepository;
public AdminController(IBlogRepository blogRepository)
{
_blogRepository = blogRepository;
}
//POST: /Admin/CreatePost
[HttpPost, ValidateInput(false)]
public ContentResult CreatePost([ModelBinder(typeof(PostModelBinder))] Post model)
{
string json;
ModelState.Clear();
if (TryValidateModel(model))
{
var id = _blogRepository.SavePost(model);
json = JsonConvert.SerializeObject(
new
{
id = id,
success = true,
message = "Post saved successfully."
});
}
else
{
json = JsonConvert.SerializeObject(
new
{
id = 0,
success = false,
message = "Post not saved."
});
}
return Content(json, "application/json");
}
}
public static class NinjectWebCommon
{
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<BlogContext>().ToSelf(); //This isn't helping either
kernel.Bind<IBlogRepository>().To<BlogRepository>();
}
}
I am using Custom Model Binding because I was getting validation exception while saving post since list of Categories and Tags received from grid do not map to actual objects in the application model. Therefore in the custom model binding, I am populating Post object with actual objects received from grid. This Post object is Sent to controller which Save to database using DbContext and Repository.
public class PostModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var post = (Post)base.BindModel(controllerContext, bindingContext);
**var blogRepository = new BlogRepository(new BlogContext());**//I think here I need to inject the dependency for BlogContext, but don't know how to do that.
if (post.Category != null)
{
post.Category = blogRepository._db.Categories.AsNoTracking().Single(c => c.CategoryId == post.Category.CategoryId);
}
var tags = bindingContext.ValueProvider.GetValue("Tags").AttemptedValue.Split(',');
if (tags.Length > 0)
{
post.Tags = new List<Tag>();
foreach (var tag in tags)
{
var id = int.Parse(tag.Trim());
post.Tags.Add(blogRepository._db.Tags.AsNoTracking().Single(t => t.Id == id));
}
}
if (bindingContext.ValueProvider.GetValue("oper").AttemptedValue.Equals("edit"))
post.ModifiedOn = DateTime.UtcNow;
else
post.PostedOn = DateTime.UtcNow;
return post;
}
}
Issue: When the Post is saved, data context inserts new rows for Category and Tags in their respective tables. The newly created post refers to new Category (Id:22) under Foreign key column.
Post:
Category:
Tag:
I think the reason for this is that when entity is saved it is attached to a different ObjectContext and I need to attach it to current context but do not know how? I found similar question asked before but there isn't an accepted answer to that. Any help would be greatly appreciated.
I was able to resolve above issue by attaching category and tags value to objectcontext manually, which indicates EF the changes it needs to make. This way it doesn't create new entries in Category and Tag's parent tables.
public int SavePost(Post post)
{
//attach tags to db context for Tags to tell EF
//that these tags already exist in database
foreach (var t in post.Tags)
{
_db.Tags.Attach(t);
}
//tell EF that Category already exists in Category table
_db.Entry(post.Category).State = EntityState.Modified;
_db.Posts.Add(post);
_db.SaveChanges();
return post.Id;
}
public void EditPost(Post post)
{
if (post == null) return;
//get current post from database
var dbPost = _db.Posts.Include(p => p.Tags).SingleOrDefault(p => p.Id == post.Id);
//get new list of tags
var newTags = post.Tags.Select(tag => new Tag() { Id = tag.Id, Name = tag.Name, Description = tag.Description }).ToList();
if (dbPost != null)
{
//get category from its parent table and assign to db post
dbPost.Category = _db.Categories.Find(post.Category.CategoryId); ;
//set scalar properties
_db.Entry(dbPost).CurrentValues.SetValues(post);
//remove tags from post in database
foreach (var t in dbPost.Tags.ToList())
{
if (!newTags.Contains(t))
{
dbPost.Tags.Remove(t);
}
}
//add tags to post in database
foreach (var t in newTags)
{
if (dbPost.Tags.All(p => p.Id != t.Id))
{
var tagInDb = _db.Tags.Find(t.Id);
if (tagInDb != null)
{
dbPost.Tags.Add(tagInDb);
}
}
}
}
//save changes
_db.SaveChanges();
}

Add Identity key to Sub Class in ravenDB

I have this two class :
public class BlogPost
{
public string Id { get; set; }
public string Title { get; set; }
public string Category { get; set; }
public string Content { get; set; }
public DateTime PublishedAt { get; set; }
public string[] Tags { get; set; }
public BlogComment[] Comments { get; set; }
}
public class BlogComment
{
public string Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
and add to documents like this :
// Creating a new instance of the BlogPost class
BlogPost post = new BlogPost()
{
Title = "Hello RavenDB",
Category = "RavenDB",
Content = "This is a blog about RavenDB",
Comments = new BlogComment[]
{
new BlogComment() {Title = "Unrealistic", Content = "This example is unrealistic"},
new BlogComment() {Title = "Nice", Content = "This example is nice"}
}
};
is there a way that my comments have Identity key like my BlogPost class?
and another question:
is there a way that get comment object without using post. something like this :
using( var session = doc.OpenSession() )
{
return session.Load<BlogComment>( ID );
}
or
using( var session = doc.OpenSession() )
{
return ( from comment in session.Query<BlogComment>()
where comment.Title == title
select comment ).FirstOrDefault();
}
You can just have an integer property on BlogPost, increment that and set that value whenever you add a new comment. That would give you identity style ids within the scope of the post.

Joining documents with Raven 2.0 - Could not find index?

I'm new to RavenDB and I have a question about joining two documents with Raven 2.0
I found this page http://daniellang.net/joining-documents-in-ravendb-2-0/ that helped me in finding a solution to join two documents.
Please see my code first (it compiles)
internal class Program
{
private static void Main(string[] args)
{
using (var store = new EmbeddableDocumentStore {DataDirectory = #"C:\temp\ravendata"}.Initialize())
{
using (var session = store.OpenSession())
{
var products = session.Query<Product, UserProducts>()
.AsProjection<UserProductProjection>()
.ToList();
}
}
}
}
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public string UserId { get; set; }
}
public class User
{
public string Id { get; set; }
public string Name { get; set; }
}
public class UserProductProjection
{
public string Id { get; set; }
public string UserName { get; set; }
public string ProductName { get; set; }
public string ProductId { get; set; }
}
internal class UserProducts : AbstractIndexCreationTask<Product, UserProductProjection>
{
public UserProducts()
{
Map = products => from product in products
select new
{
UserName = LoadDocument<User>(product.UserId).Name,
ProductName = product.Name,
ProductId = product.Id
};
Index(projection => projection.ProductId, FieldIndexing.Analyzed);
Index(projection => projection.ProductName, FieldIndexing.Analyzed);
Store(projection => projection.UserName, FieldStorage.Yes);
}
}
Unfortunately it doesn't work :(
Raven.Database.Exceptions.IndexDoesNotExistsException was unhandled
HResult=-2146233088
Message=Could not find index named: UserProducts
Source=Raven.Database
StackTrace:
at Raven.Database.DocumentDatabase.<>c__DisplayClass9a.<Query>b__90(IStorageActionsAccessor actions) in c:\Builds\RavenDB-Stable\Raven.Database\DocumentDatabase.cs:line 1100
....
I really have NO clue at all!! Google doesn't help me on this subject as well, because it is still pretty new as I found out.
If someone has a hint or a solution I would be very grateful.
While you have defined the index you haven't created it in ravendb.
See Defining a static index but basically you need....
IndexCreation.CreateIndexes(typeof(UserProducts).Assembly, documentStore);

How to log in my custom user table in MVC 4?

I want to use my custom User table in MVC 4 code first application. I defined advance User table in my database context:
public class MyDatabase : DbContext
{
public DbSet<User> UserSet { get; set; }
public DbSet<News> NewsSet { get; set; }
...
}
Model is like:
public class User
{
[Key]
public int Id{ get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string SurName { get; set; }
...
}
When application start, it calls this:
WebSecurity.InitializeDatabaseConnection("MyDatabase", "Users", "Id", "UserName", autoCreateTables: true);
In controller I use Add(entity) to save user entity. After saving I want to log in user. But it does not work:
[HttpPost]
public ActionResult Register(User user)
{
var result = MyService.SaveUser(user);
WebSecurity.Login(result.UserName, result.Password, true);
return RedirectToAction("Index", "Profile", new { id = result.Id });
}
After saving user, it's data stored in my database, but it can not log in. How should I do?
Edit:
Is it right to save User entity with my business method? Or I must do it only with
WebSecurity.CreateUserAndAccount()?
If I can use my own save method, how to save password in database?
You could just use forms authentication directly.
[HttpPost]
public ActionResult Register(User user)
{
var result = MyService.SaveUser(user);
SignIn(result.Id, "");
return RedirectToAction("Index", "Profile", new { id = result.Id });
}
public void SignIn(string accountId, string roles)
{
var authTicket = new FormsAuthenticationTicket(
1,
accountId,
DateTime.Now,
DateTime.Now.AddMinutes(20),
false,
roles
);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Current.Response.Cookies.Add(authCookie);
}
Here is a user class that will help you with password issue. It relies on BCrypt
public class UserAccount
{
public string Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string Password
{
get { return PasswordHash; }
set { PasswordHash = HashPassword(value); }
}
public string PasswordHash { get; private set; }
public List<string> Roles { get; set; }
public string AuthenticationRoles
{
get { return Roles == null ? "" : String.Join(",", Roles.Select(x => x.ToString())); }
}
public bool IsActive { get; set; }
public string Name { get; set; }
public bool PasswordIsValid(string password)
{
bool matches = BCrypt.Net.BCrypt.Verify(password, Password);
return matches;
}
private string HashPassword(string value)
{
return BCrypt.Net.BCrypt.HashPassword(value);
}
}