Skip to content

Commit

Permalink
Refactored controller and views into generic reusable class
Browse files Browse the repository at this point in the history
  • Loading branch information
badcommandorfilename committed Aug 4, 2016
1 parent 658371d commit a152572
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 138 deletions.
18 changes: 18 additions & 0 deletions muleapp/Controllers/GenericControllers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Mule.Controllers
{
[Route("")]
[Route("apphosts")]
public class AppHostController : ItemController<AppHost>
{
public AppHostController(IRepository<AppHost> repository) : base(repository)
{

}
}
}
19 changes: 0 additions & 19 deletions muleapp/Controllers/HomeController.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,72 +1,74 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860

namespace Mule.Controllers
{
[Route("")]
[Route("apphosts")]
public class AppHostController : Controller
public abstract class ItemController<T> : Controller where T : class
{
readonly IRepository<AppHost> AppHosts;
public AppHostController(IRepository<AppHost> apphosts)
{
AppHosts = apphosts;
}
readonly IRepository<T> Items;

/// <summary>
/// Called before each request
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuting(ActionExecutingContext context)
public ItemController(IRepository<T> repository)
{
var sw = new Stopwatch();
sw.Start();
ViewData["Title"] = typeof(AppHost).Name;
ViewData["Ponies"] = new Random().NextDouble() > 0.9;
ViewData["Stopwatch"] = sw;
base.OnActionExecuting(context);
Items = repository;
}

[Route("")]
[Route("index")]
public IActionResult Index()
{
return View(AppHosts.Read());
return View("Items/Index",Items.Read());
}

[HttpPost]
[Route("create")]
public IActionResult Create([Bind] AppHost item)
public IActionResult Create([Bind] T item)
{
var existing = (from x in AppHosts.Read()
var existing = (from x in Items.Read()
where x.Equals(item)
select x).FirstOrDefault();
if (existing != null)
{
AppHosts.Update(existing, item);
Items.Update(existing, item);
}
else
{
AppHosts.Create(item);
Items.Create(item);
}
return RedirectToAction("Index");
}

[HttpPost]
[Route("delete")]
public IActionResult Delete([Bind] AppHost item)
public IActionResult Delete([Bind] T item)
{
var existing = from x in AppHosts.Read()
where x.Equals(item)
select x;
foreach(var i in existing)
var existing = from x in Items.Read()
where x.Equals(item)
select x;
foreach (var i in existing)
{
AppHosts.Delete(i);
Items.Delete(i);
}
return RedirectToAction("Index");
}


/// <summary>
/// Called before each request
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuting(ActionExecutingContext context)
{
var sw = new Stopwatch();
sw.Start();
ViewData["Title"] = typeof(AppHost).Name;
ViewData["Ponies"] = new Random().NextDouble() > 0.9;
ViewData["Stopwatch"] = sw;
base.OnActionExecuting(context);
}
}
}
69 changes: 9 additions & 60 deletions muleapp/Models/AppHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,77 +13,26 @@ public class AppHost
{
[Key]//Unique id for object
public string URL { get; set; } = "";

string version = "";
[Required] //Property cannot be null
public string Version { get; set; } = "";
public string Version { get { return version; } set { version = value ?? ""; } }

[DataType(DataType.MultilineText)] //Input style/formatting hint
public string Description { get; set; } = "";

[HiddenInput(DisplayValue = false)] //Property won't show in edit form
public DateTime Updated { get; private set; } = DateTime.Now;

public DateTime Touch() => Updated = DateTime.Now;
public AppHost()
{

}

/// Equality Comparer (determines if row will be overwritten or created)
public override bool Equals(object obj) => URL == (obj as AppHost)?.URL;

/// Unique key definition (use primary key)
public override int GetHashCode() => URL.GetHashCode();
}

/// <summary>
/// Access to SQLite repository of AppHosts from SQLiteContext
/// </summary>
public class AppHostRepository : IRepository<AppHost>
{
readonly SQLiteContext _context;
public AppHostRepository(SQLiteContext context)
{
_context = context;
try
{
context.Database.EnsureCreated();
}
catch (InvalidOperationException ex)
when (ex.Message.Contains("The process has no package identity"))
{
//Ignore this - sqlite needs signature
}
}

public AppHost Create(AppHost item)
{
_context.Add(item);
_context.SaveChanges();
return item;
}

public IEnumerable<AppHost> Read()
{
return _context.AppHosts.AsEnumerable();
}

public AppHost Update(AppHost existing, AppHost item = null)
{
if(item != null)
{
var vals = ModelService.GetPropertyValues(item);
foreach(var prop in ModelService.GetPropertyInfo(typeof(AppHost)))
{
var newprop = vals.Where(x => x.Name == prop.Name).FirstOrDefault()?.Value;
prop.SetValue(existing, newprop);
}
}

existing.Touch();
_context.Update(existing);
_context.SaveChanges();
return existing;
}

public int Delete(AppHost item)
{
_context.Remove(item);
return _context.SaveChanges();
}
}

}
61 changes: 59 additions & 2 deletions muleapp/Services/IRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Mule
{
Expand All @@ -7,12 +9,67 @@ namespace Mule
/// Use this interface for dependency injection to mock or abstract persistence objects during testing
/// </summary>
/// <typeparam name="T">Repository model</typeparam>
public interface IRepository<T> where T: class
public interface IRepository<T> where T : class
{
IEnumerable<T> Read();
T Create(T item);
T Update(T existing, T item = null);
int Delete(T item);
}


/// <summary>
/// Access to SQLite repository from SQLiteContext
/// </summary>
public class Repository<T> : IRepository<T> where T : class
{
readonly SQLiteContext _context;
public Repository(SQLiteContext context)
{
_context = context;
try
{
context.Database.EnsureCreated();
}
catch (InvalidOperationException ex)
when (ex.Message.Contains("The process has no package identity"))
{
//Ignore this - sqlite needs signature
}
}

public T Create(T item)
{
_context.Add(item);
_context.SaveChanges();
return item;
}

public IEnumerable<T> Read()
{
return _context.Set<T>().AsEnumerable();
}

public T Update(T existing, T item = null)
{
if (item != null)
{
var vals = ModelService.GetPropertyValues(item);
foreach (var prop in ModelService.GetPropertyInfo(typeof(AppHost)))
{
var newprop = vals.Where(x => x.Name == prop.Name).FirstOrDefault()?.Value;
prop.SetValue(existing, newprop);
}
}
_context.Update(existing);
_context.SaveChanges();
return existing;
}

public int Delete(T item)
{
_context.Remove(item);
return _context.SaveChanges();
}
}
}
45 changes: 34 additions & 11 deletions muleapp/Services/ModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Mvc;

