C# CSLA 业务对象困境:只读与读/写

发布于 2024-08-01 16:36:54 字数 1765 浏览 9 评论 0原文

我所在的团队负责将旧的 VB6 UI/COBOL 数据库应用程序改造为现代版本。 在我被聘用之前,我决定(我确信主要是基于销售)在数据库之前重做用户界面。 所以,现在我们使用 WPF 和 MVVM 取得了很好的效果,到目前为止令人惊叹,特别是使用 CSLA 作为我们的模型层。

然而,由于我们的开发是与旧产品的下一个版本并行进行的,因此我们受到了一些限制。 我们无法对 COBOL 数据库的调用进行任何更改(或最小的更改)。 到目前为止,一切都还不错,但如果你相信这一点的话,你会回想起 SQL Server 的辉煌岁月。

关于我们的 BO 设计,我遇到的一个特别令人讨厌的障碍是处理列表中返回的“轻”业务对象及其“完整”对应对象。 让我尝试构建一个示例:

假设我们在数据库中有一个包含一堆字段的人员对象。 当我们对该表进行搜索时,我们不会返回所有字段,因此我们用这些字段填充我们的精简对象。 这些字段可能是也可能不是完整人员的子集。 我们可能已经进行了一两次连接来检索特定于搜索的其他一些信息。 但是,如果我们想要编辑 person 对象,我们必须再次调用以获取完整版本来填充 UI。 这给我们留下了两个对象,并试图在 1 个虚拟机中调整它们的状态,同时在删除、编辑和添加后尝试使其所在的任何父对象上的人员列表保持同步。 最初,我使我们的 lite person 对象派生自 ReadOnlyBase<>。 但现在我正在处理与处理完整 BO 列表相同的列表行为,除了半满、半精简版之外,我想我应该让精简版和完整版都派生自 BusinessBase< > 并简单地将精简版设置器属性设为私有。

还有其他人遇到过这个问题并找到解决方案吗? 睡了一觉之后,我想出了这个潜在的解决方案。 如果我们将 BO 的完整版本和精简版本包装在另一个 BO 中会怎么样,如下所示:

public class PersonFull : BusinessBase<PersonFull>
{
  ...
}
public class PersonLite : BusinessBase<PersonLite>
{
  ...
}

public class Person : BusinessBase<Person>
{
  public PersonFull PersonFull;
  public PersonLite PersonLite;
}
public class PersonList : BusinessListBase<PersonList, Person>
{
}

显然,所有内容都将是 CSLA 注册属性等,但为了简洁起见,它们是那里的字段。 在这种情况下,Person 和 PersonList 将保存所有工厂方法。 搜索操作后,PersonList 将由 Person 对象填充,PersonLite 成员已全部填充,而 PersonFull 对象全部为 null。 如果我们需要获取完整版本,我们只需告诉 Person 对象即可,现在我们有了 PersonFull 对象,因此我们可以填充编辑 UI。 如果要删除 Person 对象,我们可以使用适当的 CSLA 删​​除过程轻松地完成此操作,同时仍然保持所有正在侦听它的虚拟机的列表的完整性。

因此,我希望这对每个人都有意义,如果有人有不同的解决方案,他们已经成功采用或批评了这个解决方案,无论如何!

谢谢!

(转发自:http://forums.lhotka.net/forums/thread/35576。 .aspx)

I'm part of a team tasked to revamping our old VB6 UI/COBOL database application to modern times. Before I was hired, the decision was made (largely on sales, I'm sure) to redo the UI before the database. So, now we're using WPF and MVVM to great effect, it's been amazing so far, especially using CSLA as our Model layer.

However, because our development is side-by-side with the next version of the old product, we're constrained a bit. We can't make any changes (or minimal changes) to the calls made to the COBOL database. This has been fine so far, albeit pining back to the glory days of SQL Server if you can believe it.

Where I've hit a particularly nasty roadblock regarding our BO design is in dealing with "light" business objects returned in lists and their "full" counterparts. Let me try and construct an example:

Let's say we have a person object in the DB with a bunch of fields. When we do a search on that table, we don't return all the fields, so we populate our lite object with these. These fields may or may not be a subset of the full person. We may have done a join or two to retrieve some other information specific to the search. But, if we want to edit our person object, we have to make another call to get the full version to populate the UI. This leaves us with two objects and attempting to juggle their state in 1 VM, all the while trying to keep the person list in sync on whatever parent object it sits after delete, edit, and add. Originally, I made our lite person object derive from ReadOnlyBase<>. But now that I'm dealing with the same list behavior you'd have with a list of full BOs except with half full, half lite, I'm thinking I should've just made both the lite and full versions derive from BusinessBase<> and simply made the lite version setter properties private.

Has anyone else out there come across and found a solution for this? After sleeping on it, I've come up with this potential solution. What if we wrap the full and lite version of our BO in another BO, like this:

