AggregateRootRepository

This commit is contained in:
2881099 2022-09-03 16:27:14 +08:00
parent 074dd7f13d
commit 9180375a2b
5 changed files with 226 additions and 110 deletions

View File

@ -800,5 +800,14 @@
<param name="that"></param> <param name="that"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Microsoft.Extensions.DependencyInjection.FreeSqlRepositoryDependencyInjection.AddFreeRepository(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{FreeSql.FluentDataFilter},System.Reflection.Assembly[])">
<summary>
批量注入 Repository可以参考代码自行调整
</summary>
<param name="services"></param>
<param name="globalDataFilter"></param>
<param name="assemblies"></param>
<returns></returns>
</member>
</members> </members>
</doc> </doc>

View File

@ -1,16 +1,9 @@
using FreeSql.Extensions.EntityUtil; using FreeSql.Extensions.EntityUtil;
using FreeSql.Internal;
using FreeSql.Internal.Model;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql namespace FreeSql
{ {
@ -156,7 +149,7 @@ namespace FreeSql
get get
{ {
var query = _repository.Select.TrackToList(SelectAggregateRootTracking); var query = _repository.Select.TrackToList(SelectAggregateRootTracking);
SelectAggregateRootNavigateReader(query, EntityType, "", new Stack<Type>()); query = AggregateRootUtils.GetAutoIncludeQuery(query);
return query; return query;
} }
} }
@ -166,7 +159,7 @@ namespace FreeSql
/// 2、返回的内容用可用于配合重写仓储 override Select 属性<para></para> /// 2、返回的内容用可用于配合重写仓储 override Select 属性<para></para>
/// 返回内容fsql.Select&lt;T&gt;().Include(...).IncludeMany(...) /// 返回内容fsql.Select&lt;T&gt;().Include(...).IncludeMany(...)
/// </summary> /// </summary>
protected string SelectAggregateRootStaticCode => $"//fsql.Select<{EntityType.Name}>()\r\nthis.SelectDiy{SelectAggregateRootNavigateReader(1, EntityType, "", new Stack<Type>())}"; protected string SelectAggregateRootStaticCode => $"//fsql.Select<{EntityType.Name}>()\r\nthis.SelectDiy{AggregateRootUtils.GetAutoIncludeQueryStaicCode(Orm, EntityType)}";
/// <summary> /// <summary>
/// ISelect.TrackToList 委托,数据返回后自动 Attach /// ISelect.TrackToList 委托,数据返回后自动 Attach
/// </summary> /// </summary>
@ -198,83 +191,40 @@ namespace FreeSql
return; return;
} }
} }
void SelectAggregateRootNavigateReader<T1>(ISelect<T1> currentQuery, Type entityType, string navigatePath, Stack<Type> ignores) //void SelectAggregateRootNavigateReader<T1>(ISelect<T1> currentQuery, Type entityType, string navigatePath, Stack<Type> ignores)
{ //{
if (ignores.Any(a => a == entityType)) return; // if (ignores.Any(a => a == entityType)) return;
ignores.Push(entityType); // ignores.Push(entityType);
var table = Orm.CodeFirst.GetTableByEntity(entityType); // var table = Orm.CodeFirst.GetTableByEntity(entityType);
if (table == null) return; // if (table == null) return;
if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}."; // if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}.";
foreach (var tr in table.GetAllTableRef()) // foreach (var tr in table.GetAllTableRef())
{ // {
var tbref = tr.Value; // var tbref = tr.Value;
if (tbref.Exception != null) continue; // if (tbref.Exception != null) continue;
var navigateExpression = $"{navigatePath}{tr.Key}"; // var navigateExpression = $"{navigatePath}{tr.Key}";
switch (tbref.RefType) // switch (tbref.RefType)
{ // {
case TableRefType.OneToOne: // case TableRefType.OneToOne:
if (ignores.Any(a => a == tbref.RefEntityType)) break; // if (ignores.Any(a => a == tbref.RefEntityType)) break;
currentQuery.IncludeByPropertyName(navigateExpression); // currentQuery.IncludeByPropertyName(navigateExpression);
SelectAggregateRootNavigateReader(currentQuery, tbref.RefEntityType, navigateExpression, ignores); // SelectAggregateRootNavigateReader(currentQuery, tbref.RefEntityType, navigateExpression, ignores);
break; // break;
case TableRefType.OneToMany: // case TableRefType.OneToMany:
var ignoresCopy = new Stack<Type>(ignores.ToArray()); // var ignoresCopy = new Stack<Type>(ignores.ToArray());
currentQuery.IncludeByPropertyName(navigateExpression, then => // currentQuery.IncludeByPropertyName(navigateExpression, then =>
SelectAggregateRootNavigateReader(then, tbref.RefEntityType, "", ignoresCopy)); // SelectAggregateRootNavigateReader(then, tbref.RefEntityType, "", ignoresCopy)); //variable 'then' of type 'FreeSql.ISelect`1[System.Object]' referenced from scope '', but it is not defined
break; // break;
case TableRefType.ManyToMany: // case TableRefType.ManyToMany:
currentQuery.IncludeByPropertyName(navigateExpression); // currentQuery.IncludeByPropertyName(navigateExpression);
break; // break;
case TableRefType.PgArrayToMany: // case TableRefType.PgArrayToMany:
case TableRefType.ManyToOne: //不属于聚合根 // case TableRefType.ManyToOne: //不属于聚合根
break; // break;
} // }
} // }
ignores.Pop(); // ignores.Pop();
} //}
string SelectAggregateRootNavigateReader(int depth, Type entityType, string navigatePath, Stack<Type> ignores)
{
var code = new StringBuilder();
if (ignores.Any(a => a == entityType)) return null;
ignores.Push(entityType);
var table = Orm.CodeFirst.GetTableByEntity(entityType);
if (table == null) return null;
if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}.";
foreach (var tr in table.GetAllTableRef())
{
var tbref = tr.Value;
if (tbref.Exception != null) continue;
var navigateExpression = $"{navigatePath}{tr.Key}";
var depthTab = "".PadLeft(depth * 4);
var lambdaAlias = (char)((byte)'a' + (depth - 1));
var lambdaStr = $"{lambdaAlias} => {lambdaAlias}.";
switch (tbref.RefType)
{
case TableRefType.OneToOne:
if (ignores.Any(a => a == tbref.RefEntityType)) break;
code.Append("\r\n").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
code.Append(SelectAggregateRootNavigateReader(depth, tbref.RefEntityType, navigateExpression, ignores));
break;
case TableRefType.OneToMany:
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression);
var thencode = SelectAggregateRootNavigateReader(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray()));
if (thencode.Length > 0) code.Append(", then => then").Append(thencode);
code.Append(")");
break;
case TableRefType.ManyToMany:
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression).Append(")");
break;
case TableRefType.PgArrayToMany:
code.Append("\r\n//").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression).Append(")");
break;
case TableRefType.ManyToOne: //不属于聚合根
code.Append("\r\n//").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
break;
}
}
ignores.Pop();
return code.ToString();
}
#endregion #endregion
} }

