add some default types to de-duplicate similar implmentations
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using Castle.Core.Logging;
|
using Castle.Core.Logging;
|
||||||
using Pilz.Extensions.Reflection;
|
using Pilz.Extensions.Reflection;
|
||||||
|
using Pilz.Net.Data;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -12,8 +13,12 @@ namespace Pilz.Net.Api;
|
|||||||
|
|
||||||
public class ApiServer : IApiServer
|
public class ApiServer : IApiServer
|
||||||
{
|
{
|
||||||
|
protected record struct ThreadHolder(Thread? Thread);
|
||||||
|
public class MissingDataManagerException : Exception { }
|
||||||
|
|
||||||
protected readonly List<PrivateMessageHandler> handlers = [];
|
protected readonly List<PrivateMessageHandler> handlers = [];
|
||||||
protected readonly Dictionary<Type, IApiMessageSerializer> serializers = [];
|
protected readonly Dictionary<Type, IApiMessageSerializer> serializers = [];
|
||||||
|
protected readonly Dictionary<ThreadHolder, IDataManager> managers = [];
|
||||||
protected HttpListener httpListener;
|
protected HttpListener httpListener;
|
||||||
protected int restartAttempts = 0;
|
protected int restartAttempts = 0;
|
||||||
protected DateTime lastRestartAttempt;
|
protected DateTime lastRestartAttempt;
|
||||||
@@ -24,6 +29,8 @@ public class ApiServer : IApiServer
|
|||||||
public event OnCheckAuthenticationEventHandler? OnCheckAuthentication;
|
public event OnCheckAuthenticationEventHandler? OnCheckAuthentication;
|
||||||
public event OnCheckContextEventHandler? OnCheckContext;
|
public event OnCheckContextEventHandler? OnCheckContext;
|
||||||
public event OnCheckContextEventHandler? OnCheckContextCompleted;
|
public event OnCheckContextEventHandler? OnCheckContextCompleted;
|
||||||
|
public event OnGetNewDataManagerEventHandler? OnGetNewDataManager;
|
||||||
|
public event DataManagerEventHandler? OnResetDataManager;
|
||||||
|
|
||||||
protected record PrivateParameterInfo(string Name, int Index);
|
protected record PrivateParameterInfo(string Name, int Index);
|
||||||
|
|
||||||
@@ -51,6 +58,10 @@ public class ApiServer : IApiServer
|
|||||||
|
|
||||||
public int MaxConcurentConnections { get; set; } = 5;
|
public int MaxConcurentConnections { get; set; } = 5;
|
||||||
|
|
||||||
|
public IDataManager Manager => GetManager();
|
||||||
|
|
||||||
|
public bool ThreadedDataManager { get; set; }
|
||||||
|
|
||||||
public ApiServer(string apiUrl) : this(apiUrl, null)
|
public ApiServer(string apiUrl) : this(apiUrl, null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -61,16 +72,38 @@ public class ApiServer : IApiServer
|
|||||||
this.httpListener = httpListener ?? CreateDefaultHttpListener();
|
this.httpListener = httpListener ?? CreateDefaultHttpListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IDataManager GetManager()
|
||||||
|
{
|
||||||
|
var curThread = ThreadedDataManager ? Thread.CurrentThread : null;
|
||||||
|
var threadHolder = new ThreadHolder(curThread);
|
||||||
|
|
||||||
|
if (managers.TryGetValue(threadHolder, out var mgr))
|
||||||
|
return mgr;
|
||||||
|
|
||||||
|
if (OnGetNewDataManager?.Invoke(this, EventArgs.Empty) is not IDataManager manager)
|
||||||
|
throw new MissingDataManagerException();
|
||||||
|
|
||||||
|
managers.Add(threadHolder, manager);
|
||||||
|
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void ResetManager()
|
||||||
|
{
|
||||||
|
if (managers.Remove(new(ThreadedDataManager ? Thread.CurrentThread : null), out var manager))
|
||||||
|
OnResetDataManager?.Invoke(this, new(manager));
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual HttpListener CreateDefaultHttpListener()
|
protected virtual HttpListener CreateDefaultHttpListener()
|
||||||
{
|
{
|
||||||
var httpListener = new HttpListener();
|
var httpListener = new HttpListener();
|
||||||
|
|
||||||
httpListener.TimeoutManager.IdleConnection = new TimeSpan(0, 10, 0);
|
httpListener.TimeoutManager.IdleConnection = new TimeSpan(0, 2, 0);
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
httpListener.TimeoutManager.RequestQueue = new TimeSpan(0, 10, 0);
|
httpListener.TimeoutManager.RequestQueue = new TimeSpan(0, 2, 0);
|
||||||
httpListener.TimeoutManager.HeaderWait = new TimeSpan(0, 10, 0);
|
httpListener.TimeoutManager.HeaderWait = new TimeSpan(0, 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpListener;
|
return httpListener;
|
||||||
@@ -296,21 +329,27 @@ public class ApiServer : IApiServer
|
|||||||
if (context is not null)
|
if (context is not null)
|
||||||
{
|
{
|
||||||
Log.Info("Request retrived for " + context.Request.RawUrl);
|
Log.Info("Request retrived for " + context.Request.RawUrl);
|
||||||
|
ResetManager();
|
||||||
OnCheckContext?.Invoke(this, new(context));
|
OnCheckContext?.Invoke(this, new(context));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CheckContext(context);
|
CheckContext(context);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
catch (MissingDataManagerException mdmex)
|
||||||
|
{
|
||||||
|
Log.Error("DataManager is not supported by this server instnace!", mdmex);
|
||||||
|
if (DebugMode) throw;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error("Error checking context", ex);
|
Log.Error("Error checking context", ex);
|
||||||
if (DebugMode)
|
if (DebugMode) throw;
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
OnCheckContextCompleted?.Invoke(this, new(context));
|
OnCheckContextCompleted?.Invoke(this, new(context));
|
||||||
|
ResetManager();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
Pilz.Net/Api/Client/BaseChildItemClient.cs
Normal file
34
Pilz.Net/Api/Client/BaseChildItemClient.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Pilz.Net.Api.Messages;
|
||||||
|
using Pilz.Net.Data;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Client;
|
||||||
|
|
||||||
|
public abstract class BaseChildItemClient<T>(IApiClient client) : BaseClient<T>(client), IBaseChildItemClient<T> where T : IDataObject
|
||||||
|
{
|
||||||
|
public virtual async Task<IEnumerable<T>> GetAll(int parentId)
|
||||||
|
{
|
||||||
|
return (await client.SendRequest<GeneralItemMessages<T>.Items>(ApiEndpoint, HttpMethod.Get, new ApiParameterCollection
|
||||||
|
{
|
||||||
|
["parent"] = parentId,
|
||||||
|
})).EnsureOk().Items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<T> Save(T item)
|
||||||
|
{
|
||||||
|
if (item.Id == 0)
|
||||||
|
throw new NullReferenceException("Item has no parent yet!");
|
||||||
|
return await base.Save(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<T> Save(T item, int parentId)
|
||||||
|
{
|
||||||
|
return (await client.SendRequest<GeneralItemMessages<T>.Item>(
|
||||||
|
ApiEndpoint,
|
||||||
|
HttpMethod.Post,
|
||||||
|
GenerateUpdateMessage(item, true),
|
||||||
|
new ApiParameterCollection
|
||||||
|
{
|
||||||
|
["parent"] = parentId,
|
||||||
|
})).EnsureOk().Item;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Pilz.Net/Api/Client/BaseClient.cs
Normal file
30
Pilz.Net/Api/Client/BaseClient.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Pilz.Net.Api.Messages;
|
||||||
|
using Pilz.Net.Data;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Client;
|
||||||
|
|
||||||
|
public abstract class BaseClient<T>(IApiClient client) : IBaseClient<T> where T : IDataObject
|
||||||
|
{
|
||||||
|
public abstract string ApiEndpoint { get; }
|
||||||
|
|
||||||
|
public virtual async Task<T> Get(int id)
|
||||||
|
{
|
||||||
|
return (await client.SendRequest<GeneralItemMessages<T>.Item>($"{ApiEndpoint}/{id}", HttpMethod.Get)).EnsureOk().Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task Delete(int id)
|
||||||
|
{
|
||||||
|
(await client.SendRequest($"{ApiEndpoint}/{id}", HttpMethod.Delete)).EnsureOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<T> Save(T item)
|
||||||
|
{
|
||||||
|
var generateNew = item.Id == 0;
|
||||||
|
return (await client.SendRequest<GeneralItemMessages<T>.Item>(
|
||||||
|
generateNew ? ApiEndpoint : $"{ApiEndpoint}/{item.Id}",
|
||||||
|
generateNew ? HttpMethod.Post : HttpMethod.Put,
|
||||||
|
GenerateUpdateMessage(item, generateNew))).EnsureOk().Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract ApiMessage GenerateUpdateMessage(T item, bool generateNew);
|
||||||
|
}
|
||||||
12
Pilz.Net/Api/Client/BaseItemClient.cs
Normal file
12
Pilz.Net/Api/Client/BaseItemClient.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Pilz.Net.Api.Messages;
|
||||||
|
using Pilz.Net.Data;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Client;
|
||||||
|
|
||||||
|
public abstract class BaseItemClient<T>(IApiClient client) : BaseClient<T>(client), IBaseItemClient<T> where T : IDataObject
|
||||||
|
{
|
||||||
|
public virtual async Task<IEnumerable<T>> GetAll()
|
||||||
|
{
|
||||||
|
return (await client.SendRequest<GeneralItemMessages<T>.Items>(ApiEndpoint, HttpMethod.Get)).EnsureOk().Items;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Pilz.Net/Api/Client/IBaseChildItemClient.cs
Normal file
9
Pilz.Net/Api/Client/IBaseChildItemClient.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Pilz.Net.Data;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Client;
|
||||||
|
|
||||||
|
public interface IBaseChildItemClient<T> : IBaseClient<T> where T : IDataObject
|
||||||
|
{
|
||||||
|
Task<IEnumerable<T>> GetAll(int parentId);
|
||||||
|
Task<T> Save(T item, int parentId);
|
||||||
|
}
|
||||||
10
Pilz.Net/Api/Client/IBaseClient.cs
Normal file
10
Pilz.Net/Api/Client/IBaseClient.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Pilz.Net.Data;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Client;
|
||||||
|
|
||||||
|
public interface IBaseClient<T> where T : IDataObject
|
||||||
|
{
|
||||||
|
Task Delete(int id);
|
||||||
|
Task<T> Get(int id);
|
||||||
|
Task<T> Save(T item);
|
||||||
|
}
|
||||||
8
Pilz.Net/Api/Client/IBaseItemClient.cs
Normal file
8
Pilz.Net/Api/Client/IBaseItemClient.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Pilz.Net.Data;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Client;
|
||||||
|
|
||||||
|
public interface IBaseItemClient<T> : IBaseClient<T> where T : IDataObject
|
||||||
|
{
|
||||||
|
Task<IEnumerable<T>> GetAll();
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Castle.Core.Logging;
|
using Castle.Core.Logging;
|
||||||
|
using Pilz.Net.Data;
|
||||||
|
|
||||||
namespace Pilz.Net.Api;
|
namespace Pilz.Net.Api;
|
||||||
|
|
||||||
@@ -6,10 +7,15 @@ public interface IApiServer
|
|||||||
{
|
{
|
||||||
public delegate void OnCheckAuthenticationEventHandler(object sender, ApiAuthCheckEventArgs e);
|
public delegate void OnCheckAuthenticationEventHandler(object sender, ApiAuthCheckEventArgs e);
|
||||||
public delegate void OnCheckContextEventHandler(object sender, ApiContextEventArgs e);
|
public delegate void OnCheckContextEventHandler(object sender, ApiContextEventArgs e);
|
||||||
|
public delegate IDataManager? OnGetNewDataManagerEventHandler(object sender, EventArgs e);
|
||||||
|
|
||||||
event OnCheckAuthenticationEventHandler? OnCheckAuthentication;
|
event OnCheckAuthenticationEventHandler? OnCheckAuthentication;
|
||||||
event OnCheckContextEventHandler OnCheckContext;
|
event OnCheckContextEventHandler? OnCheckContext;
|
||||||
event OnCheckContextEventHandler OnCheckContextCompleted;
|
event OnCheckContextEventHandler? OnCheckContextCompleted;
|
||||||
|
event OnGetNewDataManagerEventHandler? OnGetNewDataManager;
|
||||||
|
event DataManagerEventHandler? OnResetDataManager;
|
||||||
|
|
||||||
|
IDataManager Manager { get; }
|
||||||
|
|
||||||
string ApiUrl { get; }
|
string ApiUrl { get; }
|
||||||
|
|
||||||
|
|||||||
21
Pilz.Net/Api/Messages/GeneralItemMessages.cs
Normal file
21
Pilz.Net/Api/Messages/GeneralItemMessages.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Messages;
|
||||||
|
|
||||||
|
public static class GeneralItemMessages<T>
|
||||||
|
{
|
||||||
|
public class Item(T item) : ObjectMessage(item);
|
||||||
|
public class Items(List<T> items) : ObjectsMessage(items);
|
||||||
|
|
||||||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
|
public class ObjectMessage(T item) : ApiMessage
|
||||||
|
{
|
||||||
|
public T Item { get; } = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
|
public class ObjectsMessage(List<T> items) : ApiMessage
|
||||||
|
{
|
||||||
|
public List<T> Items { get; } = items;
|
||||||
|
}
|
||||||
|
}
|
||||||
52
Pilz.Net/Api/Server/BaseChildItemHandler.cs
Normal file
52
Pilz.Net/Api/Server/BaseChildItemHandler.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using Pilz.Extensions.Reflection;
|
||||||
|
using Pilz.Net.Data;
|
||||||
|
using Pilz.Net.Extensions;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Server;
|
||||||
|
|
||||||
|
public abstract class BaseChildItemHandler<TEntity, TParent, TUpdateMsg>(IApiServer server)
|
||||||
|
: BaseHandler<TEntity, TUpdateMsg>(server)
|
||||||
|
where TEntity : IDataObject
|
||||||
|
where TParent : IDataObject
|
||||||
|
where TUpdateMsg : ApiMessage
|
||||||
|
{
|
||||||
|
protected virtual bool RegisterGetAll => true;
|
||||||
|
protected virtual bool RegisterPost => true;
|
||||||
|
|
||||||
|
public override void Initialize(IApiServer server)
|
||||||
|
{
|
||||||
|
base.Initialize(server);
|
||||||
|
var t = GetType();
|
||||||
|
if (RegisterGetAll)
|
||||||
|
server.RegisterHandler(t.GetMethod(nameof(GetAll))!.CreateDelegate(this), new(Route, "GET"), Debugger.IsAttached);
|
||||||
|
if (RegisterPost)
|
||||||
|
server.RegisterHandler(t.GetMethod(nameof(Post))!.CreateDelegate(this), new(Route, "POST"), Debugger.IsAttached);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ApiResult GetAll(int parent)
|
||||||
|
{
|
||||||
|
IQueryable<TEntity> list;
|
||||||
|
|
||||||
|
if (parent != 0 && server.Manager.Find(parent, out TParent? parentEntity))
|
||||||
|
list = GetChilds(parentEntity).AsQueryable();
|
||||||
|
else
|
||||||
|
list = server.Manager.Get<TEntity>();
|
||||||
|
|
||||||
|
return list.ToList().Select(ToClient).ToItemsResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ApiResult Post(int parent, TUpdateMsg msg)
|
||||||
|
{
|
||||||
|
if (!server.Manager.Find(parent, out TParent? parentEntity))
|
||||||
|
return ApiResult.NotFound();
|
||||||
|
var entity = CreateNewEntity();
|
||||||
|
if (UpdateEntity(entity, msg) is ApiResult result)
|
||||||
|
return result;
|
||||||
|
GetChilds(parentEntity).Add(entity);
|
||||||
|
server.Manager.Save(parentEntity, true);
|
||||||
|
return ToClient(entity).ToItemResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract IList<TEntity> GetChilds(TParent parent);
|
||||||
|
}
|
||||||
63
Pilz.Net/Api/Server/BaseHandler.cs
Normal file
63
Pilz.Net/Api/Server/BaseHandler.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using Pilz.Extensions.Reflection;
|
||||||
|
using Pilz.Net.Data;
|
||||||
|
using Pilz.Net.Extensions;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Server;
|
||||||
|
|
||||||
|
public abstract class BaseHandler<TEntity, TUpdateMsg>(IApiServer server)
|
||||||
|
: IApiHandlerInitializer
|
||||||
|
where TEntity : IDataObject
|
||||||
|
where TUpdateMsg : ApiMessage
|
||||||
|
{
|
||||||
|
public abstract string Route { get; }
|
||||||
|
protected virtual bool RegisterGet => true;
|
||||||
|
protected virtual bool RegisterPut => true;
|
||||||
|
protected virtual bool RegisterDelete => true;
|
||||||
|
|
||||||
|
public virtual void Initialize(IApiServer server)
|
||||||
|
{
|
||||||
|
var t = GetType();
|
||||||
|
if (RegisterGet)
|
||||||
|
server.RegisterHandler(t.GetMethod(nameof(Get))!.CreateDelegate(this), new(Route + "/{id}", "GET"), Debugger.IsAttached);
|
||||||
|
if (RegisterPut)
|
||||||
|
server.RegisterHandler(t.GetMethod(nameof(Put))!.CreateDelegate(this), new(Route + "/{id}", "PUT"), Debugger.IsAttached);
|
||||||
|
if (RegisterDelete)
|
||||||
|
server.RegisterHandler(t.GetMethod(nameof(Delete))!.CreateDelegate(this), new(Route + "/{id}", "DELETE"), Debugger.IsAttached);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ApiResult Get(int id)
|
||||||
|
{
|
||||||
|
if (!server.Manager.Find(id, out TEntity? entity))
|
||||||
|
return ApiResult.NotFound();
|
||||||
|
return ToClient(entity).ToItemResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ApiResult Put(int id, TUpdateMsg msg)
|
||||||
|
{
|
||||||
|
if (!server.Manager.Find(id, out TEntity? entity))
|
||||||
|
return ApiResult.NotFound();
|
||||||
|
if (UpdateEntity(entity, msg) is ApiResult result)
|
||||||
|
return result;
|
||||||
|
server.Manager.Save(entity, true);
|
||||||
|
return ToClient(entity).ToItemResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ApiResult Delete(int id)
|
||||||
|
{
|
||||||
|
server.Manager.Delete<TEntity>(id, true);
|
||||||
|
return ApiResult.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual TEntity CreateNewEntity()
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance<TEntity>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract ApiResult? UpdateEntity(TEntity entity, TUpdateMsg msg);
|
||||||
|
|
||||||
|
protected abstract IDataObject ToClient(TEntity entity);
|
||||||
|
}
|
||||||
39
Pilz.Net/Api/Server/BaseItemHandler.cs
Normal file
39
Pilz.Net/Api/Server/BaseItemHandler.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Pilz.Extensions.Reflection;
|
||||||
|
using Pilz.Net.Data;
|
||||||
|
using Pilz.Net.Extensions;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api.Server;
|
||||||
|
|
||||||
|
public abstract class BaseItemHandler<TEntity, TUpdateMsg>(IApiServer server)
|
||||||
|
: BaseHandler<TEntity, TUpdateMsg>(server)
|
||||||
|
where TEntity : IDataObject
|
||||||
|
where TUpdateMsg : ApiMessage
|
||||||
|
{
|
||||||
|
protected virtual bool RegisterGetAll => true;
|
||||||
|
protected virtual bool RegisterPost => true;
|
||||||
|
|
||||||
|
public override void Initialize(IApiServer server)
|
||||||
|
{
|
||||||
|
base.Initialize(server);
|
||||||
|
var t = GetType();
|
||||||
|
if (RegisterGetAll)
|
||||||
|
server.RegisterHandler(t.GetMethod(nameof(GetAll))!.CreateDelegate(this), new(Route, "GET"), Debugger.IsAttached);
|
||||||
|
if (RegisterPost)
|
||||||
|
server.RegisterHandler(t.GetMethod(nameof(Put))!.CreateDelegate(this), new(Route, "POST"), Debugger.IsAttached);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ApiResult GetAll()
|
||||||
|
{
|
||||||
|
return server.Manager.Get<TEntity>().ToList().Select(ToClient).ToItemsResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ApiResult Post(TUpdateMsg msg)
|
||||||
|
{
|
||||||
|
var entity = CreateNewEntity();
|
||||||
|
if (UpdateEntity(entity, msg) is ApiResult result)
|
||||||
|
return result;
|
||||||
|
server.Manager.Save(entity, true);
|
||||||
|
return entity.ToItemResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
149
Pilz.Net/Data/DataManager.cs
Normal file
149
Pilz.Net/Data/DataManager.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Data;
|
||||||
|
|
||||||
|
public abstract class DataManager : IDataManager
|
||||||
|
{
|
||||||
|
protected abstract void UpdateEntity<T>(T obj) where T : IDataObject;
|
||||||
|
protected abstract IQueryable<T> GetEntitySet<T>() where T : IDataObject;
|
||||||
|
protected abstract T? FindEntity<T>(int id) where T : IDataObject;
|
||||||
|
protected abstract void RemoveEntity<T>(T obj) where T : IDataObject;
|
||||||
|
protected abstract void SaveChanges();
|
||||||
|
|
||||||
|
public virtual IQueryable<T> Get<T>() where T : IDataObject
|
||||||
|
{
|
||||||
|
return GetEntitySet<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T FindOrNew<T>(int? id) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (Find<T>(id) is not T obj)
|
||||||
|
obj = Activator.CreateInstance<T>();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T FindOrNew<T>(int id) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (Find<T>(id) is not T obj)
|
||||||
|
obj = Activator.CreateInstance<T>();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T? FindOrNull<T>(int? id) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (id == null || id == 0)
|
||||||
|
return default;
|
||||||
|
return Find<T>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T? FindOrNull<T>(int id) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (id == 0)
|
||||||
|
return default;
|
||||||
|
return Find<T>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T? Find<T>(int? id) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (id == null)
|
||||||
|
return default;
|
||||||
|
return Find<T>(id.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T? Find<T>(int id) where T : IDataObject
|
||||||
|
{
|
||||||
|
return FindEntity<T>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool FindOrNew<T>(int? id, [NotNullWhen(true)] out T? obj) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (id == null || id == 0)
|
||||||
|
{
|
||||||
|
obj = Activator.CreateInstance<T>();
|
||||||
|
return obj != null;
|
||||||
|
}
|
||||||
|
return Find(id.Value, out obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool FindOrNew<T>(int id, [NotNullWhen(true)] out T? obj) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (id == 0)
|
||||||
|
{
|
||||||
|
obj = Activator.CreateInstance<T>();
|
||||||
|
return obj != null;
|
||||||
|
}
|
||||||
|
return Find(id, out obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool FindOrNull<T>(int? id, out T? obj) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (id == null || id == 0)
|
||||||
|
{
|
||||||
|
obj = default;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Find(id.Value, out obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool FindOrNull<T>(int id, out T? obj) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (id == 0)
|
||||||
|
{
|
||||||
|
obj = default;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Find(id, out obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Find<T>(int id, [NotNullWhen(true)] out T? obj) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (FindEntity<T>(id) is not T t)
|
||||||
|
{
|
||||||
|
obj = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
obj = t;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Delete<T>(int id) where T : IDataObject
|
||||||
|
{
|
||||||
|
Delete<T>(id, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Delete<T>(int id, bool save) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (Find(id, out T? obj))
|
||||||
|
Delete(obj, save);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Delete<T>(T? obj) where T : IDataObject
|
||||||
|
{
|
||||||
|
Delete(obj, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Delete<T>(T? obj, bool save) where T : IDataObject
|
||||||
|
{
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
RemoveEntity(obj);
|
||||||
|
if (save) Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Save<T>(T obj) where T : IDataObject
|
||||||
|
{
|
||||||
|
Save(obj, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Save<T>(T obj, bool save) where T : IDataObject
|
||||||
|
{
|
||||||
|
UpdateEntity(obj);
|
||||||
|
if (save) Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Save()
|
||||||
|
{
|
||||||
|
SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Pilz.Net/Data/DataManagerEventArgs.cs
Normal file
8
Pilz.Net/Data/DataManagerEventArgs.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Pilz.Net.Data;
|
||||||
|
|
||||||
|
public class DataManagerEventArgs(IDataManager manager) : EventArgs
|
||||||
|
{
|
||||||
|
public IDataManager Manager { get; } = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void DataManagerEventHandler(object sender, DataManagerEventArgs e);
|
||||||
26
Pilz.Net/Data/IDataManager.cs
Normal file
26
Pilz.Net/Data/IDataManager.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Data;
|
||||||
|
|
||||||
|
public interface IDataManager
|
||||||
|
{
|
||||||
|
void Delete<T>(int id) where T : IDataObject;
|
||||||
|
void Delete<T>(int id, bool save) where T : IDataObject;
|
||||||
|
void Delete<T>(T? obj) where T : IDataObject;
|
||||||
|
void Delete<T>(T? obj, bool save) where T : IDataObject;
|
||||||
|
T? Find<T>(int id) where T : IDataObject;
|
||||||
|
bool Find<T>(int id, [NotNullWhen(true)] out T? obj) where T : IDataObject;
|
||||||
|
T? Find<T>(int? id) where T : IDataObject;
|
||||||
|
T FindOrNew<T>(int id) where T : IDataObject;
|
||||||
|
bool FindOrNew<T>(int id, [NotNullWhen(true)] out T? obj) where T : IDataObject;
|
||||||
|
T FindOrNew<T>(int? id) where T : IDataObject;
|
||||||
|
bool FindOrNew<T>(int? id, [NotNullWhen(true)] out T? obj) where T : IDataObject;
|
||||||
|
T? FindOrNull<T>(int id) where T : IDataObject;
|
||||||
|
bool FindOrNull<T>(int id, out T? obj) where T : IDataObject;
|
||||||
|
T? FindOrNull<T>(int? id) where T : IDataObject;
|
||||||
|
bool FindOrNull<T>(int? id, out T? obj) where T : IDataObject;
|
||||||
|
IQueryable<T> Get<T>() where T : IDataObject;
|
||||||
|
void Save();
|
||||||
|
void Save<T>(T obj) where T : IDataObject;
|
||||||
|
void Save<T>(T obj, bool save) where T : IDataObject;
|
||||||
|
}
|
||||||
6
Pilz.Net/Data/IDataObject.cs
Normal file
6
Pilz.Net/Data/IDataObject.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Pilz.Net.Data;
|
||||||
|
|
||||||
|
public interface IDataObject
|
||||||
|
{
|
||||||
|
public int Id { get; }
|
||||||
|
}
|
||||||
36
Pilz.Net/Extensions/ApiMessageExtensions.cs
Normal file
36
Pilz.Net/Extensions/ApiMessageExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Pilz.Net.Api;
|
||||||
|
using Pilz.Net.Api.Messages;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Extensions;
|
||||||
|
|
||||||
|
public static class ApiMessageExtensions
|
||||||
|
{
|
||||||
|
public static ApiResult ToItemResult<T>(this T? @this)
|
||||||
|
{
|
||||||
|
if (@this == null)
|
||||||
|
return ApiResult.NotFound();
|
||||||
|
return ApiResult.Ok(@this.ToItemMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApiResult ToItemsResult<T>(this IEnumerable<T>? @this)
|
||||||
|
{
|
||||||
|
if (@this == null)
|
||||||
|
return ApiResult.NotFound();
|
||||||
|
return ApiResult.Ok(@this.ToItemsMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GeneralItemMessages<T>.Item ToItemMsg<T>(this T @this)
|
||||||
|
{
|
||||||
|
return new GeneralItemMessages<T>.Item(@this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GeneralItemMessages<T>.Items ToItemsMsg<T>(this IEnumerable<T> @this)
|
||||||
|
{
|
||||||
|
return new GeneralItemMessages<T>.Items([.. @this]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GeneralItemMessages<T>.Items ToItemsMsg<T>(this T[] @this)
|
||||||
|
{
|
||||||
|
return new GeneralItemMessages<T>.Items([.. @this]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user