修改了CopyTo方法,使用反射可以将列表的内容也可以复制

This commit is contained in:
2025-06-24 20:48:38 +08:00
parent 8fcd2fdf2a
commit f76e88b330
9 changed files with 230 additions and 43 deletions

View File

@@ -1,4 +1,6 @@
using System.Windows;
using System.Windows.Controls;
using iNKORE.UI.WPF.Modern.Common.IconKeys;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
@@ -60,11 +62,29 @@ public partial class App : Application
{
base.OnStartup(e);
InitDB();
// InitMenu();
MainWindow = Services.GetRequiredService<MainView>();
MainWindow.Show();
}
private void InitMenu()
{
using (var db = DbContext.GetInstance())
{
List<DbMenu> items = new List<DbMenu>();
items.Add(new DbMenu() { Id = 1, Name = "主页", Icon = SegoeFluentIcons.Home.Glyph, ParentId = 0});
items.Add(new DbMenu() { Id = 1, Name = "设备", Icon = SegoeFluentIcons.Devices.Glyph, ParentId = 0});
items.Add(new DbMenu() { Id = 1, Name = "数据转换", Icon = SegoeFluentIcons.Move.Glyph, ParentId = 0});
items.Add(new DbMenu() { Id = 1, Name = "设置", Icon = SegoeFluentIcons.Settings.Glyph, ParentId = 0});
items.Add(new DbMenu() { Id = 1, Name = "关于", Icon = SegoeFluentIcons.Info.Glyph, ParentId = 0});
db.Insertable<DbMenu>(items).ExecuteCommand();
}
}
private void InitDB()
{
var _db = DbContext.GetInstance();
@@ -77,5 +97,6 @@ public partial class App : Application
_db.CodeFirst.InitTables<DbS7DataVariable>();
_db.CodeFirst.InitTables<DbUser>();
_db.CodeFirst.InitTables<DbMqtt>();
_db.CodeFirst.InitTables<DbMenu>();
}
}

16
Data/Entities/DbMenu.cs Normal file
View File

@@ -0,0 +1,16 @@
using SqlSugar;
namespace PMSWPF.Data.Entities;
[SugarTable("Menu")]
public class DbMenu
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
public string Icon { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
[SugarColumn(IsIgnore = true)]
public List<DbMenu> Items { get; set; }
}

View File