View File

@ -1,6 +1,7 @@
using FreeSql; using FreeSql;
using FreeSql.Extensions.EntityUtil; using FreeSql.Extensions.EntityUtil;
using FreeSql.Internal; using FreeSql.Internal;
using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.Model; using FreeSql.Internal.Model;
using System; using System;
using System.Collections; using System.Collections;
@ -8,7 +9,9 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text;
static class AggregateRootUtils static class AggregateRootUtils
{ {
@ -85,13 +88,13 @@ static class AggregateRootUtils
switch (tbref.RefType) switch (tbref.RefType)
{ {
case TableRefType.OneToOne: case TableRefType.OneToOne:
SetNavigateRelationshipValue(fsql, tbref, tbref.RefEntityType, entityBefore, propvalBefore); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
SetNavigateRelationshipValue(fsql, tbref, tbref.RefEntityType, entityAfter, propvalAfter); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null); LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null);
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
SetNavigateRelationshipValue(fsql, tbref, tbref.RefEntityType, entityBefore, propvalBefore); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
SetNavigateRelationshipValue(fsql, tbref, tbref.RefEntityType, entityAfter, propvalAfter); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
LocalCompareEntityValueCollection(tbref, propvalBefore as IEnumerable, propvalAfter as IEnumerable); LocalCompareEntityValueCollection(tbref, propvalBefore as IEnumerable, propvalAfter as IEnumerable);
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
@ -201,7 +204,7 @@ static class AggregateRootUtils
var propval = table.GetPropertyValue(entity, prop.Name); var propval = table.GetPropertyValue(entity, prop.Name);
statckPath.Push(prop.Name); statckPath.Push(prop.Name);
stackValues.Add(propval); stackValues.Add(propval);
SetNavigateRelationshipValue(fsql, tbref, tbref.RefEntityType, entity, propval); SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propval);
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues); callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
LocalNavigateReader(tbref.RefEntityType, propval); LocalNavigateReader(tbref.RefEntityType, propval);
stackValues.RemoveAt(stackValues.Count - 1); stackValues.RemoveAt(stackValues.Count - 1);
@ -209,7 +212,7 @@ static class AggregateRootUtils
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
var propvalOtm = table.GetPropertyValue(entity, prop.Name); var propvalOtm = table.GetPropertyValue(entity, prop.Name);
SetNavigateRelationshipValue(fsql, tbref, tbref.RefEntityType, entity, propvalOtm); SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propvalOtm);
var propvalOtmList = new List<object>(); var propvalOtmList = new List<object>();
foreach (var val in propvalOtm as IEnumerable) foreach (var val in propvalOtm as IEnumerable)
propvalOtmList.Add(val); propvalOtmList.Add(val);
@ -275,12 +278,12 @@ static class AggregateRootUtils
{ {
case TableRefType.OneToOne: case TableRefType.OneToOne:
var propvalTo = tbref.RefEntityType.CreateInstanceGetDefaultValue(); var propvalTo = tbref.RefEntityType.CreateInstanceGetDefaultValue();
SetNavigateRelationshipValue(fsql, tbref, tbref.RefEntityType, entityFrom, propvalFrom); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
LocalMapEntityValue(tbref.RefEntityType, propvalFrom, propvalTo); LocalMapEntityValue(tbref.RefEntityType, propvalFrom, propvalTo);
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo); EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo);
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
SetNavigateRelationshipValue(fsql, tbref, tbref.RefEntityType, entityFrom, propvalFrom); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, true); LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, true);
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
@ -315,6 +318,127 @@ static class AggregateRootUtils
} }
} }
static ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>> _dicGetAutoIncludeQuery = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>();
public static ISelect<TEntity> GetAutoIncludeQuery<TEntity>(ISelect<TEntity> select)
{
var select0p = select as Select0Provider;
var table0Type = select0p._tables[0].Table.Type;
var func = _dicGetAutoIncludeQuery.GetOrAdd(typeof(TEntity), t => new ConcurrentDictionary<Type, Action<ISelect0>>()).GetOrAdd(table0Type, t =>
{
var parmExp1 = Expression.Parameter(typeof(ISelect0));
var parmNavigateParameterExp = Expression.Parameter(typeof(TEntity), "a");
var parmQueryExp = Expression.Convert(parmExp1, typeof(ISelect<>).MakeGenericType(typeof(TEntity)));
var exp = LocalGetAutoIncludeQuery(parmQueryExp, 1, t, parmNavigateParameterExp, parmNavigateParameterExp, new Stack<Type>());
return Expression.Lambda<Action<ISelect0>>(exp, parmExp1).Compile();
});
func(select);
return select;
Expression LocalGetAutoIncludeQuery(Expression queryExp, int depth, Type entityType, ParameterExpression navigateParameterExp, Expression navigatePathExp, Stack<Type> ignores)
{
if (ignores.Any(a => a == entityType)) return queryExp;
ignores.Push(entityType);
var table = select0p._orm.CodeFirst.GetTableByEntity(entityType);
if (table == null) return queryExp;
foreach (var tr in table.GetAllTableRef())
{
var tbref = tr.Value;
if (tbref.Exception != null) continue;
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
Expression navigateExp = Expression.MakeMemberAccess(navigatePathExp, prop);
//var lambdaAlias = (char)((byte)'a' + (depth - 1));
switch (tbref.RefType)
{
case TableRefType.OneToOne:
if (ignores.Any(a => a == tbref.RefEntityType)) break;
LocalInclude(tbref, navigateExp);
queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores);
break;
case TableRefType.OneToMany:
LocalIncludeMany(tbref, navigateExp, true);
break;
case TableRefType.ManyToMany:
LocalIncludeMany(tbref, navigateExp, false);
break;
case TableRefType.PgArrayToMany:
break;
case TableRefType.ManyToOne: //不属于聚合根
break;
}
}
ignores.Pop();
return queryExp;
void LocalInclude(TableRef tbref, Expression exp)
{
var incMethod = queryExp.Type.GetMethod("Include");
if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany.Replace("IncludeMany", "Include"));
queryExp = Expression.Call(queryExp, incMethod.MakeGenericMethod(tbref.RefEntityType),
Expression.Lambda(typeof(Func<,>).MakeGenericType(entityType, tbref.RefEntityType), exp, navigateParameterExp));
}
void LocalIncludeMany(TableRef tbref, Expression exp, bool isthen)
{
var funcType = typeof(Func<,>).MakeGenericType(entityType, typeof(IEnumerable<>).MakeGenericType(tbref.RefEntityType));
var navigateSelector = Expression.Lambda(funcType, exp, navigateParameterExp);
var incMethod = queryExp.Type.GetMethod("IncludeMany");
if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany);
LambdaExpression navigateThen = null;
var navigateThenType = typeof(Action<>).MakeGenericType(typeof(ISelect<>).MakeGenericType(tbref.RefEntityType));
var thenParameter = Expression.Parameter(typeof(ISelect<>).MakeGenericType(tbref.RefEntityType), "then");
Expression paramQueryExp = thenParameter;
var paramNavigateParameterExp = Expression.Parameter(tbref.RefEntityType, string.Concat((char)((byte)'a' + (depth - 1))));
if (isthen) paramQueryExp = LocalGetAutoIncludeQuery(paramQueryExp, depth + 1, tbref.RefEntityType, paramNavigateParameterExp, paramNavigateParameterExp, ignores);
navigateThen = Expression.Lambda(navigateThenType, paramQueryExp, thenParameter);
queryExp = Expression.Call(queryExp, incMethod.MakeGenericMethod(tbref.RefEntityType), navigateSelector, navigateThen);
}
}
}
public static string GetAutoIncludeQueryStaicCode(IFreeSql fsql, Type rootEntityType)
{
return LocalGetAutoIncludeQueryStaicCode(1, rootEntityType, "", new Stack<Type>());
string LocalGetAutoIncludeQueryStaicCode(int depth, Type entityType, string navigatePath, Stack<Type> ignores)
{
var code = new StringBuilder();
if (ignores.Any(a => a == entityType)) return null;
ignores.Push(entityType);
var table = fsql.CodeFirst.GetTableByEntity(entityType);
if (table == null) return null;
if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}.";
foreach (var tr in table.GetAllTableRef())
{
var tbref = tr.Value;
if (tbref.Exception != null) continue;
var navigateExpression = $"{navigatePath}{tr.Key}";
var depthTab = "".PadLeft(depth * 4);
var lambdaAlias = (char)((byte)'a' + (depth - 1));
var lambdaStr = $"{lambdaAlias} => {lambdaAlias}.";
switch (tbref.RefType)
{
case TableRefType.OneToOne:
if (ignores.Any(a => a == tbref.RefEntityType)) break;
code.Append("\r\n").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores));
break;
case TableRefType.OneToMany:
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression);
var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray()));
if (thencode.Length > 0) code.Append(", then => then").Append(thencode);
code.Append(")");
break;
case TableRefType.ManyToMany:
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression).Append(")");
break;
case TableRefType.PgArrayToMany:
code.Append("\r\n//").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression).Append(")");
break;
case TableRefType.ManyToOne: //不属于聚合根
code.Append("\r\n//").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
break;
}
}
ignores.Pop();
return code.ToString();
}
}
public static List<object> GetManyToManyObjects(IFreeSql fsql, TableInfo table, TableRef tbref, object entity, PropertyInfo prop) public static List<object> GetManyToManyObjects(IFreeSql fsql, TableInfo table, TableRef tbref, object entity, PropertyInfo prop)
{ {
if (tbref.RefType != TableRefType.ManyToMany) return null; if (tbref.RefType != TableRefType.ManyToMany) return null;

View File

@ -28,6 +28,15 @@ namespace FreeSql.Tests.DbContext2
{ {
new OrderRepository(fsql, null); new OrderRepository(fsql, null);
fsql.Insert(new[]
{
new Tag { Name = "tag1" },
new Tag { Name = "tag2" },
new Tag { Name = "tag3" },
new Tag { Name = "tag4" },
new Tag { Name = "tag5" }
}).ExecuteAffrows();
var repo = fsql.GetAggregateRootRepository<Order>(); var repo = fsql.GetAggregateRootRepository<Order>();
var order = new Order var order = new Order
{ {
@ -35,13 +44,37 @@ namespace FreeSql.Tests.DbContext2
Extdata = new OrderExt { Field3 = "field3" }, Extdata = new OrderExt { Field3 = "field3" },
Details = new List<OrderDetail> Details = new List<OrderDetail>
{ {
[0] = new OrderDetail { Field4 = "field4_01", Extdata = new OrderDetailExt { Field5 = "field5_01" } }, new OrderDetail { Field4 = "field4_01", Extdata = new OrderDetailExt { Field5 = "field5_01" } },
[0] = new OrderDetail { Field4 = "field4_02", Extdata = new OrderDetailExt { Field5 = "field5_02" } }, new OrderDetail { Field4 = "field4_02", Extdata = new OrderDetailExt { Field5 = "field5_02" } },
[0] = new OrderDetail { Field4 = "field4_03", Extdata = new OrderDetailExt { Field5 = "field5_03" } }, new OrderDetail { Field4 = "field4_03", Extdata = new OrderDetailExt { Field5 = "field5_03" } },
}, },
Tags = fsql.Select<Tag>().Where(a => new[] { 1, 2, 3 }.Contains(a.Id)).ToList() Tags = fsql.Select<Tag>().Where(a => new[] { 1, 2, 3 }.Contains(a.Id)).ToList()
}; };
repo.Insert(order); //级联插入 repo.Insert(order); //级联插入
var order2 = repo.Select.Where(a => a.Id == a.Id).First();
Assert.NotNull(order2);
Assert.Equal(order.Id, order2.Id);
Assert.Equal(order.Field2, order2.Field2);
Assert.NotNull(order2.Extdata);
Assert.Equal(order.Extdata.Field3, order2.Extdata.Field3);
Assert.NotNull(order2.Details);
Assert.Equal(order.Details.Count, order2.Details.Count);
Assert.Equal(3, order2.Details.Count);
for (var a = 0; a < 3; a++)
{
Assert.Equal(order.Details[a].Id, order2.Details[a].Id);
Assert.Equal(order.Details[a].OrderId, order2.Details[a].OrderId);
Assert.Equal(order.Details[a].Field4, order2.Details[a].Field4);
Assert.NotNull(order2.Details[a].Extdata);
Assert.Equal(order.Details[a].Extdata.Field5, order2.Details[a].Extdata.Field5);
}
Assert.NotNull(order2.Tags);
Assert.Equal(3, order2.Tags.Count);
Assert.Equal("tag1", order2.Tags[0].Name);
Assert.Equal("tag2", order2.Tags[1].Name);
Assert.Equal("tag3", order2.Tags[2].Name);
} }
} }
class Order class Order
@ -103,7 +136,7 @@ namespace FreeSql.Tests.DbContext2
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
[Navigate(ManyToMany = typeof(Order))] [Navigate(ManyToMany = typeof(OrderTag))]
public List<Order> Orders { get; set; } public List<Order> Orders { get; set; }
} }
} }