public class PersonFull : BusinessBase<PersonFull>
{
  ...
}
public class PersonLite : BusinessBase<PersonLite>
{
  ...
}

public class Person : BusinessBase<Person>
{
  public PersonFull PersonFull;
  public PersonLite PersonLite;
}
public class PersonList : BusinessListBase<PersonList, Person>
{
}

Obviously everything would be CSLA registered properties and such, but for the sake of brevity they're fields there. In this case Person and PersonList would hold all the factory methods. After a search operation PersonList would be populated by Person objects whose PersonLite members were all populated and the PersonFull objects were all null. If we needed to get the full version, we simply tell the Person object to do so, and now we have our PersonFull object so we can populate the edit UI. If the Person object is to be deleted, we can easily do this with the CSLA delete procedures in place, while still maintaining the integrity of our lists across all the VMs that are listening to it.

So, I hope this made sense to everyone, and if anyone has a different solution they've successfully employed or criticism of this one, by all means!

Thanks!

(Reposted from: http://forums.lhotka.net/forums/thread/35576.aspx)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

给不了的爱 2024-08-08 16:36:54
public class PersonLite : ReadOnlyBase<PersonLite>
{
    public void Update(PersonFull person) { }
}

public class PersonFull : BusinessBase<PersonFull>
{
    // blah blah
}

我将使用对“完整”对象所做的更改来更新“精简”对象,并将其保留为 ReadOnlyBase 。 重要的是要记住,ReadOnlyBase 中的“ReadOnly”表示仅从数据库读取且从不保存到数据库的对象。 一个不太优雅但更准确的名称是 NotSavableBase,因为此类对象缺乏用于除获取之外的任何操作的 DataPortal_XYZ 机制。 由于显而易见的原因,此类对象通常具有不可变的属性,但它们并非必须如此。 ReadOnlyBase 派生自 Core.BindableBase 并实现 INotifyPropertyChanged,因此更改其属性的值将可以很好地与绑定配合使用。

当您保存“完整”对象时,您将新保存的实例传递给列表中实例的 Update(PersonFull) 方法,并从“完整”的对象。

我已经多次使用这种技术并且效果很好。

public class PersonLite : ReadOnlyBase<PersonLite>
{
    public void Update(PersonFull person) { }
}

public class PersonFull : BusinessBase<PersonFull>
{
    // blah blah
}

I would update the "lite" object with the changes made to the "full" object, and leave it as ReadOnlyBase. It's important to remember that the "ReadOnly" in ReadOnlyBase means an object that is only read from the database, and never saved to it. A less elegant, but more accurate name would be NotSavableBase, because such objects lack the DataPortal_XYZ machinery for anything but fetches. For obvious reasons, such objects usually have immutable properties, but they don't have to. ReadOnlyBase derives from Core.BindableBase and implements INotifyPropertyChanged, so changing the values of its properties will work just fine with binding.

When you save your "full" object, you pass the newly saved instance to the Update(PersonFull) method of the instance that sits in your list, and update the properties of the "lite" object from the "full" object.

I've used this technique many times and it works just fine.

仙女 2024-08-08 16:36:54

如果您查看 CSLA 框架附带的 Rocky 示例,您会发现他总是将只读对象与读/写对象分开。 我认为这样做是有充分理由的,因为行为将会截然不同。 只读对象将更加基于性能,它们的验证将非常不同,并且通常具有更少的信息。 读/写对象不会基于性能,并且严重依赖验证、授权等。

但是,这会让您陷入当前的困境。我要做的是重载每个类的构造函数,以便您可以通过他们之间互相“复制”你需要的东西。

像这样的事情:

public class PersonLite : BusinessBase<PersonLite>
{
    public PersonLite(PersonFull fullPerson)
    {
        //copy from fullPerson's properties or whatever
    }
}

public class PersonFull : BusinessBase<PersonFull>
{
    public PersonFull(PersonLite litePerson)
    {
        //copy from litePerson's properties or whatever
    }
}

你也可以使用工厂模式来做到这一点,我相信这是 Rocky 的偏好。

If you look over Rocky's examples that come with the CSLA framework, you'll notice that he always separates the read only objects from the read/write objects. I think this is done for good reason, because the behaviors are going to be drastically different. Read only objects will be more performance based, their validation will be very different, and usually have less information altogether. The read/write objects will not be as perfomance based and rely heavily on validation, authorization, etc.

However, that leaves you with the dilemma you currently find yourself in. What I would do is overload the constructor of each class so you can pass them between each other and "copy" what you need out of each other.

Something like this:

public class PersonLite : BusinessBase<PersonLite>
{
    public PersonLite(PersonFull fullPerson)
    {
        //copy from fullPerson's properties or whatever
    }
}

public class PersonFull : BusinessBase<PersonFull>
{
    public PersonFull(PersonLite litePerson)
    {
        //copy from litePerson's properties or whatever
    }
}

You could do this with a factory pattern as well, which is Rocky's preference I believe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文