@@ -3,11 +3,17 @@ using PMSWPF.Excptions;
using PMSWPF.Extensions;
using PMSWPF.Helper;
using PMSWPF.Models;
using SqlSugar;
namespace PMSWPF.Data.Repositories;
public class DevicesRepositories : BaseRepositories
public class DevicesRepositories
{
private SqlSugarClient _db;
public DevicesRepositories()
{
_db = DbContext.GetInstance();
}
public async Task<bool> Add(Device device)
{
var exist = await _db.Queryable<DbDevice>().Where(d => d.Name == device.Name).FirstAsync();
@@ -30,8 +36,7 @@ public class DevicesRepositories : BaseRepositories
var devices = new List<Device>();
foreach (var dbDevice in dlist)
{
var device = dbDevice.NewTo<Device>();
device.VariableTables = CovertHelper.ConvertList<DbVariableTable, VariableTable>(dbDevice.VariableTables);
var device = dbDevice.CopyTo<Device>();
devices.Add(device);
}

View File

@@ -0,0 +1,25 @@
using System.Windows.Controls;
using PMSWPF.Data.Entities;
using SqlSugar;
namespace PMSWPF.Data.Repositories;
public class MenuRepositories
{
private readonly SqlSugarClient _db;
public MenuRepositories()
{
_db=DbContext.GetInstance();
}
public async Task<List<DbMenu>> GetMenu()
{
return await _db.Queryable<DbMenu>().ToListAsync();
}
public async Task<int> AddMenu(DbMenu dbMenu)
{
return await _db.Insertable<DbMenu>(dbMenu).ExecuteCommandAsync();
}
}

View File

@@ -1,53 +1,161 @@
namespace PMSWPF.Extensions;
using System.Collections;
using System.Reflection;
namespace PMSWPF.Extensions;
public static class ObjectExtensions
{
/// <summary>
/// 对象转换将source对象上的所有属性的值都转换到target对象上
/// 复制一个新的对象并返回
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
/// <typeparam name="T"></typeparam>
public static void CopyTo<T>(this object source, T target)
/// <param name="source">源对象</param>
/// <typeparam name="T">新的对象类型</typeparam>
/// <returns>新的对象</returns>
public static T CopyTo<T>(this Object source) where T : new()
{
var sourceType = source.GetType();
var targetType = target.GetType();
var sourceProperties = sourceType.GetProperties();
foreach (var sourceProperty in sourceProperties)
T t = new T();
CopyTo(source, t);
return t;
}
/// <summary>
/// 将可读写的公共属性值从源对象复制到目标对象。
/// 属性名称和类型必须匹配或者它们是具有匹配元素名称的泛型List类型。
/// 对嵌套对象和列表执行深拷贝。
/// </summary>
/// <param name="tsource">源对象。</param>
/// <param name="ttarget">目标对象。</param>
public static void CopyTo(this object tsource, object ttarget)
{
// 1. 基本的空值检查,提高健壮性
if (tsource == null)
{
var targetProperty = targetType.GetProperty(sourceProperty.Name);
if (targetProperty != null && targetProperty.CanWrite && sourceProperty.CanRead &&
targetProperty.PropertyType == sourceProperty.PropertyType)
// Console.WriteLine("源对象为空。无法复制。");
throw new ArgumentNullException("源对象为空。无法复制。");
return;
}
if (ttarget == null)
{
// Console.WriteLine("目标对象为空。无法复制。");
throw new ArgumentNullException("目标对象为空。无法复制。");
return;
}
Type sourceType = tsource.GetType();
Type targetType = ttarget.GetType();
// 2. 缓存源类型的公共实例属性,避免在循环中重复获取
PropertyInfo[] sourceProperties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo sourceProperty in sourceProperties)
{
// 确保源属性可读
if (!sourceProperty.CanRead)
{
var value = sourceProperty.GetValue(source, null);
targetProperty.SetValue(target, value, null);
continue;
}
// 在目标类型中查找对应的属性
PropertyInfo targetProperty =
targetType.GetProperty(sourceProperty.Name, BindingFlags.Public | BindingFlags.Instance);
// 确保目标属性存在且可写
if (targetProperty != null && targetProperty.CanWrite)
{
object sourceValue = sourceProperty.GetValue(tsource);
// 场景 1: 属性类型完全相同
if (targetProperty.PropertyType == sourceProperty.PropertyType)
{
targetProperty.SetValue(ttarget, sourceValue);
}
// 场景 2: 属性类型不同,但可能是泛型 List<T> 类型
else
{
bool isSourceList = sourceProperty.PropertyType.IsGenericType &&
sourceProperty.PropertyType.GetGenericTypeDefinition() == typeof(List<>);
bool isTargetList = targetProperty.PropertyType.IsGenericType &&
targetProperty.PropertyType.GetGenericTypeDefinition() == typeof(List<>);
if (isSourceList && isTargetList)
{
// 获取源列表的元素类型
Type sourceListItemType = sourceProperty.PropertyType.GetGenericArguments()[0];
// 获取目标列表的元素类型
Type targetListItemType = targetProperty.PropertyType.GetGenericArguments()[0];
// 检查列表元素类型名称是否匹配(用于潜在的深拷贝)
if (sourceProperty.Name == targetProperty.Name)
{
// 如果源列表为空,则将目标属性设置为 null
if (sourceValue == null)
{
targetProperty.SetValue(ttarget, null);
continue;
}
// 将源值强制转换为 IEnumerable 以便遍历
var sourceList = (IEnumerable)sourceValue;
// 创建一个目标泛型列表的新实例
// 假设 CreateGenericList 助手方法可用,或按注释创建
// var targetList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(targetListItemType));
IList targetList = CreateGenericList(targetListItemType);
foreach (var item in sourceList)
{
// 确保列表项不为空,然后尝试深拷贝
if (item == null)
{
// 如果源列表项为 null则直接添加到目标列表
targetList.Add(null);
continue;
}
// 创建目标列表元素类型的一个实例
var targetDataItem = Activator.CreateInstance(targetListItemType);
// 递归调用 CopyTo对嵌套对象进行深拷贝
CopyTo(item, targetDataItem);
// 将复制后的项添加到目标列表
targetList.Add(targetDataItem);
}
// 将填充好的目标列表设置给目标对象的属性
targetProperty.SetValue(ttarget, targetList);
}
else
{
// 列表类型匹配,但元素类型名称不匹配。此处跳过,或可实现更复杂的转换。
Console.WriteLine(
$"由于列表元素类型不匹配,跳过属性 '{sourceProperty.Name}':源 '{sourceListItemType.Name}',目标 '{targetListItemType.Name}'");
}
}
else
{
// 类型不同,且都不是泛型 List<T>。此处跳过,或可实现类型转换。
Console.WriteLine(
$"由于类型不匹配,跳过属性 '{sourceProperty.Name}':源 '{sourceProperty.PropertyType.Name}',目标 '{targetProperty.PropertyType.Name}'");
}
}
}
}
}
/// <summary>
/// 创建一个泛型对象将source对象上的所有属性的值都转换到新创建对象上
/// 辅助方法,用于动态创建泛型 List<T>。
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
/// <typeparam name="T"></typeparam>
public static T NewTo<T>(this object source) where T : new()
/// <param name="itemType">列表应包含的元素类型。</param>
/// <returns>List<itemType> 的 IList 实例。</returns>
private static IList CreateGenericList(Type itemType)
{
var target = new T();
var sourceType = source.GetType();
var targetType = target.GetType();
var sourceProperties = sourceType.GetProperties();
foreach (var sourceProperty in sourceProperties)
{
var targetProperty = targetType.GetProperty(sourceProperty.Name);
if (targetProperty != null && targetProperty.CanWrite && sourceProperty.CanRead &&
targetProperty.PropertyType == sourceProperty.PropertyType)
{
var value = sourceProperty.GetValue(source, null);
targetProperty.SetValue(target, value, null);
}
}
return target;
Type listType = typeof(List<>).MakeGenericType(itemType);
return (IList)Activator.CreateInstance(listType);
}
}

View File

@@ -25,5 +25,8 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Compile Remove="Data\Repositories\BaseRepositories.cs" />
</ItemGroup>
</Project>

View File

@@ -23,6 +23,6 @@ public partial class DeviceDialogViewModel : ObservableObject
[RelayCommand]
public void AddDevice()
{
device.CopyTo<Device>(_saveDevice);
device.CopyTo(_saveDevice);
}
}

