我在C#中创建了一个win form解决方案。该解决方案有三个项目,前层(FL)业务层(BL)和数据层(DL)。
前层有各种形式。所有表单都实现了IForms接口 业务层具有所有业务逻辑。所有业务逻辑类都实现了IController接口 数据层具有与sql数据库通信的所有数据逻辑。所有数据逻辑类都继承IModel接口
我正在尝试使用MVC设计模式,但我有点挣扎 目标:当我按下表单中的按钮时,调用BL类中的方法。
如果我将BL dll添加到FL,那么我可以调用BL的方法。
BL.Class classObject = new BL.Class(this);
classObject.CallMethod();
在BL中,我希望得到调用该方法的对象的引用,所以我尝试添加FL dll的引用,但是它表示循环dll引用。
我想在BL类中有一个构造函数:
公共BL.Class(IView视图) { 的MessageBox(view.Name); }
如何构建我的解决方案来实现这一目标?请注意,解决方案很大,所以我不能将所有类合并到一个DLL中。
保重, FM
答案 0 :(得分:4)
您正尝试将演示逻辑注入业务层。你的BL不应该创建消息框 - 这是表示层的可重用性。
答案 1 :(得分:2)
我知道你的编程胜利,但你可能想看看WebFormsMVP或MVVM模式以获得一些想法。
“在BL中,我希望得到调用该方法的对象的引用,所以我尝试添加FL dll的引用,但是它表示循环dll引用。”
特别是在WebFormsMVP项目中,参数在层之间根据它们在.Net框架中的方式传递,例如(object sender,mySpecialEventArgs e) - 参数“mySpecialEventArgs”中的第二个参数具有通过的所有信息来回。
答案 2 :(得分:2)
您可以在BL中为FL定义事件以进行注册。每当模型发生变化时,BL都应该触发一个事件。这是视图处理它。
您的BL创建了一个消息框。如果您的观点是WebForm怎么办? BL不应该知道视图的细节。
此外,您的BL项目不应该有FL的dll引用。你需要在BL中声明IView,而不是FL。
编辑: 这是我之前使用的结构。你的BL定义了界面。如果想要使用BL,您的视图必须实现该接口。 BL使用该界面与视图进行交互。这样,您可以构建使用相同BL的不同视图。
BL.dll
public interface IView
{
/// <summary>Update the view</summary>
void UpdateView(object myBusinessObject);
/// <summary>Display message</summary>
void ShowMessage(string msg);
}
public class BL
{
// Model and View
private IView _view;
/// <summary>Constructor</summary>
public BL (IView view)
{
_view = view;
}
public void foo()
{
// Do something
// Show message
_view.ShowMessage("Hello World");
}
}
FL.dll
public class FL
{
private BL _myBL;
/// <summary>Constructor</summary>
public FL ()
{
_myBL = new BL(this);
}
/// <summary>Handles user event</summary>
public void handleEvent()
{
// Call BL to do something
_myBL.foo();
}
public void UpdateView(object myBusinessObject)
{
// Update your view
}
public void ShowMessage(string msg)
{
// Display message to user
}
}
答案 3 :(得分:2)
分层架构背后的基本思想是第n层应该与第n + 1层对话但是从不跨越层之间,因此第n层不能跳转并直接与第n + 2层对话。
这是一个架构原则,现在归结为你的情况。
<1>首先,您正在讨论3层体系结构,它不是3层体系结构,因为您没有在sepreate服务器中托管DAL OR BL,而是在解决方案中创建单独的层(通过创建单独的项目)2-由于您有三个DLLS(FL,BL,DAL),所以现在您的分层必须是FL - &gt; BL ----&GT; DAL在哪里---&gt;代表与之交谈。
您将如何在应用程序中获得上述路径(FL - &gt; BL ----&gt; DAL)?
为此,您可以在BL项目中添加DAL的引用,并在FL项目中添加BL引用,这样就可以遵循上述架构原则。
如何在您的情况下实施MVC?
1-'C'代表MVC中的Controller,Controller是一个负责更新FL和你的模型的类。因此,您需要为应用程序中的各个独特功能创建一个独特的控制器(请注意,您不需要为应用程序中的每个表单创建一个控制器),因此如果您有订单模块而不是创建OrderBaseController并将此控制器作为引用您的观点(Winform)
2-在步骤1中创建的控制器可以公开模型(记住,我们在FL中有BL的引用),OrderView可以使用它。
3- View可以通过控制器更改模型(注意模型在BL中但我们在FL中有参考)但实际的数据库更改仍未提交。
您将如何执行上述第3步?
interface IOrderController
{
bool SaveOrder(order order);
bool ValidateOrder(order order);
order GetOrder();
}
public class OrderBaseController : IOrderController
{
private OrderServiceFacade Orderhelper { get; set; }
public OrderBaseController()
{
Orderhelper = new OrderServiceFacade();
}
public bool ValidateOrder(order objOrder)
{
}
#region IOrderController Members
public bool SaveOrder(order order)
{
bool success = false;
if (ValidateOrder(order))
{
success = Orderhelper.SaveOrder(order);
}
return success;
}
#endregion
#region IOrderController Members
public order GetOrder()
{
throw new NotImplementedException();
}
#endregion
}
我们刚刚实现了一个ordercontroller。现在是时候将这个控制器附加到视图中了。
public partial class Form1 : Form
{
IOrderController Controller;
public order OrderToBeSaved { get; set; }
public Form1()
{
InitializeComponent();
Controller = new OrderBaseController(); // you have the controller ,
// controller creation can also be delegated to some other component but that's totally different issue.
OrderToBeSaved = Controller.GetOrder(); // You got the order object here , once you get the order object you can bind its properties to the different control.
}
}
你有完全加载的订单对象,你可以使用它的属性来绑定表单上的不同控件。
现在是保存订单的时候了
private void btnSave_Click(object sender, EventArgs e)
{
Controller.SaveOrder(OrderToBeSaved);
}
这里我们已经将保存逻辑删除到控制器,现在让我们看看如何从控制器中保存。 Befor Controller保存顺序,它必须通过链,即它必须有一些逻辑与BL通信(控制器在FL中)
由于我们在FL中引用了BL,我们可以直接与BL交谈,但是我们再经历了一个我们称之为ServiceFacade的设计概念,一个ServiceFacade将用于将任务从一个层委托给另一个层层
class OrderServiceFacade
{
public bool SaveOrder(order order)
{
return OrderDAO.SaveOrder(order);
}
}
最后我们需要在数据库中保存订单,因此我们需要在DAL中定义一个类,我们称之为OrderDAO。
static class OrderDAO
{
static public bool SaveOrder(order order)
{
// put logic to access database and save the data.
}
static DataTable GetOrder(int OrderID);
}
它不完整,但它让你了解MVC如何在分层的情节中运作。
答案 4 :(得分:1)
实际上有人建议设计这种类型的东西。大多数关于此的文档都专门针对WPF,如果你查找MVVM(MVC的另一个名字),你会发现它。
基本上,你需要一个第四个dll,它是一种框架层。这就是你需要的:
ServiceProvider类 - 实现IServiceProvider的东西。
为什么我们需要这个?因为您的FL将提供UI服务。在你的情况下一个MessageBox服务。但是,您可能会发现这非常有用,您将需要创建其他服务。喜欢浏览文件或文件夹的东西。还有更多,但我们现在就坚持这一点。
您的ServiceProvider类还应保留其知道的服务列表,并提供添加到此列表的方法。完整服务提供商可能包括以下内容。
private readonly Dictionary<Type,object> mServices = new Dictionary<Type,object>();
public void Add(Type type,object value) { ...}
public object GetService(Type serviceType) { ... }
public void Remove(Type type) { ... }
public T Resolve<T>() { return (T)this.GetService(typeof(T)); }
如果你把它变成一个单独的类,那么你可以在任何地方使用这个服务。
接下来的工作是创建服务。在您的新框架中,DLL将创建一个类似IMessageBoxService的接口,其中包含一些您可能希望调用以向用户触发消息的方法。您可以通过以下功能开始简单,稍后再添加。
void ShowError(string message);
然后,您需要在FL dll中实现此接口。此实现将对MessageBox进行实际调用。
下一步是向服务提供商注册新的服务实施。因此,在GUI打开之前的某个地方调用ServiceProvider.add(typeof(IMessageBoxService), new MyMessageBoxService());
现在,在您的业务逻辑中,每次需要弹出消息时,您都可以向服务提供商询问消息框服务,并在IMessageBoxService中调用所需的方法。像这样......
IMessageBoxService wMess = ServiceProvider.GetInstance().Resolve<IMessageBoxService>();
wMess.ShowError("My Error Message");
现在你有一个干净漂亮的MVC兼容设计,用于弹出错误消息而不会产生任何令人讨厌的循环依赖。
如果您想了解有关MVVM的更多信息以及它的全部内容,可以查看Microsoft链接。或者有一个完整的MVVM框架,有人为WPF创建了具有许多有趣功能的麻烦。如果你有时间,你可能也想看一下。
答案 5 :(得分:0)
如果将两个图层实现为单独的项目(导致循环依赖),则无法让两个图层彼此了解。
一种方法是使用观察者模式。在业务类中设置事件,并让表示层订阅这些事件。当业务层完成其操作时,它可以触发事件并将结果传递回视图。业务类不需要知道表示层,它只知道有订阅它的对象,并避免创建循环依赖。
更新:这是BL使用事件调用FL的一种方法。这是一种循环逻辑,但如果你已经围绕两层了解彼此的代码,那么你可以考虑这个。
class BL
{
public void CallFromFL (int parameter)
{
int result = DoSomeWork (parameter);
if (OnComplete != null)
OnComplete (result);
}
public event Action <int> OnComplete;
}
class FL
{
void Foo ()
{
BL bl = new BL ();
bl.OnComplete += this.getResult;
bl.CallFromFL (5);
}
void GetResult (int result) {...}
}
答案 6 :(得分:0)
为什么BL需要了解FL?真正的BL与FL无关。目前使用WindowsForms实现FL,如果稍后需要将FL更改为WPF,则BL也需要更改。那么如果一层中的变化也影响另一层,那么分层是什么? BL的想法是它独立于表示层,但可以由不同的FL使用。 BL不应做出任何假设或需要了解FL。