我有以下课程:
的档案
public class Item : INotifyPropertyChanged, IDataErrorInfo
{
private int? id;
public int? ID
{
get
{ return id; }
set
{ id = value; }
}
private string name;
public string Name
{
get
{ return name; }
set
{
if (value != name)
{
ClearError("Name");
if (string.IsNullOrEmpty(value) || value.Trim() == "")
SetError("Name", "Required Value");
name = value;
}
}
}
private List<MedicineComposition> medicineCompositions;
public List<MedicineComposition> MedicineCompositions
{
set { medicineCompositions = value; }
get { return medicineCompositions; }
}
}
MedicineComposition
public class MedicineComposition : INotifyPropertyChanged, IDataErrorInfo
{
private int? id;
public int? ID
{
get
{ return id; }
set
{ id = value; }
}
private Item item;
public Item Item
{
get
{ return item; }
set
{
if (item != value)
{
ClearError("Item");
if (value == null)
SetError("Item", "Required Value");
item = value;
}
}
}
private Component component;
public Component Component
{
get
{ return component; }
set
{
if (component != value)
{
ClearError("Component");
if (value == null)
SetError("Component", "Required Value");
component = value;
}
}
}
}
仅有id
和Name
的 组件
以及从数据库中获取数据并生成我的对象列表的以下函数:
Item
类
public static List<Item> GetAllItems
{
get
{
List<Item> MyItems = new List<Item>();
SqlConnection con = new SqlConnection(BaseDataBase.ConnectionString);
SqlCommand com = new SqlCommand("sp_Get_All_Item", con);
com.CommandType = System.Data.CommandType.StoredProcedure;
try
{
con.Open();
SqlDataReader rd = com.ExecuteReader();
while (rd.Read())
{
Item i = new Item();
if (!(rd["ID"] is DBNull))
i.ID = System.Int32.Parse(rd["ID"].ToString());
i.Name = rd["Name"].ToString();
i.MedicineCompositions = MedicineComposition.GetAllByItem(i);
MyItems.Add(i);
}
rd.Close();
}
catch
{
MyItems = null;
}
finally
{
con.Close();
}
return MyItems;
}
MedicalCompositions
中的GetAllByItem
public static List<MedicineComposition> GetAllByItem(Item i)
{
List<MedicineComposition> MyMedicineCompositions = new List<MedicineComposition>();
SqlConnection con = new SqlConnection(BaseDataBase.ConnectionString);
SqlCommand com = new SqlCommand("sp_Get_ByItemID_MedicineComposition", con);
com.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter pr = new SqlParameter("@ID", i.ID);
com.Parameters.Add(pr);
try
{
con.Open();
SqlDataReader rd = com.ExecuteReader();
while (rd.Read())
{
MedicineComposition m = new MedicineComposition() { };
if (!(rd["ID"] is DBNull))
m.ID = Int32.Parse(rd["ID"].ToString());
if (!(rd["ComponentID"] is DBNull))
m.Component = Component.GetByID(Int32.Parse(rd["ComponentID"].ToString()));
m.Item = i;
MyMedicineCompositions.Add(m);
}
rd.Close();
}
catch
{
MyMedicineCompositions = null;
}
finally
{
con.Close();
}
return MyMedicineCompositions;
}
喜欢使用mvvm
,因为它可以让你处理对象而不是datatable
,但是当我使用以前的类结构形状时,我有以下问题:
Item
表中至少有1000条记录,所以当我调用GetAllItems
时,我的性能会很慢,尤其是当数据库不在本地计算机上时。Items
,需要时间但需要中等性能Item
表格上的每次更新时,我都应该记得GetAllItems
这么慢
我的问题是我在创建类时遇到的问题,这是在mvvm
答案 0 :(得分:4)
我不认为你的用户需要一目了然地看到所有1000个项目,甚至没有相关的数千个组合和组件。
我这样的情况我会:
答案 1 :(得分:2)
您可以在此处改进一些事项,例如:
MedicalComposition
,拥有nullable
唯一标识符id
和name
的课程,则可以使用KeyValuePair<>
或Tuple<>
代替ModelBase
INotifyPropertyChanged
repository pattern
,如果可能,实施缓存/页面结果Item
你有MedicineComposition
的IEnumerable,但MedicineComposition
你也有Item
吗?也许你根本不需要它或相关Item.Id
就足够了? <timestamp>
以来已添加/修改/删除的项目,并仅更新Items
集合中所需的内容Lazy<>
TAP
(基于任务的异步模式)以下是您的问题的“一气呵成”,无法阻止UI线程。它远非完整但仍然存在。存储库中的Thread.Sleep
正在模仿您的数据库查询延迟
查看\ MainWindow.xaml 强>
Codebehind仅包含InitializeComponents
。
<Window x:Class="WpfApplication1.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:WpfApplication1.ViewModel"
Title="MainWindow"
Height="300"
Width="250">
<Window.DataContext>
<viewModel:MainViewModel />
</Window.DataContext>
<!-- Layout root -->
<Grid x:Name="ContentPanel" Margin="12,0,12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Status label -->
<Label Grid.Row="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Background="Bisque"
Margin="0,3,0,3"
Content="{Binding Status}" />
<!-- Controls -->
<StackPanel Grid.Row="1">
<Label Content="Items" />
<!-- Items combo -->
<ComboBox HorizontalAlignment="Stretch"
MaxDropDownHeight="120"
VerticalAlignment="Top"
Width="Auto"
Margin="0,0,0,5"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
DisplayMemberPath="Name" />
<!-- Medicine components -->
<ItemsControl ItemsSource="{Binding SelectedItem.MedicineCompositions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<!-- Components -->
<ItemsControl ItemsSource="{Binding Components}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text=" * " />
<Run Text="{Binding Name}" />
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</Window>
<强>视图模型\ MainViewModel 强>
public class MainViewModel : ViewModelBase
{
private string _status;
private Item _selectedItem;
private ObservableCollection<Item> _items;
public MainViewModel()
:this(new ItemRepository(), new MedicineCompositionRepository())
{}
public MainViewModel(IRepository<Item> itemRepository, IRepository<MedicineComposition> medicineCompositionRepository)
{
ItemRepository = itemRepository;
MedicineCompositionRepository = medicineCompositionRepository;
Task.Run(() => LoadItemsData());
}
public IRepository<Item> ItemRepository { get; set; }
public IRepository<MedicineComposition> MedicineCompositionRepository { get; set; }
public Item SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
Task.Run(() => LoadMedicineCompositionsData(_selectedItem));
}
}
public ObservableCollection<Item> Items
{
get { return _items; }
set { _items = value; OnPropertyChanged(); }
}
public string Status
{
get { return _status; }
set { _status = value; OnPropertyChanged(); }
}
private async Task LoadItemsData()
{
Status = "Loading items...";
var result = await ItemRepository.GetAll();
Items = new ObservableCollection<Item>(result);
Status = "Idle";
}
private async Task LoadMedicineCompositionsData(Item item)
{
if (item.MedicineCompositions != null)
return;
Status = string.Format("Loading compositions for {0}...", item.Name);
var result = await MedicineCompositionRepository.GetById(item.Id);
SelectedItem.MedicineCompositions = result;
Status = "Idle";
}
}
<强>模型强>
public class Component : ModelBase
{}
public class MedicineComposition : ModelBase
{
private IEnumerable<Component> _component;
public IEnumerable<Component> Components
{
get { return _component; }
set { _component = value; OnPropertyChanged(); }
}
}
public class Item : ModelBase
{
private IEnumerable<MedicineComposition> _medicineCompositions;
public IEnumerable<MedicineComposition> MedicineCompositions
{
get { return _medicineCompositions; }
set { _medicineCompositions = value; OnPropertyChanged(); }
}
}
public abstract class ModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
private string _name;
public int Id
{
get { return _id; }
set { _id = value; OnPropertyChanged(); }
}
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
<强>存储库强>
public interface IRepository<T> where T : class
{
Task<IEnumerable<T>> GetAll();
Task<IEnumerable<T>> GetById(int id);
}
public class ItemRepository : IRepository<Item>
{
private readonly IList<Item> _mockItems;
public ItemRepository()
{
_mockItems = new List<Item>();
for (int i = 0; i < 100; i++)
_mockItems.Add(new Item { Id = i, Name = string.Format("Item #{0}", i), MedicineCompositions = null });
}
public Task<IEnumerable<Item>> GetAll()
{
Thread.Sleep(1500);
return Task.FromResult((IEnumerable<Item>) _mockItems);
}
public Task<IEnumerable<Item>> GetById(int id)
{
throw new NotImplementedException();
}
}
public class MedicineCompositionRepository : IRepository<MedicineComposition>
{
private readonly Random _random;
public MedicineCompositionRepository()
{
_random = new Random();
}
public Task<IEnumerable<MedicineComposition>> GetAll()
{
throw new NotImplementedException();
}
public Task<IEnumerable<MedicineComposition>> GetById(int id)
{
// since we are mocking, id is actually ignored
var compositions = new List<MedicineComposition>();
int compositionsCount = _random.Next(1, 3);
for (int i = 0; i <= compositionsCount; i++)
{
var components = new List<Component>();
int componentsCount = _random.Next(1, 3);
for (int j = 0; j <= componentsCount; j++)
components.Add(new Component {Id = j, Name = string.Format("Component #1{0}", j)});
compositions.Add(new MedicineComposition { Id = i, Name = string.Format("MedicalComposition #{0}", i), Components = components });
}
Thread.Sleep(500);
return Task.FromResult((IEnumerable<MedicineComposition>) compositions);
}
}
答案 2 :(得分:1)
将数据集分配到ObservableCollection
属性的构造函数中。否则,您的视图将通过PropertyChanged
通知更新ObservableCollection执行添加操作的每个项目。
试试这个:
var items = services.LoadItems();
myObservableCollection = new ObservableCollection<somedatatype>(items);
这种类型的分配将通知您的视图一次,而不是您的实现执行1000次的当前方式。
答案 3 :(得分:1)
不是返回List,而是返回IEnumerable并在需要时生成结果。当你没有阅读所有结果时,显然它只会提高性能,这在大多数情况下都是如此。要做到这一点,你必须删除捕获,因为你不能有收益和集合。捕获可以绕过con.Open和ExecuteReader,在catch中你可以产生break:
public static IEnumerable<MedicineComposition> GetAllByItem(Item i)
{
SqlConnection con = new SqlConnection(BaseDataBase.ConnectionString);
SqlCommand com = new SqlCommand("sp_Get_ByItemID_MedicineComposition", con);
com.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter pr = new SqlParameter("@ID", i.ID);
com.Parameters.Add(pr);
try
{
SqlDataReader rd;
try
{
con.Open();
rd = com.ExecuteReader();
}
catch { yield break;}
while (rd.Read())
{
MedicineComposition m = new MedicineComposition() { };
if (!(rd["ID"] is DBNull))
m.ID = Int32.Parse(rd["ID"].ToString());
if (!(rd["ComponentID"] is DBNull))
m.Component = Component.GetByID(Int32.Parse(rd["ComponentID"].ToString()));
m.Item = i;
yield return m;
}
rd.Close();
}
finally
{
con.Close();
}
}
现在,如果发生异常,则不再返回null,但可以返回少量项甚至空枚举。我宁愿把捕获物移动到这个吸气剂的来电者身上。 如果由于某种原因需要返回项目的计数,请调用GetAllByItem(item).ToArray()。这将枚举所有项目并为您获取长度。绝对不要调用枚举两次来获取长度,然后枚举项目:
var length = GetAllByItem(item).Count();// this will get all the items from the db
foreach(var i in GetAllByItem(item)) // this will get all the items from the db again
而是这样做:
var list = GetAllByItem(item); // this will get all the items and now you have the length and the items.
显然,如果由于某种原因需要长度,那么改为IEnumerable没有意义,只是为了更好的抽象。
其他改进可能是,只创建一次连接,而不是每次调用getter。只有这样才有可能,如果你知道它不会造成任何伤害。