View File

@@ -1,4 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using PMSWPF.Data.Entities;
using PMSWPF.Data.Repositories;
using PMSWPF.Services;
namespace PMSWPF.ViewModels;
@@ -8,16 +11,22 @@ public partial class MainViewModel : ViewModelBase
private readonly NavgatorServices _navgatorServices;
[ObservableProperty] private ViewModelBase currentViewModel;
[ObservableProperty]
private ObservableCollection<DbMenu> _menus;
public MainViewModel(NavgatorServices navgatorServices)
{
_navgatorServices = navgatorServices;
_navgatorServices.OnViewModelChanged += () => { CurrentViewModel = _navgatorServices.CurrentViewModel; };
CurrentViewModel = new HomeViewModel();
CurrentViewModel.OnLoaded();
}
public override void OnLoaded()
public override async void OnLoaded()
{
MenuRepositories mr = new MenuRepositories();
var menuList= await mr.GetMenu();
Menus=new ObservableCollection<DbMenu>(menuList);
}
}

View File

@@ -11,7 +11,7 @@
Title="设备管理系统"
Width="1080"
Height="800"
ui:WindowHelper.UseModernWindowStyle="True"
ui:WindowHelper.SystemBackdropType="Mica"
d:DataContext="{d:DesignInstance vm:MainViewModel}"