|
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.
|
|
|
|
|
What is your preferred implementation for AJAX modal popups?
I think that the Netflix implementation is pretty standard.
Does .NET provide a good AJAX popup implementation out of the box or do you tend to use third party controls for this instead?
|
|
|
|
|
|
I'm using ASP.NET 2.0, Visual Studio 2005, AJAX, C#.
I have a search results page that lists out products that a user is searching for inside the Repeater control. I have an image inside every product that when a user clicks on it I need to query the database to get the data. All the samples I have found show code with the panel already populated with data. I want to do it on demand. I've attempted to use the DropDownExtender, HoverMenuExtender and PopupControlExtender with no luck.
|
|
|
|
|
Hi,
I have this popup page where i am doing this in the button click:
Response.Write("<script language='javascript'>function changeParent(){window.opener.document.getElementById('ctl00$ContentPlaceHolder1$txtChange').value='Change';}window.close();</script>");
In the parent form I have a hidden textbox set to autopostback=true and in the textchanged event handler, I am doing response.redirect(url).
In the script above(in the popup), I am changing the value of the textbox located in the parent page.
So, I was expecting it to work, but its not.
Any idea?
Thanks.
|
|
|
|