|
|
It hasn't been, due to other month-end pressures at work, but I've just found a wikipedia article on the algorithm used by diff. It's based on the Longest Common Sequence problem, but it's now 03:40, and it's a little difficult to understand.
|
|
|
|
|
Have you considered starting with an Open Source diff utility [like WinMerge - www.winmerge.org] or the sample diff tool on this site [www.codeproject.com/tools/difftool.asp]?
|
|
|
|
|
egottwald wrote: www.winmerge.org
Yes, I have, but I think the Perl utility I've just found, csvdiff[^], will get me further, and I stand to learn some Perl if I need to tweak it.
|
|
|
|
|
Hi!
I wasn't sure whether to post this in ASP.NET or Design and Architecture forum, but decided to land it here after all as I believe the same would apply to a WinForms app or even a web service.
I am working on an ASP.NET 2.0 project, this is my first attempt at a real multi tier architecture, before I was just binding a DataSet to my WebForms directly. I have a DAL, a BLL and an UI. All is fine, they work well together, but I came to the point where I need to display some reports in a GridView and my grasp of business layer started to fall apart.
Look, the project is about embeddable web widgets, every widget belongs to some user. I have (among others) some classes in BLL (they communicate with DAL for CRUD and other database operations, BLL does not know anything about the database):
1. WidgetsBLL - this class 'manages' Widgets. You can create new widgets here, retrieve all widgets, retrieve widgets belonging to a particular user, etc.
2. Widget - this class represents a particular widget. It knows the widget's URL, clickthru count, theme and so on.
3. UsersBLL - same as WidgetsBLL, just deals with Users.
4. User - represents a user, stores data such as first & last name, e-mail address, etc.
It all worked great so far, but now I came to the point where I must display combined data in a GridView. I need data from Widget class (such as widget's ID, name and clickthru count) as well as owner's data from User class (first & last name, e-mail). Now, this data cannot be retrieved from a single class as you can see. I need to get a Widget instance, then get a User instance and display a list of rows which are built from those two classes, so the GridView needs columns such as:
Widget.WidgetId | Widget.WidgetName | User.LastName | User.Email
I am not sure what one should do to solve such problem, I don't have experience with this kind of architecture. I might just create a new class (WidgetStats?) - this seems like the easiest approach, but somehow it looks like bad design to me, because widget statistics is not a real object - it looks kind of artificial, doesn't it?
What is the best way to return an object composed of other objects? I began by calling WidgetsBLL.GetWidgets() and binding the result to the GridView. Then I can have TemplateFields and retrieve for example user's e-mail. But what about sorting in this scenario?!
Please, help. I have been trying to work this out for two days.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
Pawel Krakowiak wrote: but now I came to the point where I must display combined data in a GridView. I need data from Widget class
If I understand your post correctly a "widget" is a presentation object or "view" object, therefore other views are never going to "get data" from it. The GridView is another presentation object so it would get all it's data through the back end services ( business, session, database ).
Pawel Krakowiak wrote: this is my first attempt at a real multi tier architecture
Pawel Krakowiak wrote: I don't have experience with this kind of architecture.
That's not architecture it's software design. You are designing your business logic and views that all execute in the middle tier. You are not creating tiers you are designing.
|
|
|
|
|
led mike wrote: If I understand your post correctly a "widget" is a presentation object or "view" object
Not exactly, it is a domain object which represents data from the database and allows the user to modify that data - such as giving it a name, assigning a URL to it, choosing a theme, etc. The user control which renders this is another subject.
I need to display GridView rows composed of some fields from Widget class and some fields from User (widget's owner) class. I am wondering whether I shouldn't just create a WidgetStatistics class which would have 1 property of type Widget and 1 property of type User and just bind my grid to that. I don't know if it's a good idea, definitely easier.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
Pawel Krakowiak wrote: and allows the user to modify that data
If the user interacts with it, it is a presentation object. I am far to confused by your posts to help.
|
|
|
|
|
led mike wrote: If the user interacts with it, it is a presentation object.
Class user, not application user. I guess I can't express my problem better.
How it works in code is like this:
WidgetsBLL widgetsLogic = new WidgetsBLL();
Widget widget = widgetsLogic.GetWidgetById(13);
Now you can access various fields of the widget:
widget.WidgetId
widget.FriendlyName
And so on. You also have a User class, which has properties such as FirstName, LastName, etc.
I need to present combined data, so in one row we have WidgetId but also user's FirstName - as you can see this data belongs to two different classes. I am not sure whether to design a third class and include both Widget and User by composition or to do something else. That is my problem I tried to describe.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
Pawel Krakowiak wrote: include both Widget and User by composition or to do something else
Composition is reasonable.
You are asking an Object Oriented Analysis and Design question. Without seeing your analysis and existing design it's difficult to offer any meaningful advice but under the heading of General points:
Are you using interfaces?
and...
Pawel Krakowiak wrote: Now you can access various fields of the widget:
widget.WidgetId
widget.FriendlyName
See this article[^]
|
|
|
|
|
It seems to me you should be binding your datagrid to
Widget.WidgetId | Widget.WidgetName | Widget.Owner.LastName | Widget.Owner.Email
This is a fairly important distinction - the example you gave doesnt appear to take advantage of the relationship between Widgets and Users. You might not want your BLL to know about the exact FK relationship in the database, but the fact that Widgets have an Owner is part of your domain, and there is no escaping that.
How you implement these properties on the Widget class is up to you.
You might also want to consider using a tool that handles the database mapping for you. My company (linked in my signature) provides such a product, which is free for non-commercial use.
|
|
|
|
|
Mark Churchill wrote: It seems to me you should be binding your datagrid to
Widget.WidgetId | Widget.WidgetName | Widget.Owner.LastName | Widget.Owner.Email
Yeah, I redesigned the application, so now my business objects make more sense.
I could write a SQL query (stored proc) which would return data from joined tables, then create another Table Adapter and BO. But this doesn't look good to me in terms of design. First rule is you should not update the business layer to meet the UI requirements, it should be exactly the opposite. And that's why I don't like the idea of creating a new BO just for displaying statistics in a GridView in the presentation layer.
My problem (at least a similar one) was already discussed at ASP.NET Forums[^].
Thanks.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
Pawel Krakowiak wrote: First rule is you should not update the business layer to meet the UI requirements
I never heard that rule, can you provide a link to your source?
|
|
|
|
|
led mike wrote: I never heard that rule, can you provide a link to your source?
It's called common sense. The whole idea behind decoupled modules is to make them pluggable, so you can throw out your ASP.NET UI and plug in WinForms and it will still work, because the business layer stays the same. If you start adding UI related stuff to your BOs you are coupling those two layers again. In the end when you want to switch the UI you have to rewrite the BOs or end up with BOs hanging in the air (not used anymore) and writing new ones.
I guess the size of your project is important and its particular requirements, right? Follow the ASP.NET Forums discussion if you want, I am just agreeing with some opinions there.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
Pawel Krakowiak wrote: It's called common sense.
I see you are joking but seriously I have my own Number One Rule: Don't make stuff up.
What I mean by that is that so much information is available now there is no need to in 99.9% of the work I do.
Pawel Krakowiak wrote: and its particular requirements, right?
Yes requirements, and analysis, are the key in your problem. What I mean is, if you have discovered a missing requirement or incomplete analysis then of course it might require a change in any layer(s) or part(s) of your system or even design dependencies forcing a refactoring to alter the design.
Pawel Krakowiak wrote: but now I came to the point where I must display combined data in a GridView
Certainly that could have been anticipated but that is what iterative development is about. Understanding that we are not always able to have all the requirements before we start designing or that our analysis might be flawed or have holes. When these issues occur what we hope to find is that our design supports adding the new feature but even that is not always possible.
|
|
|
|
|
led mike wrote: I see you are joking but seriously I have my own Number One Rule: Don't make stuff up.
I shouldn't have said 'rule', rather 'suggestion' or 'good practice'. Of course there are no rules which say you can't include UI stuff in your business layer.
led mike wrote:
Certainly that could have been anticipated but that is what iterative development is about.
In this particular project the statistics were to be supported by an external system, but we had to put the website in an IFrame (which wasn't planned) and the external system doesn't work that way, so I had to write my own statistics "engine" and now I need to include a couple of stats pages. But this doesn't mean I should be adding Business Objects which represent "a statistic", which really consists of a few fields taken from two objects which already exists - GUI should do the job - it already can get the objects and should take care of combining and displaying them. I was looking for somebody to tell me maybe that creating a new BO just for this purpose was OK, even though I didn't like it, hence my original question.
Anyway, as you originally said, this would be difficult without showing my classes. I asked a theoretical question.
Oh, by the way - regarding the 'accessors are evil' article - you must have public properties in order to bind ASP.NET controls to data. .NET Framework itself is full of properties, sometimes they are more readable in code and easier to use, but as someone (maybe it was in that article?) said - working for Sun or Microsoft doesn't magically improve your skills.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
I don't totally agree here. Implementation aside, if you were to draw me an ERD then you would agree that Widgets have an Owner of type Person. A person owns none or more widgets. Regardless of whether you have a ASP.Net/Webforms or command line, this relationship exists.
You business layer should also have Widget.Owner, and likely Person.Widgets[]. Sure, your database reflects this as a foreign key. Thats fine. Call it a coincidence. Your BL is based off the ERD, your database is also based off the ERD.
They do both represent the same things, expect them to be similar.
|
|
|
|
|
Mark Churchill wrote: You business layer should also have Widget.Owner, and likely Person.Widgets[]. Sure, your database reflects this as a foreign key. Thats fine. Call it a coincidence. Your BL is based off the ERD, your database is also based off the ERD.
Mark, but this leads to a circular dependency, i.e. a Person has Widgets collection, each widget has an Owner, which is a Person, which has a Widgets collection, each widget has an Owner, which is a Person, which has a Widgets collection, each widget has an Owner...
You know what I mean?
Currently my idea is to have a User class, which contains WidgetList (inherits from List[Widget]), but Widget only has UserId as its owner, not the whole object. If you need the owner, you can retrieve it from UserManager (another class which manages objects of type User). This, of course, means that ASP.NET databinding is not so easy, because I need to add some TemplateFields and fill them in in GridView event handlers.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
That circular dependancy seems to be correct behaviour to me. What is important is that the User and the Widget are the same instance every time around.
I would expect "somePerson == somePerson.Widgets[0].Owner".
This is similar to say a heirachy of Controls on a page. "someThing.Controls[0].Parent" could have you going in circles in a similar manner.
Diamond Binding will give you a Owner property on the Widget class of type Person, and an IList<widget> on the Person class. The actual implementation of IList depends on whether you have picked lazy-loading. You could also configure an IDictionary<x,widget> where x is a property of Widget that is unique per Person.
Think of it this way: Is it incorrect of me to ask "Who is your father's son's father's son's father?". It might be inefficient of me to ask that, but the query has an answer in your problem domain.
|
|
|
|
|
So, you suggest to have an accessor like this:
<code>private int _ownerId;
public User Owner
{
get
{
return UserManager.GetUserById(_ownerId);
}
set
{
_ownerId = ((User)value).UserId;
}
}</code>
User = Person, I currently have that class named User, although it conflicts with a class from .NET Framework sometimes, so renaming it to Person is a good idea.
This is a simplified example, but something like the above? This would allow to bind easily the property in ASP.NET, like "User.Owner.FullName", but still wouldn't endlessly load objects, right?
BTW. Is DiamondBinding an ORM? I have currently a license for EntitySpaces[^], but am not using it at the moment. The way how ORMs present the objects looks like more fitting for the DAL rather than BLL... But this is an another discussion.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
Yep that seems ok. If the UserManager has a caching strategy of some sort then you won't have any problems with circular references giving you duplicate objects either. (Although theres a redundant cast in that setter ).
You could also keep a local cache of the child object - this would be more efficient when pulling out say User.Owner.FirstName + User.Owner.LastName - it would avoid two trips to the UserManager. Of course with any caching you have stale-cache issues to solve
As for lists, you can have your Widget collection implement IList<widget>, but not actually retrieving the objects until iterated or an item requested (lazy loading again).
Diamond Binding is an ORM and definition generator. It follows the ActiveRecord model, so its less data-model oriented than other ORMs - so you are mainly dealing with objects at a business level: Customer c = Customer.Find(primaryKey); IList/Customer/ l = Customer.FindAll(); (Edit: Forum broke my generics!) Theres an example on the product page here[^]. Give the Personal Edition a try if you like, theres no functional difference between it and the Professional / Enterprise Editions. We aim to handle the entire data access requirements and just let you build business logic on top of the model we give you.
|
|
|
|
|
Mark Churchill wrote: If the UserManager has a caching strategy of some sort then you won't have any problems with circular references giving you duplicate objects either.
I am not sure how to do this yet, can you point me to some articles?
Mark Churchill wrote: Although theres a redundant cast in that setter
Yes. I thought value was of type object, forum doesn't give me any guidelines.
Thanks for all your comments so far.
Kind regards,
Pawel Krakowiak
|
|
|
|
|
A really simplistic implementation would be to add the caching to your UserManager.Load(object pk). Check the cache, and if its in the cache then return it. Otherwise load the object's data and add it to the cache.
You can use a Dictionary for your cache for prototyping. ASP.Net provides a cache (I think you can safely use this outside of the web framework too) that you can add objects to with a key and an expiry.
You might want to add some way of forcing a reload of an object as well. To be honest though if you are writing your own data layer you are going to have to draw the line for features somewhere. I haven't done a KLOC count on Diamond Binding for a while - but I'm guessing its approaching the millions, and thats not including NHibernate
|
|
|
|
|
Slightly OT... But I was wondering, how does Diamon Binding differ/compare to the ADO.Net Entity Framework coming up in VS2008?
Me: Can you see the "up" arrow?
User:Errr...ummm....no.
Me: Can you see an arrow that points upwards?
User: Oh yes, I see it now!
-Excerpt from a support call taken by me, 08/31/2007
|
|
|
|
|
I guess the cheeky reply is that Diamond Binding is avaliable now, and is mature, and leveraging Hibernate, which is also decades mature. Last I heard the Entity Framework was dropped from Orcas and was going to be released "out-of-band" sometime in the first half of 2008.
EF, like a lot of ORM products tends to be very heavyweight and favours model-first development. It is a very complex product, however I think the key feature that everyone is interested in is LINQ to Entities. LINQ is not really EF specific. After the product becomes more mature, we will look at including LINQ support in Diamond Binding. Although we find that Expressions and HQL support cover most use cases sufficiently at the moment.
DB is designed with simplicity in mind, and is geared towards keeping your business objects synchronised with the database schema, and handling everything in between for you. If the schema changes, its a one-click synchronisation. One aim is to stop developers getting bogged down in mapping, and just let them add business value to the application!
|
|
|
|
|