mirror of
https://github.com/fergalmoran/deepsouthsounds.git
synced 2025-12-22 01:42:34 +00:00
Initial
This commit is contained in:
12
dss-api/Data/Annotations/SlugFieldAttribute.cs
Normal file
12
dss-api/Data/Annotations/SlugFieldAttribute.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace DSS.Api.Data.Annotations {
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class SlugFieldAttribute : Attribute {
|
||||
public string SourceField { get; }
|
||||
|
||||
public SlugFieldAttribute(string sourceField) {
|
||||
SourceField = sourceField;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
dss-api/Data/DSSDbContext.cs
Normal file
26
dss-api/Data/DSSDbContext.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DSS.Api.Data.Models;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DSS.Api.Data {
|
||||
public class DSSDbContext : IdentityDbContext {
|
||||
public DSSDbContext(DbContextOptions<DSSDbContext> options)
|
||||
: base(options) {
|
||||
}
|
||||
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
|
||||
CancellationToken cancellationToken = default) {
|
||||
foreach (var entity in ChangeTracker.Entries()
|
||||
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)
|
||||
.Where(e => e.Entity is ISluggedEntity)
|
||||
.Select(e => e.Entity as ISluggedEntity)
|
||||
.Where(e => string.IsNullOrEmpty(e.Slug))) {
|
||||
entity.Slug = entity.GenerateSlug(this);
|
||||
}
|
||||
}
|
||||
public DbSet<AudioItem> AudioItems { get; set; }
|
||||
public DbSet<Tag> Tags { get; set; }
|
||||
}
|
||||
}
|
||||
73
dss-api/Data/Extensions/EntityExtesions.cs
Normal file
73
dss-api/Data/Extensions/EntityExtesions.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using DSS.Api.Data.Annotations;
|
||||
using DSS.Api.Data.Models.Interfaces;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DSS.Api.Data.Extensions {
|
||||
public static class EntityExtesions {
|
||||
private class ProxySluggedModel : ISluggedEntity {
|
||||
public string Slug { get; set; }
|
||||
}
|
||||
public class GenerateSlugFailureException : Exception {
|
||||
public GenerateSlugFailureException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public static IEnumerable<T> ExecSQL<T>(this DbContext context, string query)
|
||||
where T : class, ISluggedEntity, new() {
|
||||
using (var command = context.Database.GetDbConnection().CreateCommand()) {
|
||||
command.CommandText = query;
|
||||
command.CommandType = CommandType.Text;
|
||||
context.Database.OpenConnection();
|
||||
|
||||
using (var reader = command.ExecuteReader()) {
|
||||
var result = reader.Select(r => new T {
|
||||
Slug = r["Slug"] is DBNull ? string.Empty : r["Slug"].ToString()
|
||||
});
|
||||
return result.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
public static string GenerateSlug(this IUniqueFieldEntity entity, DbContext context, ILogger logger = null) {
|
||||
try {
|
||||
var property = entity.GetType()
|
||||
.GetProperties()
|
||||
.FirstOrDefault(prop => Attribute.IsDefined(prop, typeof(SlugFieldAttribute)));
|
||||
if (property != null) {
|
||||
var attribute = property
|
||||
.GetCustomAttributes(typeof(SlugFieldAttribute), false)
|
||||
.FirstOrDefault();
|
||||
|
||||
var t = entity.GetType();
|
||||
var tableName = context.Model.FindEntityType(t).GetTableName();
|
||||
if (!string.IsNullOrEmpty(tableName)) {
|
||||
var sourceField = (attribute as SlugFieldAttribute)?.SourceField;
|
||||
if (string.IsNullOrEmpty(sourceField)) {
|
||||
logger?.LogError($"Error slugifying - Entry title is blank, cannot slugify");
|
||||
// need to throw here, shouldn't save without slug
|
||||
throw new GenerateSlugFailureException("Entry title is blank, cannot slugify");
|
||||
}
|
||||
|
||||
var slugSource = entity.GetType()
|
||||
.GetProperty(sourceField)
|
||||
?.GetValue(entity, null)
|
||||
?.ToString() ?? string.Empty;
|
||||
|
||||
var source = context.ExecSQL<ProxySluggedModel>($"SELECT Slug FROM {tableName}")
|
||||
.Select(m => m.Slug);
|
||||
|
||||
return slugSource.Slugify(source);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger?.LogError($"Error slugifying {entity.GetType().Name} - {ex.Message}");
|
||||
// need to throw here, shouldn't save without slug
|
||||
throw new GenerateSlugFailureException(ex.Message);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
dss-api/Data/Models/ApplicationUser.cs
Normal file
23
dss-api/Data/Models/ApplicationUser.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using DSS.Api.Data.Annotations;
|
||||
using DSS.Api.Data.Models.Interfaces;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace DSS.Api.Data.Models {
|
||||
public static class EntityExtesions {
|
||||
|
||||
}
|
||||
public class ApplicationUser : IdentityUser, ISluggedEntity {
|
||||
// Extended Properties
|
||||
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public long? FacebookId { get; set; }
|
||||
public string TwitterHandle { get; set; }
|
||||
|
||||
public string PictureUrl { get; set; }
|
||||
|
||||
[SlugField(sourceField: "FullName")]
|
||||
public string Slug { get; set; }
|
||||
public string FullName => $"{FirstName} {LastName}";
|
||||
}
|
||||
}
|
||||
18
dss-api/Data/Models/AudioItem.cs
Normal file
18
dss-api/Data/Models/AudioItem.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using DSS.Api.Data.Annotations;
|
||||
|
||||
namespace DSS.Api.Data.Models
|
||||
{
|
||||
public class AudioItem : BaseEntity
|
||||
{
|
||||
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public virtual List<Tag> Tags { get; set; }
|
||||
|
||||
public ApplicationUser User { get; set; }
|
||||
|
||||
[SlugField(sourceField: "Title")]
|
||||
public string Slug { get; set; }
|
||||
}
|
||||
}
|
||||
18
dss-api/Data/Models/BaseEntity.cs
Normal file
18
dss-api/Data/Models/BaseEntity.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using DSS.Api.Data.Interfaces;
|
||||
|
||||
namespace DSS.Api.Data.Models
|
||||
{
|
||||
public class BaseEntity : IEntity
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||
public DateTime UpdateDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
}
|
||||
}
|
||||
11
dss-api/Data/Models/Interfaces/IEntity.cs
Normal file
11
dss-api/Data/Models/Interfaces/IEntity.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace DSS.Api.Data.Models.Interfaces
|
||||
{
|
||||
public interface IEntity
|
||||
{
|
||||
Guid Id { get; set; }
|
||||
DateTime CreateDate { get; set; }
|
||||
DateTime UpdateDate { get; set; }
|
||||
}
|
||||
}
|
||||
7
dss-api/Data/Models/Interfaces/ISluggedEntity.cs
Normal file
7
dss-api/Data/Models/Interfaces/ISluggedEntity.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace DSS.Api.Data.Models.Interfaces
|
||||
{
|
||||
public interface ISluggedEntity : IUniqueFieldEntity
|
||||
{
|
||||
string Slug { get; set; }
|
||||
}
|
||||
}
|
||||
6
dss-api/Data/Models/Interfaces/IUniqueFieldEntity.cs
Normal file
6
dss-api/Data/Models/Interfaces/IUniqueFieldEntity.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DSS.Api.Data.Models.Interfaces
|
||||
{
|
||||
public interface IUniqueFieldEntity
|
||||
{
|
||||
}
|
||||
}
|
||||
7
dss-api/Data/Models/Tag.cs
Normal file
7
dss-api/Data/Models/Tag.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace DSS.Api.Data.Models
|
||||
{
|
||||
public class Tag : BaseEntity
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user