Thursday 31 December 2015

Cloning Reddit to learn ASP.NET 5 - Part 2

In the previous post we got started in our quest to build a Reddit Clone. We ended up with a broken project so let's try to fix that.

The first thing is to add a UserDetails class and then link it to a RedditUser class, which will also need to be added to the Models folder.

UserDetails.cs
namespace Reddit.Models
{
    public class UserDetails
    {
        public int UserId { get; set; }
        public long LinkKarma { get; set; }
        public long CommentKarma { get; set; }
        public RedditUser User { get; set; }      
    }
}

RedditUser.cs
namespace Reddit.Models
{
    public class RedditUser
    {
        public int RedditUserId { get; set; }
        public string Nick { get; set; }
        public UserDetails UserDetails { get; set; }
    }
}

We will further modify the RedditUser class to allow users to login, that's the reason for the rather small class. If this were a real clone then we'd probably want to use Guids for the Keys.

At this point we should have a working project again, which can be verified by building and/or debugging it.

We now want to create the database with the RedditUser and UserDetails and the right relationships between them, namely a 1 to 1 relationship.

Firstly, modify the Startup.cs file so that the Configuration property is static, which will allow us access to this property without having to instantiate the class again and to add the EntityFramework service.
public static IConfigurationRoot Configuration { get; set; }

public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();
            services.AddEntityFramework()
                .AddSqlServer()
                .AddDbContext<RedditContext>();      
        }
Edit the appsettings.json file
 "Data": {
    "RedditContextConnection": "Server=(localdb)\\MSSQLLocalDB;Database=Reddit;Trusted_Connection=true;MultipleActiveResultSets=true"
  }
Finally let's edit the Context class (RedditContext.cs)
using Microsoft.Data.Entity;

namespace Reddit.Models
{
    public class RedditContext : DbContext
    {
        public RedditContext()
        {
            //Database.EnsureCreated();
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var connection = Startup.Configuration["Data:RedditContextConnection"];
            optionsBuilder.UseSqlServer(connection);
            base.OnConfiguring(optionsBuilder);          
        } 

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<RedditUser>()
                .HasKey(x => x.RedditUserId);

            modelBuilder.Entity<UserDetails>()
                .HasKey(x => x.UserId);
            
            modelBuilder.Entity<RedditUser>()
                .HasOne(x => x.UserDetails)
                .WithOne(x => x.User);

            base.OnModelCreating(modelBuilder);
        }
    }
}

The OnConfiguring method sets up the connection String to the database.

The OnModelCreating method sets up the relationship between the tables in the database, it's worth mentioning here that I'm using the Fluent API to do this, but it's also possible to do it via data annotations.

I'm torn as to what the best way of doing this is, as both have advantages and disadvantages. I suspect that I will stick to the this syntax for the time being, even if I probably won't need any of the features that it provides over and above data annotations

I have added explicitly added the Primary Keys for each table, this is only necessary for UserDetails as the Key is different to the table name (I think) Now we can add create the database, from src\Reddit run the following command, which will create a Migration called Start:
 dnx ef migrations add Start 
Note that a new folder called Migrations will be created and populated with two files: A migration file, normally named DateTime_MigrationName and a model Snapshot.

When I first run this, it created the database. This was due to the commented out logic in the constructor, which wasn't commented out, once it was commented out, the expected behaviour resumed and we need to run the following command to apply the changes:
dnx ef database update
Let's add a few more models for SubReddit, Posts and Comments:

using System.Collections.Generic;

namespace Reddit.Models
{
    public class SubReddit
    {
        public int SubRedditId { get; set; }
        public string Name { get; set; }
        public bool IsPublic { get; set; }
        public List<Post> Posts { get; set; }
        public List<RedditUser> { get; set; }
        public bool IsBanned { get; set; }

    }
}
using System.Collections.Generic;

namespace Reddit.Models
{
    public class Post 
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public string Link { get; set; }
        public int UpVotes { get; set; }
        public int DownVotes { get; set; }
        public List<Comment> Comments { get; set; }
        public RedditUser Author { get; set; }
        public int AuthorId { get; set; }
        public SubReddit SubReddit { get; set; }
        public int SubRedditId { get; set; }


    }
}
namespace Reddit.Models
{
    public class Comment
    {
        public int CommentId { get; set; }
        public string Title { get; set; }
        public string Text { get; set; }
        public int UpVotes { get; set; }
        public int DownVotes { get; set; }
        public RedditUser Author { get; set; }
        public int AuthorId { get; set; }
    }
}
I have added Foreign Keys to Comment and Post to make querying easier. We create another migration called Core and apply it
dnx ef migrations add Core
dnx ef database update
Oddly nothing happens, why?

No comments:

Post a Comment