i need some suggestion for working on 3 tier architecture, recently i completed one of my clients site, with 3 tier architecture, in that i was transforming the data directly in html from sql server 2008 and fetching the records in .net, .net was just doing the rendering task, so one of my friend said, it could reduce the performance drastically, but i never got any complaint from client regarding the same, as the records i am transforming in html from sql server 2008, is not more than max 10, and now for the second site, i want to take more precaution while rendering data on the site, now this time i have planned to again use 3 tier, but now instead of transforming data to html in sql server i am pulling the records from sql server 2008, to my datalogic layer, and in bussinesslogic layer, i am transforming it to HTML.. so can anyone suggest which is the best way to go about, the previous tranforming in HTML in SQL SERVER or in .NET
By Transforming in HTML i mean:
ALTER PROCEDURE [dbo].[SP_TVT_Article_Get]
#ID INT
AS
BEGIN
DECLARE #HTML NVARCHAR(MAX)
SET #HTML = '';
SELECT #HTML = '
<div style="margin-top:10px;" class="craig"><span style='+(SELECT CASE ISNULL(Author, '') WHEN '' THEN '"display:none; float:left"' ELSE '"display:block; float:left"' END)+'> ' + ISNULL(C.Author, '' )+ ' /</span> ' + DATENAME(DW, C.PublishDate) + ' ' + CONVERT(VARCHAR(15), CAST(C.PublishDate AS TIME), 100) +'</div>
<div class="sony_title">'+C.Title+'</div>' +
CASE
WHEN C.SecondaryTitle IS NOT NULL THEN
'<div class="tokiyo">' + C.SecondaryTitle + '</div>
'ELSE
'<div class="tokiyo">TOKYO: Six broadcasters & theaters in at least 22 will carry 3D coverage.</div>'
END +
'<div class="imgtxt_para01"> <img src="'+isnull(C.Thumbnail,'')+'" style="'+(SELECT CASE ISNULL(C.Thumbnail,'') WHEN '' THEN '"display:none"' ELSE '"display:block"' END)+'" />
'+ CAST(C.Article AS NVARCHAR(MAX)) +'</div>'
FROM CrossArticle_Article C
WHERE Id = #ID
SELECT #HTML
END
and this time which i am planning to do is like this..
My DataLogic Layer, will look like this:
public void GetArticle(int PortalID, int ArticleID, out DateTime PublishDate, out string Article, out string Author, out string Title)
{
object _getArticle = "";
Article = Author = Title = null;
PublishDate = DateTime.Now;
using (SqlCommand comGetArticle = new SqlCommand("sp_EQEM_GetArticle", ConnectDatabase))
{
comGetArticle.CommandType = CommandType.StoredProcedure;
comGetArticle.Parameters.AddRange(new SqlParameter[]{new SqlParameter("#PortalID", PortalID),
new SqlParameter("#ArticleID", ArticleID)});
ConnectDatabase.Open();
using (SqlDataReader reader = comGetArticle.ExecuteReader())
{
while (reader.Read())
{
Title = reader.GetValue(0).ToString();
Author = reader.GetValue(1).ToString();
PublishDate = Convert.ToDateTime(reader.GetValue(2));
Article = reader.GetValue(3).ToString();
}
}
ConnectDatabase.Close();
}
}
and bussinesslogic layer will look like this:
public object GetArticle()
{
string Title, Author, Article;
DateTime PublishDate;
connection.GetArticle(PortalID, ArticleID, out PublishDate, out Article, out Author, out Title);
StringBuilder str = new StringBuilder();
str.Append("<div class='aricle_para'>");
str.Append("<h1>" + Title + "</h1>");
str.Append("<span style='float:left; display:" + (String.IsNullOrEmpty(Author) ? "none" : "block") + "'>By " + Author + " | </span><span> " + PublishDate.DayOfWeek.ToString().Substring(0, 3) + ", " + PublishDate.ToString("dd MMM yyyy") + "</span><br />");
str.Append(HttpContext.Current.Server.HtmlDecode(Article));
str.Append("</div>");
return str.ToString();
}
and then i will be calling the bussinesslayer function GetArticle to the page, where i want to render article...
OMG, please don't do this. It's wrong in every way, because I see embedded HTML everywhere.
3-tier architecture means that persistence should not know about services should not know about views. There's nothing layered about this arrangement.
You exclude every UI developer ever when you do this. You'll need a back end developer for simple UI changes.
I see no leveraging of CSS or JavaScript.
How do you want to include a mobile UI?
This could not be more wrong.
A "tier" in an n-tier architecture is so called because it is functionally distinct from other parts of the application; in fact, it's more correct to say it's isolated from even the question of whether it's used in one or n applications.
"Layers" commonly refer to distinct groups of functionality within one tier.
"3-tier" applications came about because the pattern of using Data Access, Business Logic and Presentation in three distinct tiers is a quick fit for many scenarios, but it's by no means prescriptive that you need 3 tiers. The point is primarily that you have tiers at all.
In order to qualify as a tier, broadly speaking its responsibilities must not bleed in to another. By having a stored procedure in your database that returns HTML, you are bleeding presentation markup up into your business logic tier (of your chosen 3-tier approach) and then further outwards into your presentation logic.
You will find everything far simpler to test, and make performant, by returning only data from your stored procedures. Your business logic can process this data, choosing what (if any) filters, modifications and workflows to apply, and add metadata to that data before passing it outwards to your presentation tier that is solely responsible for placing that data inside of your HTML markup. In this way, your presentation tier is kept distinct, and you may then add layers to it that can generate markup for mobile applications, or JSON for RESTful services responses, etc. - all without the stored procedures caring.
Your friend's comments about performance optimisations were the tip of the iceberg, but may have referred to a natural consequence of keeping this separation: you would not be concatenating strings in your stored procedure to generate markup that is then further modified in your business logic.
Related
How can I use the DXL OLE mechanism to fetch a diagram's modification time from Enterprise Architect 12?
Details:
I want to retrieve diagrams from EA and integrate them as OLE object into IBM Rational DOORS 9.5. This is already working. I intend to compare the modification dates of the EA diagram and the DOORS object before retrieving the diagram to decide if this operation is really needed.
Problem is, EA provides a diagram attribute EA.Diagram.ModifiedDate which returns the diagram's modification date as data type Variant. How can I handle this within DXL? The result parameter for oleGet() can be one of the types string|int|bool|char|OleAutoObj. No structured type (which would probably be DxlObject). Neither string nor int parameters contain any useful data after the call -- just null values.
Test code:
OleAutoObj eaRepository, eaProject, eaDiagram
OleAutoObj eaApp = oleGetAutoObject("EA.App")
OleAutoArgs autoArgs = create
string guid = "{729F140F-9DA4-4ff6-A9B2-75622AD1C22D}"
// connect to an existing EA instance
oleGet (eaApp, "Repository", eaRepository)
oleMethod (eaRepository, "GetProjectInterface", autoArgs, eaProject)
// call EA to a diagram which has this GUID
put(autoArgs, guid)
oleMethod(eaRepository, "GetDiagramByGuid", autoArgs, eaDiagram)
delete autoArgs
// access diagram attributes
string eaModifiedDate // DXL accepts [string|int|bool|char|OleAutoObj] only
oleGet(eaDiagram, "ModifiedDate", eaModifiedDate)
print "ModifiedDate = '" eaModifiedDate"'\n"
IBM's Support Team (commercial, available for paying customers) couldn't help and suggested to forward this question to the Service Team (for extra $s). Rather disappointing.
Try this one (just guessing :-)
OleAutoObj eaModifiedDate
oleGet(diagram, "ModifiedDate", eaModifiedDate)
if (null eaModifiedDate)
print "no eaModifiedDate\n"
else {
string diaDate
oleGet(eaModifiedDate, "Value", diaDate)
print "ModifiedDate = '" diaDate"'\n"
}
If that does not work then here comes the ultimate work around:
string err
string result
put(autoArgs, "SELECT ModifiedDate FROM t_diagram WHERE ea_guid = '" guid "'")
err = oleMethod (eaRepository, "SQLQuery", autoArgs, result)
if (!null err)
print "ERROR: " err "\n"
delete autoArgs
print "result= '" result"'\n"
This will return a XML formatted string (!) which contains the date in the format you like.
Edit 1: Turns out that EA's SQLQuery is buggy and returns just the date. Since Perl deals with OLE Variants in a similar way I found this code snippet:
my $dia = $rep->getdiagrambyguid("{63EFF3FA-0D5C-4986-AC0A-C723D2F755E3}");
my $value = $dia->ModifiedDate->Value;
print $value->Date( 'yyyy/MM/dd' );
print $value->Time( 'hh:mm:ss' );
So the ModifiedDate is an ole-object and has a Date and a Time method. This should work with DXL too.
Edit 2:Now here's the ulti-ultimate work around even shipping around the cliffs of EA's bug ocean:
my $dia = $rep->SQLQuery("SELECT Format (ModifiedDate, \"Short Time\") AS T FROM t_diagram");
This will format the date as time string. Works for EAP (aka Mickeysoft Access) according to this page. Other RDBMS likely have similar functions.
An update with a few corrections of my statements in comments.
I was wrong. It is indeed possible to receive structured data types using the OLE automation interface. As Thomas mentioned, the OleAutoObject is the proper DXL return type, oleGet() is the proper function. To access the returned object's attributes, it is required to be aware of the "real" data type and to have a look at the SDK documentation in order to know which class attributes are available since DXL is not aware of the data type.
However, in DXL, retrieving the attribute of a structured data type is a bit cumbersone. Example (intended to be appended to the inital post's code):
OleAutoObj diaLinksCollection
int count = -1
string buffer = ""
// access simple diagram attributes
err = oleGet(eaDiagram, "Version", buffer)
if (!null err) print "ERROR in Diagram.Version: " err "\n"
print "diagram version = " buffer "\n"
// access structured diagram attribute
err = oleGet(eaDiagram, "DiagramLinks", diaLinksCollection)
if (!null err) print "ERROR in Diagram.DiagramLinks: " err "\n"
err = oleGet(diaLinksCollection, "Count", count)
if (!null err) print "ERROR in Collection.Count: " err "\n"
print "count = " count "\n"
Thus, it is indeed possible to execute an SQL query, even if the repository is residing in a plain *.eap file, see Thomas' workaround.
As mentioned, the Variant data type can not be retrieved using oleGet() since this approach returns a NULL result.
So here's the situation. I tried to use a quick way to get info from my database and put it in a table. Apparently, if I ask too much info from the following select, it just won't work.
<%
var myConn, myRec, refCo, myCmd;
refCo = Request.querystring("refC");
Response.Write(refCo);
myConn = new ActiveXObject("ADODB.Connection");
myRec = new ActiveXObject("ADODB.Recordset");
myCmd = new ActiveXObject("ADODB.Command");
myConn.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:/inetpub/wwwroot/unsite/database/world2003.mdb;");
/* myCmd.ActiveConnection = myConn;
myCmd.CommandText = "SELECT NameCountry, Capital, Language, Area, Population, Descripton FROM COUNTRIES WHERE NumCountry = " + refCo";
myCmd.Execute(); */
myRec.Open("SELECT NameCountry, Capital, Language, Area, Population, Descripton FROM COUNTRIES WHERE NumCountry = " + refCo,myConn);%>
See the myRec.Open? It does not allow me to go pass Capital. It just won't. I figured that maybe it was too much and to go with the longer form, meaning a ADODB Command. Say I take off the comments and use myCmd, it won't work at all. Can anyone help me?
It is not because of the number of items in the select list, but because you have a reserved word Language as a field name. You could rename the field to something else or escape it as [Language].
Change your query to:SELECT NameCountry, Capital, [Language], Area, Population, Descripton FROM COUNTRIES WHERE NumCountry = " + refCo and it should work
See this: List of reserved words in Jet 4.0
Anyone have sample lookup code for AOT objects? (or know where to find the one they use for the AX properties window)
I need to replicate the functionality that you see in several fields in the properties window. The ExtendedDataType field is a good example. Type a few letters, hit the down arrow, and a filtered list of AOT ExtendedDataType objects appears.
I've been trying to use treeNode findChildren to build my custom lookup list, but it is very slow. Whatever method AX is using happens instantly.
Thanks
Try this:
Dictionary d = new Dictionary();
int i;
int cnt = d.tableCnt();
TableId tableId;
str nameForLookup;
for (i = 1; i <= cnt; i++)
{
tableId = d.tablecnt2id(i);
nameForLookup = tableid2name(tableId);
}
Queries to the Model/Util*Element tables will not be cached, and are relatively slow due to the number of records that they contain.
There may be other factors slowing down execution as well. If you are on 2012, for lookups, you may want to build a temp table with an XDS() method, which populates itself using the above code, then you can simply select from that table (and it will be cached for the session):
create a SQL Temp table (e.g. with a name like MyTableLookup), add a name column
add a method like this:
public RefreshFrequency XDS()
{
MyTableLookup tableLookup;
ttsbegin;
// Use the above code to insert records into tableLookup
ttscommit;
return RefreshFrequency::PerSession;
}
bind your form to MyLookupTable
You may develop an estándar EDT linked to the UtilElement Table properly filtered. This will show a list of objects and will have same functionality of all table-linked text fields.
We're looking into refining our User Groups in Dynamics AX 2009 into more precise and fine-tuned groupings due to the wide range of variability between specific people within the same department. With this plan, it wouldn't be uncommon for majority of our users to fall user 5+ user groups.
Part of this would involve us expanding the default length of the User Group ID from 10 to 40 (as per Best Practice for naming conventions) since 10 characters don't give us enough room to adequately name each group as we would like (again, based on Best Practice Naming Conventions).
We have found that the main information seems to be obtained from the UserGroupInfo table, but that table isn't present under the Data Dictionary (it's under the System Documentation, so unavailable to be changed that way by my understanding). We've also found the UserGroupName EDT, but that is already set at 40 characters. The form itself doesn't seem to restricting the length of the field either. We've discussed changing the field on the SQL directly, but again my understanding is that if we do a full synchronization it would overwrite this change.
Where can we go to change this particular setting, or is it possible to change?
The size of the user group id is defined as as system extended data type (here \System Documentation\Types\userGroupId) and you cannot change any of the properties including the size 10 length.
You should live with that, don't try to fake the system using direct SQL changes. Even if you did that, AX would still believe that length is 10.
You could change the SysUserInfo form to show the group name only. The groupId might as well be assigned by a number sequence in your context.
I wrote a job to change the string size via X++ and it works for EDTs, but it can't seem to find the "userGroupId". From the general feel of AX I get, I'd be willing to guess that they just have it in a different location, but maybe not. I wonder if this could be tweaked to work:
static void Job9(Args _args)
{
#AOT
TreeNode treeNode;
Struct propertiesExt;
Map mapNewPropertyValues;
void setTreeNodePropertyExt(
Struct _propertiesExt,
Map _newProperties
)
{
Counter propertiesCount;
Array propertyInfoArray;
Struct propertyInfo;
str propertyValue;
int i;
;
_newProperties.insert('IsDefault', '0');
propertiesCount = _propertiesExt.value('Entries');
propertyInfoArray = _propertiesExt.value('PropertyInfo');
for (i = 1; i <= propertiesCount; i++)
{
propertyInfo = propertyInfoArray.value(i);
if (_newProperties.exists(propertyInfo.value('Name')))
{
propertyValue = _newProperties.lookup(propertyInfo.value('Name'));
propertyInfo.value('Value', propertyValue);
}
}
}
;
treeNode = TreeNode::findNode(#ExtendedDataTypesPath);
// This doesn't seem to be able to find the system type
//treeNode = treeNode.AOTfindChild('userGroupId');
treeNode = treeNode.AOTfindChild('AccountCategory');
propertiesExt = treeNode.AOTgetPropertiesExt();
mapNewPropertyValues = new Map(Types::String, Types::String);
mapNewPropertyValues.insert('StringSize', '30');
setTreeNodePropertyExt(propertiesExt, mapNewPropertyValues);
treeNode.AOTsetPropertiesExt(propertiesExt);
treeNode.AOTsave();
info("Done");
}
I have taken over a departing programmer's Classic ASP object, and I'm trying to debug some DAC code. The trouble is I don't really understand what DAC is or how it works -- and I can't find were the DAC code "lives".
Here's the individual line of code:
set runObj = server.CreateObject("DAC.clsDb_container")
We use SQL Server, and I looked in Enterprise Manager under Stored Procedures and User-Defined functions, but I don't see anything named clsDB_container.
Any suggestions where I can find the code for this DAC object?
The full code in context:
FUNCTION getNewGUID
Dim runCON, runObj, runCMD
DebugWrite( "<BEGIN> iRoutines.asp|getNewGUID (a) GUID=" & GUID & " dealernum=" & dealernum )
set runObj = server.CreateObject("DAC.clsDb_container")
if not runObj.run_query("EXEC sproc_createGUID") then
traperror(runObj.DB_ErrStr)
else
GUID = replace(runObj.get_by_ordinal(0),"-","")
dealernum_len = trim(cstr(len(dealernum)))
set runObj = nothing
end if
getNewGUID = dealernum_len & dealernum & GUID
DebugWrite( "<END> iRoutines.asp|getNewGUID (b) getNewGUID=" & getNewGUID & " dealernum=" & dealernum )
END FUNCTION
This looks like a custom COM object that was written as a data access layer for the site.
The name of the object would be DAC.clsDb_container and lives in a DLL somewhere on the web server.
It is not standard - you will need to look for (I am guessing here) the VB6 or Delphi code that created it if you want to be enlightened further.
if all you need is a GUID then you could do this
<%
Function createGuid()
Set TypeLib = Server.CreateObject("Scriptlet.TypeLib")
dim tg : tg = TypeLib.Guid
createGuid = left(tg, len(tg)-2)
Set TypeLib = Nothing
End Function
%>