namespace Mule
{
Expand Down Expand Up @@ -45,17 +46,7 @@ public static string ToJSON(object model)
/// <returns></returns>
public static IEnumerable<PropertyInfo> GetPropertyInfo(Type type)
{
//For collections (Arrays and Enumerables), get the properties of the inner type
if (typeof(IEnumerable).IsAssignableFrom(type))
{
var innertype = from i in type.GetInterfaces()
where i.IsConstructedGenericType
&& i.GetGenericTypeDefinition() == typeof(IEnumerable<>)
select i.GetGenericArguments()[0];
return innertype.First().GetProperties();
}
//For normal types, get the public properties
return type.GetProperties();
return ModelType(type).GetProperties();
}

/// <summary>
Expand All @@ -70,5 +61,37 @@ public static IEnumerable<PropertyValue> GetPropertyValues(object model)
yield return new PropertyValue(p, model);
}
}

/// <summary>
/// Detects model type from object or collection
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static Type ModelType(Type type)
{
//For collections (Arrays and Enumerables), get the properties of the inner type
if (typeof(IEnumerable).IsAssignableFrom(type))
{
var innertype = from i in type.GetInterfaces()
where i.IsConstructedGenericType
&& i.GetGenericTypeDefinition() == typeof(IEnumerable<>)
select i.GetGenericArguments()[0];
return innertype.First();
}
//For normal types, get the public properties
return type;
}

public static IEnumerable<Type> AllModels()
{
Assembly asm = Assembly.GetEntryAssembly();

return from t in asm.GetTypes()
where typeof(Controller).IsAssignableFrom(t)
&& !t.GetTypeInfo().IsAbstract
&& t.GetTypeInfo().BaseType.IsConstructedGenericType
select t.GetTypeInfo().BaseType.GetGenericArguments()[0];
}

}
}
4 changes: 2 additions & 2 deletions muleapp/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void ConfigureServices(IServiceCollection services)
);

services.AddMvc();
services.AddScoped<IRepository<AppHost>, AppHostRepository>();
services.AddScoped<IRepository<AppHost>, Repository<AppHost>>();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -46,7 +46,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseExceptionHandler("/Shared/Error");
}

app.UseStaticFiles(new StaticFileOptions
Expand Down
Loading

0 comments on commit a152572

Please sign in to comment.