|
Lowest of the Low wrote: MVC is not currently an option for us (new technology...risk...etc, etc)
Well, I was not talking about the ASP.NET MVC framework. I was talking about the MVC concept.
Lowest of the Low wrote: I still have other questions like where is validation done?
For trivial validations, view is the appropriate place. But if you have very complex business validations, you probably need put it outside of the view.
Lowest of the Low wrote: And in relation to your comment on injecting state via interface I'm being a bit thick today but how?
You define an interface with the necessary properties and methods. Implement this interface in the same project where you have access to session. Then inject this interface instance to model(or whatever area). Read about dependency injection[^].
|
|
|
|
|
Query for you on the Dependency injection, does that not violate the OOP "Dependency Inversion Principle"?
|
|
|
|
|
The general goal behind MVP is to isolate as much logic from the "view" as possible, just like with MVC. The benefits of MVP over MVC are primarily realized when a complete rewrite of an existing WebForms app into an MVC framework can't be done (which sounds like your situation).
I've implemented MVP a couple times for existing ASP.NET WebForms sites, and its been quite successful. In terms of managing state, etc. There are a lot of different kinds of state, and it depends on what state your talking about. ASP.NET WebForms state should probably be keept in the same locations it would have been keept before for basic stuff. If you had a ViewState property on a Page that held a CustomerID, for example, you should probably keep it in the Page ViewState.
As for logic...put as much as humanly possible in the Presenter. The presenter is like the controller from MVC, and one of the main reasons for having it is to allow you to maintain SoC, separation of concerns. A view is just a representation of data, and it really shouldn't contain logic. In the case of WebForms, it will probably need to contain a little bit to handler data set on it by the presenter and render it, but keep it minimal. The presenter should handle all the logic of processing your page, it should contain event handlers to handle events on the view (including events inherited from Page and Control), it should validate input, and decide when to enable/disable controls.
The view should be a standard WebForms .aspx page with codebehind. However, in addition, you will need to create and implement an interface that represents your view. This interface is the access point for the presenter. The proper form of either Passive View or Supervising Controller MVP is that the presenter does not maintain any direct references to the view, only to an interface that represents the view. This allows the presenter to be used for multiple similar views. The view itself should handle the process of creating the presenter, and passing itself to the presenter (thats how the presenter gets a reference to the views interface). This view interface should contain all properties and events on the view, as well as exposing common stuff from the Page class as well, such as the standard page events (OnInit, OnLoad, OnPreRender, etc.)
If you need any more help, I'd be glad to offer it. I have a small MVP framework that I've written that is very lightweight and easy to integrate into existing apps a view at a time, with little configuration. I have considered writing an article about it for a while, maybe the time is right. Most people don't fully realize the benefit of MVP for ASP.NET when you don't have the option of rewriting the whole site as an MVC app (using any MVC framework). MVP is a great way to improve SoC and increase unit testability without requiring a massive rewrite of an existing application.
|
|
|
|
|
You've hit my situation on the head, said I was being a bit thick earlier today couldn't even explain the situation I'm in very well.
In work we have moved to an agile way of working involving a lot of TDD and that is one of the main drivers for looking to try and move to a MVP style of development.
The state issue is a concern because we do use session for storing some info and currently we have to reference System.Web in our existing equivilant of the model and this is one of the main issues I need to try and overcome before being able to implement MVP.
Any thoughts you've got would be appreciated.
|
|
|
|
|
I would be glad to help. There is a lot of rave these days about MVC, partially because of greater TDD movements, partly because of ASP.NET MVC. MVC is definitely nice, but its not the solution to every presentation problem.
I would like to get a bit more information about your exact scenario. From a rigid design perspective, your Model should be abstracted away from the views as much as possible, completely isolated even. The "bridge" between view and model is the presenter/controller. It depends on the scale of the application, but model could actually be a full Domain Model. Its also possible that you have a domain, but it is isolated by a set of services. Sometimes people share their domain accross the wire from services, but going back to the core principals of programming...Separation of Concerns, Single Responsibility, Isolation, etc. it might be best to have a presentation-specific model that you create from your domain or whatever your services return to give you an added degree of separation and testability.
Anyway...if you can give me a concrete example (you can keep it simple) of how your view, presenter, and model are interacting now (or would), and note where your storing state or having state issues, I'll see what I can do to help you organize it better. Sometimes its just a matter of the wrong class doing something useful...and splitting the class in two and putting the other part in a different namespace/project solves the problem.
|
|
|
|
|
ok here's the details:
The current application effecively has View and model but no presenter but the app does generally follow good OOP with SoC, Single Responsibility, Create don't use etc.
All business logic and db access is handled in the model which is in a seperate assembly and has been created as a domain model.
A fair amount of model information is accessed through properties which store their data in session (the model is closer to a desktop type app than a web app).
The code behind's of the pages mostly data bind to the properties exposed by the model although there is a lot of logic around controlling the UI (including a hack of the treeview to add a link to child nodes). Whilst ViewState is not disabled it is generally not used for storing of any particular stateful information.
One reason for the need to store the stateful information is that the application is generally driven by meta data so that when different clients access the application the app points to a different database and calls different stored procedures when working the data.
We use code from Phil Haack for simulating HttpSession to allow us to perform unit tests on the model, and we have fairly good coverage over the classes.
The application works well enough but it seems a good candidate for refactoring as its fairly simple but meaty enough to present a challenge.
|
|
|
|
|
Ok, before I dive into anything else...I think a critical distinction is that Model is most definitely not state. In your case...your model is storing both business data and state. Thats a blending of concerns, and a fairly critical one at that. Its very important to maintain a separation between business modeling facilities, and user state facilities. User state is transient, it lasts for the duration of the users visit and then dissapears. Model state is, generally, persistant...regardless of where it is stored. The purpose of state data and model data is also generally very different. You have connected two concerns into your model objects that, before anything else, should be separated into isolated facilities that serve just one function. So you need one system to manage state, and another to manage business. Management, or orchestration, of the two...state and business data, would then be done by a third facility...quite possibly the presenters, or maybe another system that the presenters use. But I would say its very, very important to separate state from your model...otherwise I am not sure that a viable implementation of an MVP can really be achieved.
A good way to put it is that state is part of context. Context always exists, sometimes its not very obvious. Context also exists at various levels...application, thread, call, etc. Another important aspect about context is that it is a cross-cutting concern...it spans layers and levels of code. This should clarify why state (context) doesn't belong in a model object.
If you can separate these concerns properly, you should be able to avoid even having to simulate HttpSessionState for unit testing, as you should be able to create your own custom session object that would in turn delegate to either HttpSessionState (for an actual representation), or possibly just a dictionary or hashtable (for a mock representation).
Is there any way you can show an example of one of your model objects? I may be able to help you separate state out of them, but still allow you to achieve the kind of muti-tennancy you need (if you don't know this term, it pretty much referrs to the way you store data in different databases for different customers).
|
|
|
|
|
ok I understand the problem. Here's a small representation of the objects:
AuthorisationLogic (used for authorising work in the model)
- Property UnreleasedSummary (bound by page)
- Method: GetUnreleasedSummary (called by page and results stored in property).
The actual data returned is an object graph which consists of:
WorkItem (a container for work to authorise for a user) this in turn contains a List<WorkItemData> .
A WorkItemData object has details about the actual work that has been done and needs to be authorised and also contains a property List<Commands> with each command holding the name of the specific stored procedure to call to authorise an individual WorkItemData instance. A WorkItemData can also hold the actual data that is being authorised for the user to review in another List and this will be bound to in the View.
Thats a one of the simple interactions hope I've put in enough details. I can tell you that the original reason that for a lot of data being stored in session was so that additional calls to the database could be avoided.
|
|
|
|
|
Yeah, that helps to clarify things a bit. I would say that your not really storing user state or dealing with context at all in that situation. What you are doing is caching. I still think that storing your state in the session directly is creating an undesired link between your model and the system and frameworks that display data from the model. The simplest solution is to abstract the caching a bit by creating a simple, mockable caching system:
interface ICache
{
void Add(string key, object toCache);
object Get(string key);
void Remove(string key);
}
class SessionCache: ICache
{
private HttpSessionState Session
{
get
{
if (HttpContext.Current != null)
HttpSessionState.Current.Session;
return null;
}
}
private bool IsSessionAvailable
{
get { return HttpContext.Current != null; }
}
public void Add(string key, object toCache)
{
if (!IsSessionAvailable) throw new InvalidOperationException("Session cache is only available within an HttpContext.");
Session[key] = toCache;
}
public object Get(string key)
{
if (!IsSessionAvailable) throw new InvalidOperationException("Session cache is only available within an HttpContext.");
object item = Session[key];
return item;
}
public void Remove(string key)
{
if (!IsSessionAvailable) throw new InvalidOperationException("Session cache is only available within an HttpContext.");
Session.Remove(key);
}
}
class DictionaryCache: ICache
{
private Dictionary<string,> m_cache;
private Dictionary<string,> Cache
{
get
{
if (m_cache == null)
{
Interlocked.CompareExchange(ref m_cache, new Dictionary<string,>(), null);
}
return m_cache;
}
}
public void Add(string key, object toCache)
{
Cache.Add(key, toCache);
}
public object Get(string key)
{
object item = Cache[key];
return item;
}
public void Remove(string key)
{
Cache.Remove(key);
}
}
static class CacheManager
{
private static ICache s_cache;
public static ICache CacheInstance
{
private get { return s_cache; }
set { s_cache = value; }
}
public void Add(string key, object toCache)
{
CacheInstance.Add(key, toCache);
}
public object Get(string key)
{
return CacheInstance.Get(key);
}
public void Remove(string key)
{
CacheInstance.Remove(key);
}
}
public class Global: HttpApplication
{
public void Application_Start(object sender, EventArgs e)
{
ICache cache = new SessionCache();
CacheManager.CacheInstance = cache;
}
}
public class ModelEntity
{
public object LazyLoadedObject
{
object item = CacheManager.Get("LazyLoadedObject");
if (item == null)
{
item = GetLazyLoadedObject();
CacheManager.Add("LazyLoadedObject", item);
}
return item;
}
private object GetLazyLoadedObject()
{
}
}
[TestClass]
public class ModelEntityTests
{
public void LazyLoadedObject_Get_AlreadyCached()
{
ICache cache = new DictionaryCache();
CacheManager.CurrentCache = cache;
object originalInstance = new object();
cache.Add("LazyLoadedObject", originalInstance);
ModelEntity entity = new ModelEntity();
object cachedInstance = entity.LazyLoadedObject;
Assert.AreEqual(originalInstance, cachedInstance);
}
}
I am not sure the model entity and test represent exactly what your doing, but it should demonstrate the need to abstract where you store cached data from the activities of caching itself. If you do something like the example above, your model stuff is now bound to ICache and CacheManager...and the link to ASP.NET is gone...so your model should be more testable in isolation from the environment(s) it is used in/bound to. The example above isn't really ideal, either...I would say its probably better to cache at a lower level than the model objects themselves. I think there are some more blending of concerns in your system...I can't say exactly as I don't know enough about it. The list of Commands should probably be separated from the model objects as well: WorkItem and WorkItemData are entities...however, Commands is a persistance utility, and probably belongs in a separate facility that handles the persistence and retrieval of your entities separately. Thats really a different discussion though...even the discussion above isn't directly tied to MVP, although it does provide a useful abstraction that lets you keep ASP.NET elements out of your model objects, and makes it easier to put your Presenters in a separate library from your views and models in a separate library from both.
|
|
|
|
|
Jon, thanks for all the help, I had one of those moments where I thought I knew what I was doing and in a few simple sentances I realise how wrong I was.
I would like to continue the 'conversation' about this but not sure the forum is the best place for this.
Thanks again for all the help.
|
|
|
|
|
Hi Nathan,
I would love to continue the conversation as well. Perhapse IM would be best...a real-time forum might be best suited to helping you improve your architecture. I'll email you my contact info.
|
|
|
|
|
That would be greatly appreciated Jon.
'bout to pack it in this evening but sure can arrange time when both of us are around
|
|
|
|
|
Hi Jon,
I tried to email you via the link but don't know if its working. Hoping you'll get notified of this as could do with discussing the architecture.
You can get me at nathans.FICTICIOUSVALUE.dropbox@NONSENSEVALUE.googlemail.RANDOMVALUE.com
I'll leave you to sort out the email address
|
|
|
|
|
Hi all,
I have used this code for sending mail using Outlook but its giving access denied problem
<%
function SendMessages()
' Dim MyDB As Database
'Dim MyRS As Recordset
'Dim objOutlook As Outlook.Application
set objOutlook = CreateObject("Outlook.Application")
set objOutlookMsg = CreateObject("Outlook.MailItem")
set objOutlookRecip = CreateObject("Outlook.Recipient")
set objOutlookAttach = CreateObject("Outlook.Attachment")
Dim TheAddress
Dim ThePhone
Dim SendEmail
Dim SendPhone
Set MyDB = CurrentDb
Set MyRS = MyDB.OpenRecordset("tblMailingList")
MyRS.MoveFirst
' Create the Outlook session.
'Set objOutlook = CreateObject("Outlook.Application")
end function
%>
|
|
|
|
|
Because your asp.net dont have permission to acces the Outlook Application.
for that in "Run" type dcomcnfg and press enter.
now go to Component Services -> Computer -> My Computer -> DCOM Config.
then find your "Microsoft Outlook Application".
righ click and select "Property".
go to Security -> Access Permission -> Customize -> Edit.
Now add "Everyone" and Allow both the options ticked.
Note : You can search on internet for specific accounts instead of giving access to everyone.
I think you can try just "ASPNET", But I am not sure.
And why you want to send mail using Outlook. you can use inbuilt classes in .net framework. that is much easier and less resource hungry. MS Office integration will eat up your resource. also there is very difficult to distroy the object. After running this if you see your Taskbar. you will see many running instance of Outlook.exe. that you cannot stop from your web application. I have used Excel so I know this problem.
|
|
|
|
|
Why would you need to use outlook on the server ? You realise this won't use outlook on your users machine, right ?
Christian Graus
Driven to the arms of OSX by Vista.
|
|
|
|
|
Hi,
Now i am not using outlook for sending mail,i m sending mail through .net classes,but thanx for giving feedback for my question
N!dh!
|
|
|
|
|
hi,
how to render a gridview from 2 database tables,all the columns from 1 table and only 1 from another table?I am using MS SQL and c# language.
Regards,
Bill
|
|
|
|
|
billcodes wrote: how to render a gridview from 2 database tables,all the columns from 1 table and only 1 from another table?
Then why you are not joining those Two table ?
|
|
|
|
|
i have added column of checkbox in a gridview..now i want to delete rows for which checkbox is checked..how can it be done?? i am doing it in asp.net(C#)
|
|
|
|
|
|
i am displaying all users name on my page and on mouseover i have to show
some datail information of that user in hover
i have done this for 1 user
<cc1:HoverMenuExtender ID="HoverMenuExtender2" runat="server" TargetControlID="LinkButton2" PopupControlID="PanelPopUp1" PopupPosition="Right" PopDelay="100" HoverCssClass="popupHover">
</cc1:HoverMenuExtender>
<asp:Panel ID="PanelPopUp1" runat="server" Width="95px" CssClass="popupMenu" BorderColor="Transparent">
<asp:GridView ID="gvPopup1" runat="server" AutoGenerateColumns="False" GridLines="None" DataSourceID="SqlDataSource2" OnSelectedIndexChanged="gvPopup1_SelectedIndexChanged" Width="194px" DataKeyNames="TaskId">
<Columns>
<asp:ButtonField CommandName="Select" Text="Select" Visible="False" />
<asp:BoundField DataField="TaskId" HeaderText="TaskId" SortExpression="TaskId" Visible="False" />
<asp:TemplateField>
<ItemTemplate>
<table>
<tr>
<td>
<asp:LinkButton ID="LinkbtnTask" ForeColor="#808080" runat="server" OnClick="LinkbtnTask_Click" CommandName="Select"><%# Eval("Task") %></asp:LinkButton>
<%--<asp:Label ID="lblTask" runat="server" Text='<%# Eval("Task") %>' Width="141px"></asp:Label>--%>
</td>
<td style="width:10px;">
</td>
<td>
<asp:Label ID="lblStatus" runat="server" Text='<%# Eval("Remark") %>'></asp:Label>
</td>
</tr>
</table>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:WebSiteConnectionString %>"
SelectCommand="DailyReport" SelectCommandType="StoredProcedure" ProviderName="<%$ ConnectionStrings:WebSiteConnectionString.ProviderName %>">
<SelectParameters>
<asp:Parameter DefaultValue="TS" Name="oprtype" Type="String" />
<asp:Parameter DefaultValue="3" Name="LoginId" Type="Int32" />
</SelectParameters>
</asp:SqlDataSource>
</asp:Panel>
<asp:LinkButton ID="LinkButton2" runat="server" Visible="false" ForeColor="ControlDarkDark" Font-Bold="True">Alien </asp:LinkButton>
but i want to do it dynamically for all users
is it possible
with regards
rahul
|
|
|
|
|
I don't see the issue. Take the code that defines the one user you did it for, and then generate code that works for all users.
Christian Graus
Driven to the arms of OSX by Vista.
|
|
|
|
|
Hi experts,
Is there a possible way to set filter options like in .net ".rpt" or any file extension?
Thanks in Advance ^^
|
|
|
|
|
There is no way to set the filter when selecting file. You can check the file extension on server side and inform about unsupported file types.
|
|
|
|
|