我是C#和ServiceStack的新手,我正在开发一个小项目,该项目包括调用第三方API并通过ServiceStack的ORMLite将我从API返回的数据加载到关系数据库中。 / p>
我们的想法是让API的每个端点都有一个可重用的模型,该模型确定如何在API的响应中接收它,以及如何将其插入到数据库中。
所以我有以下内容:
[Route("/api/{ApiEndpoint}", "POST")]
public class ApiRequest : IReturn<ApiResponse>
{
public Int32 OrderId { get; set; }
public DateTime PurchaseDate { get; set; }
public String ApiEndpoint { get; set; }
}
public class ApiResponse
{
public Endpoint1[] Data { get; set; }
public String ErrorCode { get; set; }
public Int32 ErrorNumber { get; set; }
public String ErrorDesc { get; set; }
}
public class Endpoint1
{
[AutoIncrement]
public Int32 Id { get; set; }
[CustomField("DATETIME2(7)")]
public String PurchaseDate { get; set; }
[CustomField("NVARCHAR(50)")]
public String Customer { get; set; }
[CustomField("NVARCHAR(20)")]
public String PhoneNumber { get; set; }
public Int32 Amount { get; set; }
}
我的第一个类用其路由表示API的请求,第二个类表示API的响应。 API的响应对于所有端点都是相同的,但唯一不同的是从该端点返回的Data
字段的结构。我已经在我的Endpoint1
类中定义了我的一个端点的结构,我在API的响应类中使用它。如您所见,我还在Endpoint1
类中定义了一些属性,以帮助ORM在插入数据后做出更好的决策。
好的,问题是我有大约15个端点,当我知道唯一改变的是第一个ApiResponse
字段时,我不想创建15个Data
类上课。
所以我做了这样的事情:
public class DataModels
{
public Type getModel(String endpoint)
{
Dictionary<String, Type> models = new Dictionary<String, Type>();
models.Add("Endpoint1", typeof(Endpoint1));
// models.Add("Endpoint2", typeof(Endpoint2));
// models.Add("Endpoint3", typeof(Endpoint3));
// and so forth...
return models[endpoint];
}
}
我希望在发出请求时调用getModel()
,以便我可以传入ApiEndpoint
类中的ApiRequest
字段并存储我想要的类型{ {1}}字段,以便我可以在Data
类中动态更改它。
此外,还有ORM部分,我遍历每个端点并使用每个端点的模型/类型创建不同的表。像这样:
ApiResponse
但同样,我希望能够在此处调用endpoints.ForEach(
(endpoint) =>
{
db.CreateTableIfNotExists<Endpoint1>();
// inserting data, doing other work etc
}
);
,然后定义我正在迭代的特定端点的模型。
我曾尝试在这两个地方拨打getModel()
,但我总是收到错误,如getModel()
和其他人......所以我肯定做错了。
随意为cannot use variable as a type
建议一种不同的方法。这正是我想出的,但我可能忽略了一种更为简单的方法。
答案 0 :(得分:3)
当我正确理解你时,你有不同的API调用,它们都返回相同的对象。唯一的区别是,字段“Data
”可以有不同的类型。
然后您可以简单地将数据类型更改为object:
public object Data { get; set; }
之后只需将其转换为所需的对象:
var data1=(Endpoint1[]) response.Data;
答案 1 :(得分:1)
您将非常难以动态地动态创建.NET类型,这需要高级使用Reflection.Emit。尝试使用ServiceStack动态创建Request DTO是一种自我挫败,因为客户端和元数据服务需要具体的类型才能使用Typed API调用Service。
我不能真正按照你的例子,但我最初的方法是你是否可以使用单一服务(即不是试图动态创建多个服务)。与OrmLite一样,如果POCO的Schema相同,听起来您可以展平DataModel并使用单个数据库表。
AutoQuery是一个example of a feature dynamically creates Service Implementations来自一个具体的Request DTO,它实际上是您需要的最小类型。
因此,虽然它是highly recommended to have explict DTOs for each Service,但您可以使用继承来重用公共属性,例如:
[Route("/api/{ApiEndpoint}/1", "POST")]
public ApiRequest1 : ApiRequestBase<Endpoint1> {}
[Route("/api/{ApiEndpoint}/2", "POST")]
public ApiRequest2 : ApiRequestBase<Endpoint1> {}
public abstract class ApiRequestBase<T> : IReturn<ApiResponse<T>>
{
public int OrderId { get; set; }
public DateTime PurchaseDate { get; set; }
public string ApiEndpoint { get; set; }
}
您的服务可以返回相同的通用响应DTO:
public class ApiResponse<T>
{
public T[] Data { get; set; }
public String ErrorCode { get; set; }
public Int32 ErrorNumber { get; set; }
public String ErrorDesc { get; set; }
}
我无法真正了解您尝试做的目的,因此API设计需要根据您的用例进行修改。
你会遇到与OrmLite类似的问题,这是一个Typed代码优先的POCO ORM,你会遇到摩擦试图使用在运行时不存在的动态类型,你可能会有更容易的时间executing Dynamic SQL因为生成字符串比.NET类型容易得多。
据说GenericTableExpressions.cs显示了一个更改OrmLite在运行时保存POCO的表名的示例:
const string tableName = "Entity1";
using (var db = OpenDbConnection())
{
db.DropAndCreateTable<GenericEntity>(tableName);
db.Insert(tableName, new GenericEntity { Id = 1, ColumnA = "A" });
var rows = db.Select(tableName, db.From<GenericEntity>()
.Where(x => x.ColumnA == "A"));
Assert.That(rows.Count, Is.EqualTo(1));
db.Update(tableName, new GenericEntity { ColumnA = "B" },
where: q => q.ColumnA == "A");
rows = db.Select(tableName, db.From<GenericEntity>()
.Where(x => x.ColumnA == "B"));
Assert.That(rows.Count, Is.EqualTo(1));
}
使用这些扩展方法:
public static class GenericTableExtensions
{
static object ExecWithAlias<T>(string table, Func<object> fn)
{
var modelDef = typeof(T).GetModelMetadata();
lock (modelDef)
{
var hold = modelDef.Alias;
try
{
modelDef.Alias = table;
return fn();
}
finally
{
modelDef.Alias = hold;
}
}
}
public static void DropAndCreateTable<T>(this IDbConnection db, string table)
{
ExecWithAlias<T>(table, () => {
db.DropAndCreateTable<T>();
return null;
});
}
public static long Insert<T>(this IDbConnection db, string table, T obj, bool selectIdentity = false)
{
return (long)ExecWithAlias<T>(table, () => db.Insert(obj, selectIdentity));
}
public static List<T> Select<T>(this IDbConnection db, string table, SqlExpression<T> expression)
{
return (List<T>)ExecWithAlias<T>(table, () => db.Select(expression));
}
public static int Update<T>(this IDbConnection db, string table, T item, Expression<Func<T, bool>> where)
{
return (int)ExecWithAlias<T>(table, () => db.Update(item, where));
}
}
但这不是我个人采取的方法,如果我绝对需要(并且我正在努力想到基于表的多租户或分片之外的有效用例)来保存多个表中的相同模式我再次使用继承,例如:
public class Table1 : TableBase {}
public class Table2 : TableBase {}
public class Table3 : TableBase {}