- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
这更像是一个概念性问题。这是我目前的困境;我正在编写一个 vb.net WPF 应用程序并使用 MVVM 模式(喜欢它!可维护性非常棒)。目前所有代码都是手工编写的,没有使用 NHibernate 或 Entity Framework,因为后端是一个访问数据库(由于政策我不能使用 NH 并且 EF 不支持 JET 数据库,我们可能会在某个时候切换到 MSSQL但这可能需要一段时间)。
应用程序运行良好,想知道将更新发送回数据库的最佳方式是什么。
目前的方法是将 bool 值添加到模型设置部分的记录为“脏”,然后当按下更新时,我们遍历所有“脏”的记录并使用 oledbcommand(带参数执行)sql要更新的语句。
这创造了一个很好的关注点分离,但如果这是错误的方式,我想知道替代方案(请注意数据库类型和相关的缺点,例如它不适用于 EF)。
谢谢!
VB.NET 中的最终代码在评论等之后:
Public Class Car
Implements ICloneable
Public Property Make() As String
Get
Return m_Make
End Get
Set(ByVal value As String)
m_Make = value
End Set
End Property
Private m_Make As String
Public Property Model() As String
Get
Return m_Model
End Get
Set(ByVal value As String)
m_Model = value
End Set
End Property
Private m_Model As String
Public Function Clone() As Object Implements System.ICloneable.Clone
Return New Car() With { _
.Make = Me.Make, _
.Model = Me.Model _
}
End Function
End Class
Public Class CarEqualityComparer
Implements IEqualityComparer(Of Car)
Public Overloads Function Equals(ByVal x As Car, ByVal y As Car) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Car).Equals
Return x.Make = y.Make AndAlso x.Model = y.Model
End Function
Public Overloads Function GetHashCode(ByVal obj As Car) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Car).GetHashCode
Return 1 'http://blogs.msdn.com/b/jaredpar/archive/2008/06/03/making-equality-easier.aspx
End Function
End Class
Public Class CarRepository
Private _carComparator As New CarEqualityComparer
Private _cars As New ChangeTracker(Of Car)(_carComparator)
Public Function GetCars() As IEnumerable(Of Car)
'TODO: JET/ADO code here, you would obviously do in a for/while loop
Dim dbId1 As Integer = 1
Dim make1 As String = "Ford"
Dim model1 As String = "Focus"
Dim dbId2 As Integer = 2
Dim make2 As String = "Hyundai"
Dim model2 As String = "Elantra"
'TODO: create or update car objects
Dim car1 As Car
If Not _cars.IsTracking(dbId1) Then
car1 = New Car()
Else
car1 = _cars.GetItem(dbId1)
End If
car1.Make = make1
car1.Model = model1
If Not _cars.IsTracking(dbId1) Then
_cars.StartTracking(dbId1, car1)
End If
Dim car2 As Car
If Not _cars.IsTracking(dbId2) Then
car2 = New Car()
Else
car2 = _cars.GetItem(dbId2)
End If
car2.Make = make2
car2.Model = model2
If Not _cars.IsTracking(dbId2) Then
_cars.StartTracking(dbId2, car2)
End If
Return _cars.GetTrackedItems()
End Function
Public Sub SaveCars(ByVal cars As IEnumerable(Of Car))
'TODO: JET/ADO code here to update the item
Console.WriteLine("Distinct " & cars.Distinct.Count.ToString)
For Each changedItem As Car In _cars.GetChangedItems().Intersect(cars)
Console.Write("Saving: ")
Console.WriteLine(changedItem.Make)
Next
For Each newItem As Car In cars.Except(_cars.GetTrackedItems())
Console.Write("Adding: ")
Console.WriteLine(newItem.Make)
Dim newId As Integer = CInt(Math.Ceiling(Rnd() * 5000)) 'Random right now but JET/ADO to get the id later....
_cars.StartTracking(newId, newItem)
Next
Dim removalArray As New ArrayList
For Each deletedItem As Car In _cars.GetTrackedItems().Except(cars)
Console.Write("Removing: ")
Console.WriteLine(deletedItem.Make)
removalArray.Add(_cars.GetId(deletedItem)) 'Cannot remove right as iterating through array - clearly that would be problematic....
Next
For Each dbId As Integer In removalArray
_cars.StopTracking(dbId)
Next
_cars.SetNewCheckpoint()
End Sub
End Class
Public Class ChangeTracker(Of T As {ICloneable})
'item "checkpoints" that are internal to this list
Private _originals As New Dictionary(Of Integer, T)()
Private _originalIndex As New Dictionary(Of T, Integer)()
'the current, live-edited objects
Private _copies As New Dictionary(Of Integer, T)()
Private _copyIndex As New Dictionary(Of T, Integer)()
Private _comparator As System.Collections.Generic.IEqualityComparer(Of T)
Public Sub New(ByVal comparator As System.Collections.Generic.IEqualityComparer(Of T))
_comparator = comparator
End Sub
Public Function IsChanged(ByVal copy As T) As Boolean
Dim original = _originals(_copyIndex(copy))
Return Not _comparator.Equals(copy, original)
End Function
Public Function GetChangedItems() As IEnumerable(Of T)
Dim items As IEnumerable(Of T)
items = _copies.Values.Where(Function(c) IsChanged(c))
Return items
End Function
Public Function GetTrackedItems() As IEnumerable(Of T)
Return _copies.Values
End Function
Public Sub SetNewCheckpoint()
For Each copy In Me.GetChangedItems().ToList()
Dim dbId As Integer = _copyIndex(copy)
Dim oldOriginal = _originals(dbId)
Dim newOriginal = DirectCast(copy.Clone(), T)
_originals(dbId) = newOriginal
_originalIndex.Remove(oldOriginal)
_originalIndex.Add(newOriginal, dbId)
Next
End Sub
Public Sub StartTracking(ByVal dbId As Integer, ByVal item As T)
Dim newOriginal = DirectCast(item.Clone(), T)
_originals(dbId) = newOriginal
_originalIndex(newOriginal) = dbId
_copies(dbId) = item
_copyIndex(item) = dbId
End Sub
Public Sub StopTracking(ByVal dbId As Integer)
Dim original = _originals(dbId)
Dim copy = _copies(dbId)
_copies.Remove(dbId)
_originals.Remove(dbId)
_copyIndex.Remove(copy)
_originalIndex.Remove(original)
End Sub
Public Function IsTracking(ByVal dbId As Integer) As Boolean
Return _originals.ContainsKey(dbId)
End Function
Public Function IsTracking(ByVal item As T) As Boolean
Return _copyIndex.ContainsKey(item)
End Function
Public Function GetItem(ByVal dbId As Integer) As T
Return _copies(dbId)
End Function
Public Function GetId(ByVal item As T) As Integer
Dim dbId As Integer = (_copyIndex(item))
Return dbId
End Function
End Class
最佳答案
由于您使用更新/保存按钮将更改提交到数据库,我建议使用类似于存储库的模式,其中存储库会在执行保存操作时跟踪更改。
这类似于 Entity Framework 实现 self 跟踪实体 (STE) 的方式。在 EF STE 中,为您要跟踪的每个实体创建一个跟踪器对象,该对象监听类似于 PropertyChanged
的事件以确定对象是否“脏”。
这种方法的主要好处是您可以执行批量更新/删除,而无需存储模型或 View 模型的任何持久性状态,也不必始终将您拥有的所有内容保存到数据库中。这提供了更大的关注点分离(DAL vs M vs VM vs V)。我发现 MVVM 和存储库模式配合得很好。
这是整体方法:
这是我编写的一些示例代码:
首先,这里有一个名为 Car
的示例类,我们将在我们的存储库中使用它。请注意,对象上没有 Dirty 属性。
public class Car : IEquatable<Car>, ICloneable
{
public string Make { get; set; }
public string Model { get; set; }
public bool Equals(Car other)
{
return other.Make == this.Make &&
other.Model == this.Model;
}
public object Clone()
{
return new Car { Make = this.Make, Model = this.Model };
}
}
接下来,这是一个 CarRepository
,您将使用它来初始化数据库中的对象:
public class CarRepository
{
private ChangeTracker<Car> _cars = new ChangeTracker<Car>();
public IEnumerable<Car> GetCars()
{
//TODO: JET/ADO code here, you would obviously do in a for/while loop
int dbId1 = 1;
string make1 = "Ford";
string model1 = "Focus";
//TODO: create or update car objects
Car car1;
if (!_cars.IsTracking(dbId1))
car1 = new Car();
else
car1 = _cars.GetItem(dbId1);
car1.Make = make1;
car1.Model = model1;
if (!_cars.IsTracking(dbId1))
_cars.StartTracking(dbId1, car1);
return _cars.GetTrackedItems();
}
public void SaveCars(IEnumerable<Car> cars)
{
foreach (var changedItem in _cars.GetChangedItems().Intersect(cars))
{
//TODO: JET/ADO code here to update the item
}
foreach (var newItem in cars.Except(_cars.GetTrackedItems()))
{
//TODO: JET/ADO code here to add the item to the DB and get its new ID
int newId = 5;
_cars.StartTracking(newId, newItem);
}
_cars.SetNewCheckpoint();
}
}
最后,存储库用于跟踪更改和设置检查点的辅助类 ChangeTracker
。
public class ChangeTracker<T> where T : IEquatable<T>, ICloneable
{
//item "checkpoints" that are internal to this list
private Dictionary<int, T> _originals = new Dictionary<int, T>();
private Dictionary<T, int> _originalIndex = new Dictionary<T, int>();
//the current, live-edited objects
private Dictionary<int, T> _copies = new Dictionary<int, T>();
private Dictionary<T, int> _copyIndex = new Dictionary<T, int>();
public bool IsChanged(T copy)
{
var original = _originals[_copyIndex[copy]];
return original.Equals(copy);
}
public IEnumerable<T> GetChangedItems()
{
return _copies.Values.Where(c => IsChanged(c));
}
public IEnumerable<T> GetTrackedItems()
{
return _copies.Values;
}
public void SetNewCheckpoint()
{
foreach (var copy in this.GetChangedItems().ToList())
{
int dbId = _copyIndex[copy];
var oldOriginal = _originals[dbId];
var newOriginal = (T)copy.Clone();
_originals[dbId] = newOriginal;
_originalIndex.Remove(oldOriginal);
_originalIndex.Add(newOriginal, dbId);
}
}
public void StartTracking(int dbId, T item)
{
var newOriginal = (T)item.Clone();
_originals[dbId] = newOriginal;
_originalIndex[newOriginal] = dbId;
_copies[dbId] = item;
_copyIndex[item] = dbId;
}
public void StopTracking(int dbId)
{
var original = _originals[dbId];
var copy = _copies[dbId];
_copies.Remove(dbId);
_originals.Remove(dbId);
_copyIndex.Remove(copy);
_originalIndex.Remove(original);
}
public bool IsTracking(int dbId)
{
return _originals.ContainsKey(dbId);
}
public bool IsTracking(T item)
{
return _copyIndex.ContainsKey(item);
}
public T GetItem(int dbId)
{
return _liveCopies[dbId];
}
}
下面是您将如何在程序中使用您的存储库:
static void Main(string[] args)
{
var repository = new CarRepository();
var cars = repository.GetCars().ToArray();
//make some arbitrary changes...
cars[0].Make = "Chevy";
cars[1].Model = "Van";
//when we call SaveCars, the repository will detect that
//both of these cars have changed, and write them to the database
repository.SaveCars(cars);
}
这种朴素的实现依赖于 IEquatable 和 ICloneable,尽管这些当然不是必需的,并且可能有更好的做事方式,或者您可能有更有效的方法来确定项目是否已更改。 (例如,创建对象副本的想法并不完全是内存友好的)。您还需要处理已删除的项目,但这很容易添加到上面的示例中。
关于c# - 以 MVVM 模式更新记录以获得最大效率的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9707109/
“用 Haskell 进行函数式思考”中的练习之一是使用融合定律使程序更加高效。我在尝试复制答案时遇到了一些麻烦。 部分计算要求您将 maximum (xs++ map (x+) xs) 转换为 ma
我正在尝试获得 R 中最大/最小的可表示数字。 输入“.Machine”后 我有: $double.xmin [1] 2.225074e-308 $double.xmax [1] 1.797693e+
有没有办法更改浏览器验证消息 请检查所附图片。 我目前正在使用 wooCommerce 目前它显示小于或等于 X 个数字,我想更改为请求超过 X 个项目的报价。 请多多指教 最佳答案 您需要使用oni
我正在尝试将解决方案从 Excel 求解器复制到 R 中,但不知道从哪里开始。 问题: 每小时选择 5 个选项(5 行),以最大化“分数”的总和,而无需在多个小时内选择同一组 2 次。 换句话说: 最
Haskell 中是否有这样的功能: max_of_type :: (Num a) => a 所以: max_of_type :: Int == 2 ^ 31 - 1 // for example,
我有这两个表示时间范围(秒)的输入字段,我需要这样设置,以便“from/min”字段不能高于“to/max”,反之亦然。 到目前为止我得到了这个: jQuery(document).ready(fun
我有一个看起来像这样的表: http://sqlfiddle.com/#!9/152d2/1/0 CREATE TABLE Table1 ( id int, value decimal(10,
我会尝试尽可能简单地解释它: 首先是一些带有虚拟数据的数据库结构。 结构 tb_spec_fk feature value ----------------- 1 1 1
我有两个表。 表 1: +---------+---------+ | Lead_ID | Deal_ID | +---------+---------+ | 2323 | null |
我的数据库中有一个字段可以包含数字,例如8.00 或范围编号,例如8.00 - 10.00。 如果您将每个数字作为单独的数字,我需要从表中获取 MIN() 和 MAX()。例如当范围为 8.00 -
max(float('nan'), 1) 计算结果为 nan max(1, float('nan')) 计算结果为 1 这是预期的行为吗? 感谢您的回答。 max 在 iterable 为空时引发异常
我想问一下如何在 CSS 中创建一个页脚栏,它具有最小宽度(比如 650 像素),并且会根据窗口大小进行拉伸(stretch),但仅限于某个点(比如 1024 像素)。 我的意思是当窗口大小为例如 1
我尝试调整表格列宽(下一个链接上的“作者”列 http://deploy.jtalks.org/jcommune/branches/1?lang=en)。我已将最小/最大属性添加到 .author-c
在 C# 中,是否有用于将最小值和最大值存储为 double 值的内置类? 此处列出的要点 http://msdn.microsoft.com/en-us/library/system.windows
问题: 每个任务队列是否可以每秒处理超过 500 个任务? 每个 GAE 应用是否可以每秒处理超过 50,000 个任务? 详细信息: Task queue quota文档说: Push Queue
我想知道是否允许最大或最小堆树具有重复值?我试图仅通过在线资源查找与此相关的信息,但一直没有成功。 最佳答案 是的,他们可以。您可以在“算法简介”(Charles E. Leiserson、Cliff
首先,我是 .NET 开发人员,喜欢 C# 中的 LINQ 和扩展方法。 但是当我编写脚本时,我需要相当于 Enumerable extension methods 的东西 任何人都可以给我任何建议/
这是一个检查最大 malloc 大小的简单程序: #include std::size_t maxDataSize = 2097152000; //2000mb void MallocTest(vo
我想找到我的数据的最小值和最大值。 我的数据文件: 1 2 4 5 -3 -13 112 -3 55 42 42 而我的脚本: {min=max=$1} {if ($1max) {max=$1}
我想查询我的Elastic-Search以获取仅具有正值的最低价格价格。我的价格也可以为零和-1;所以我不希望我的最小聚合返回0或-1。我知道我应该向查询(或过滤器)添加脚本,但是我不知道如何。我当前
我是一名优秀的程序员,十分优秀!