TheChaseMan's Frenetic SoapBox

Always looking for better ways to do things...

Saturday, June 06, 2009 #

Interested in writing a LINQ query to fill a DataTable? Normally I don't condone writing ugly code but let's have some fun. In this example, an anonymous type array is created followed by a DataTable. Then a LINQ query that includes a generic Func delegate encapsulating a lambda expression which is invoked to create a new DataRow based on the DataTable structure and then fill it with the properties from the anonymous type. The results (IEnumerable<DataRow>) are then added to the DataTable and then the DataTable is serialized to the Console output stream. In the words of Dilbert: if this is still too readable I can photocopy it and then run it through the fax machine a few times...

using System;

using System.Data;

using System.Linq;

 

namespace App {

    class Program {

        static void Main(string[] args) {

            var pretendThisIsADatabaseTable = new[] { new { Name = "Steve", Age = 33 }, new { Name = "Doug", Age = 34 } };

 

            //Create empty destination data table

            DataTable personTable = new DataTable();

            personTable.TableName = "PersonTable";

            personTable.Columns.Add("Name", typeof(string));

            personTable.Columns.Add("Age", typeof(int));

 

            //Query anonymous type array as DataRow objects

            var results = from person in pretendThisIsADatabaseTable

                          select new Func<DataRow, string, int, DataRow>(

                              (DataRow row, string name, int age) => {

                                  row["Name"] = name;

                                  row["Age"] = age;

                                  return row;

                              }

                          )

                          .Invoke(personTable.NewRow(), person.Name, person.Age);

 

            //put rows into data table

            results.ToList().ForEach(row => personTable.Rows.Add(row));

 

            personTable.WriteXml(Console.Out);

        }

    }

}

posted @ 9:20 AM | Feedback (0)

Wednesday, June 03, 2009 #

For a while now I've been in "lurker" mode in all of the .NET community blogs. One of the interesting transitions that's been going on is the demo code for...well...just about everything including LINQ (whether it's the ADO.NET Entity Framework, LINQ-to-SQL, etc) or even ASP.NET MVC which includes using LINQ. Specifically, the removal of application "layers." Don't get me wrong, I think logically there's great patterns of cohesion to support the whole ability necessary for things like ASP.NET MVC's indirection. However sometimes, looking at this code (not to mention writing it) gives me a sense of going backwards into monolithic-type architecture. So I've been pondering from an architecture/design perspective how to implement all of this cool stuff but at the same time keep my mind at ease. So in playing around with Northwind (my favorite example database of all time :) )

  • Data Access Layer - Simply generate an ADO.NET Entity Framework Model  (.edmx)
  • Business Interface - This layer contains our use-cases where the UI leverages its services via messages, data structures, whatever makes sense.
  • Common Library - Since I don't like the idea of ADO.NET EF objects as my DTOs, I'd rather roll my own POJO (C#) objects (or even use DataTable or derivative of it).

//Common Library Assembly DTO Class

namespace Northwind.Common {

    public class CustomerEntity {

        public string Address { get; set; }

        public string City { get; set; }

        public string CompanyName { get; set; }

        public string ContactName { get; set; }

        public string ContactTitle { get; set; }

        public string Country { get; set; }

        public string CustomerID { get; set; }

        public string Fax { get; set; }

        public string Phone { get; set; }

        public string PostalCode { get; set; }

        public string Region { get; set; }

    }

}

 

//Business Interface (or Facade) Assembly

namespace Northwind.BusinessInterface {

    static public class CustomerInterface {

 

        static public List<Northwind.Common.CustomerEntity> GetAll() {

            using (var nwEntities = new NorthwindAPI.NorthwindEntities()) {

                var results = from custs in nwEntities.Customers

                              select new Northwind.Common.CustomerEntity() {

                                  Address = custs.Address,

                                  City = custs.City,

                                  CompanyName = custs.CompanyName,

                                  ContactName = custs.ContactName,

                                  ContactTitle = custs.ContactTitle,

                                  Country = custs.Country,

                                  CustomerID = custs.CustomerID,

                                  Fax = custs.Fax,

                                  Phone = custs.Phone,

                                  PostalCode = custs.PostalCode,

                                  Region = custs.Region

                              };

 

                Debug.WriteLine(((ObjectQuery)results).ToTraceString());

 

                return results.ToList();

            }

        }

    }

}

 

//Client Console App

namespace TestClient {

    class Program {

        static void Main(string[] args) {

            List<Northwind.Common.CustomerEntity> custs = Northwind.BusinessInterface.CustomerInterface.GetAll();

            custs.ForEach(c => Console.WriteLine(c.CustomerID));

        }

    }

}

 

This is nothing new as far as architecture ideas go - many designers are running into the same philosophical conflict, but what is interesting is the other questions it raises. When you've had tried-and-true methodologies for many years, it's difficult to simply jump ship and feel good about compiling LINQ queries into your code versus writing stored procs, or using ORM entities throughout your application versus creating more vanilla DTOs.  For something intended as only a Web application, how much do I care about tier separation versus logic-layers? We're assuming the application will end up only being a Web application. If it turns into a system that then needs to become published into a service, do you then refactor at that point, or do you plan ahead by separating layers as I'm inclined to do? What is the maintenance impact? How can we assume that our database is already designed and fleshed-out enough to the point where it is OK to use an ORM against it? Does this mean we design our database first? Generally it makes more sense to come up with our use-cases first, which is more of the BusinessInterface layer (and UI to some extent) before we identify what our system entity/attribute and data storage look like. If we start with use-cases (or tests for our TDD enthusiasts), will the database ever change during the development cycle? If so, will having to refactor my model and the corresponding ORM-generated classes have a significant impact on my team during development?

It is interesting stuff to think about. I'll keep lurking and see what others come up with too. Drop me a line if you have any interesting opinions on the subject. :)

 

posted @ 10:15 AM | Feedback (0)

Tuesday, March 10, 2009 #

Long title - but that says it all. Today I wasted a large chunk of time trying to figure out why I was getting a challenge dialog on my ASP.NET site. The site was configured to use an application pool that I made. The app pool was set to run as a domain account identity and the NTFS permissions on all of the folders looked fine...but I kept getting prompted to enter my user credentials. Buried in a sea of security event log entries were several kerberos errors. It turns out that on this server, kerberos was on by default.

The solution was to run a simple command line to force IIS to use NTLM:

adsutil.vbs set w3svc/3/NTAuthenticationProviders "NTLM"

http://support.microsoft.com/default.aspx?scid=kb;EN-US;871179

Hope this saves someone some time if you run into the same problem.  :)

posted @ 7:44 PM | Feedback (0)

Tuesday, November 27, 2007 #

I'm sure you've heard by now the Visual Studio 2008 is RTM. If you haven't already, play around a bit with object/collection initializers and LINQ. One interesting thing  I've learned today is the “let” clause which allows you to define a subexpression in a LINQ query. Also you can generate XML fairly easily in your LINQ queries (or anything else you want to transform for that matter).

class Program

{

    static void Main(string[] args)

    {

        List<Person> people = new List<Person>(){

            new Person { Name = "Jack", Age = 22, FavoriteColors = new string[] { "Green", "Red" } },

            new Person { Name = "Jill", Age = 33, FavoriteColors = new string[] { "Black", "Blue" } }

        };

 

        var xmlSource = new XElement("People",

        from person in people

        let colors = String.Format("{0},{1}", person.FavoriteColors[0], person.FavoriteColors[1])

        select new XElement("Person",

                   new XElement("Name", person.Name),

                   new XElement("Age", person.Age),

                   new XElement("FavoriteColors", colors)

                )

            );

 

        Console.WriteLine(xmlSource);

 

        /* output:

            <People>

              <Person>

                <Name>Jack</Name>

                <Age>22</Age>

                <FavoriteColors>Green,Red</FavoriteColors>

              </Person>

              <Person>

                <Name>Jill</Name>

                <Age>33</Age>

                <FavoriteColors>Black,Blue</FavoriteColors>

              </Person>

            </People>

         */

    }

}

posted @ 2:48 PM | Feedback (0)

Thursday, October 25, 2007 #

Just FYI - if you are using the System.Net.OracleClient namespace with 10g it is possible you will run into this error. Now before you start getting too excited about being able to set the ReturnProviderSpecificTypes property on an adapter, you can run into this problem with output parameters on an ExecuteNonQuery so it's probably a safer bet to use the OleDb provider with 10g.

Flame on if I'm wrong.

posted @ 12:31 PM | Feedback (0)

I'm not going to bash Oracle...it does a good job of bashing itself. For those of us used to being productive using SQL Server (I've heard it said that Oracle sells its products to managers, Microsoft sells to developers), here's some interesting info on calling an Oracle proc.

OK, so you are used to SQL Server and you want to create a proc that ultimately (at some point) returns some results in the form of...well, a bunch of records!

create procedure foo as
    ...
    select a, b, c
    from bar
    where blah = 1

You jump into Oracle and try the same thing you'll get this nice error message:

PLS-00428: an INTO clause is expected in this SELECT statement. Also we are better than you M$ noobcakes and Bill Gates is a jerk. Windoze is teh suxxorz. ZOMG barrens chat.

The way around this is to use a ref cursor. It's silly and it's juvenile and it's like going back in time, but it works. http://support.microsoft.com/?id=309361

But before you get to pissed off about having to create an Oracle package and define a cursor type, with 9i and 10g you can use sys_refcursor and save the trouble.

CREATE OR REPLACE PROCEDURE Foo (io_cursor IN OUT sys_refcursor) AS

v_cursor sys_refcursor;

...
begin
    open v_cursor for
    select a, b, c
    where blah = 1;

    io_cursor := v_cursor;
end;

That's it, you're done! Yay!
 
Oh wait no you're not!!! Because if you want to test it you're probably thinking "Hey I can just type EXEC Foo in Query Analyzer." Sorry there's more pig $#!+ you have to go through than that. Isn't DBArtisan a great tool?!?! *vomits*

declare
   
v_cursor sys_refcursor;
    a number;
    b number;
    c number

begin
   
Foo(io_cursor => v_cursor);

    loop
        fetch v_cursor into a, b, c; 
        exit when v_cursor%NOTFOUND;
        dbms_output.put_line(a || b || c);
     end loop;
     close v_cursor;
end;

Hopefully you're not stuck in Oracle hell (god I miss SQL Server 2005), but if you've had your nose jammed in it like a bad dog and you're not getting help from the elitist oracle buff community, and you run across this problem...hope it helps. :-)

posted @ 12:13 PM | Feedback (0)

Friday, February 23, 2007 #

Just a heads up if you use the AutoCompleteExtender Ajax control. I'm not sure if this is because I'm using Visual Studio SP1 (using Web Application Projects), but I could not get this control to work with an ASMX, but rather a WebMethod in the page's code-behind. Also, make sure your ServiceMethod attribute declaration is static!

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:TextBox runat="server" ID="myTextBox" Width="300" autocomplete="off" />
<ajaxToolkit:AutoCompleteExtender runat="server"
                                  ID="autoComplete1"
                                  TargetControlID="myTextBox"
                                  ServiceMethod="GetCompletionList"
                                  MinimumPrefixLength="1"
                                  CompletionInterval="1000"
                                  EnableCaching="true"
                                  CompletionSetCount="12" />
</form>

 

public partial class _Default : System.Web.UI.Page {
    [WebMethod]
    public static string[] GetCompletionList(string prefixText, int count) {
        string sql = String.Format("select companyName from customers where companyname like @companyname + '%'");

        List<string> companyList = new List<string>();
        using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Initial Catalog=Northwind;Data Source=."))
        using (SqlCommand command = new SqlCommand(sql, connection)) {
            connection.Open();
            command.Parameters.AddWithValue("@companyname", prefixText);
            using (SqlDataReader reader = command.ExecuteReader()) {
                while (reader.Read()) {
                    companyList.Add(reader.GetString(0));
                }
            }
        }

        return companyList.ToArray();
    }
}

 

 

posted @ 2:20 PM | Feedback (3)

Thursday, February 15, 2007 #

Just wanted to share this in case anyone has issues with very slow build and debug time in Visual Studio for ASP.NET applications. This solution might sound a lot like holding tin foil over your head, but....try removing all of your breakpoints. "Clear All Breakpoints" can actually make good things happen.

posted @ 3:18 PM | Feedback (1)

This sums it up nicely.  :-)

posted @ 3:12 PM | Feedback (1)

Monday, December 18, 2006 #

We've had some interesting discussions about Web Services around the office lately. One of the major challenges are people that want to consume Web Services that transfer large amounts of data rather than a single operation per record or having to deal with filter constraints. The major problem with this is "ballooning" of data on the server - meaning you build up a huge result set to send back to the client which can take up a ton of resources. This can increase exponentially as the number of requests increases. One solution is to stream the data to the clients in "chunks." Yasser Shohoud has a really good example of streaming songs in this manner; however, many of us are dealing with data from a database rather than songs so I wanted to blog about how this can be done. First I'd like to thank someone who wishes to remain anonymous (we'll call him Homer) for helping me understand conceptually what was happening during the most challenging part of this example: ReadXml().

This example is architected with three assemblies: Client, Web Service, and a Common library. Let's start with the Common library where our main functionality will be a class named CustomerStream. CustomerStream simply implements IXmlSerializable. This requires us to implement GetSchema, ReadXml, and WriteXml methods. CustomerStream will be designed so that we can pass it a SqlDataReader in the constructor so we can use that to write the data in chunks. First, we'll implement GetSchema which is very simple, and WriteXml. WriteXml will use our SqlDataReader to write out our data chunks (in this case we'll use Customer records from the Northwind example database).

 

namespace StreamService.Common {
    public class CustomerStream : IXmlSerializable {
        private const string ns = "http://tempuri.org";
        private SqlDataReader _reader;
 
        public CustomerStream() { }
 
        public CustomerStream(SqlDataReader reader) {
            _reader = reader;
        }
 
        public System.Xml.Schema.XmlSchema GetSchema() {
            string schema = @"<xs:schema id=""Customer"" targetNamespace=""http://tempuri.org/Customer.xsd"" elementFormDefault=""qualified"" xmlns=""http://tempuri.org/Customer.xsd"" xmlns:mstns=""http://tempuri.org/Customer.xsd"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"">
                              <xs:element name=""Customer"">
                                <xs:complexType>
                                  <xs:sequence>
                                    <xs:element name=""CustomerID"" type=""xs:string"" />
                                    <xs:element name=""CompanyName"" type=""xs:string"" />
                                    <xs:element name=""ContactName"" type=""xs:string"" />
                                    <xs:element name=""Country"" type=""xs:string"" />
                                    <xs:element name=""Phone"" type=""xs:string"" />
                                  </xs:sequence>
                                </xs:complexType>
                              </xs:element>
                            </xs:schema>";
 
            XmlSchema xs = XmlSchema.Read(new StringReader(schema), null);
            return xs;
        }
 
        public void WriteXml(System.Xml.XmlWriter writer) {
            if (_reader == null) throw new InvalidOperationException("SqlDataReader expected");
 
            using (_reader) {
                while (_reader.Read()) {
                    writer.WriteStartElement("Customer", ns);
                    for (int fieldIndex = 0; fieldIndex < _reader.FieldCount; fieldIndex++) {
                        writer.WriteElementString(_reader.GetName(fieldIndex), ns, _reader.GetValue(fieldIndex).ToString());
                    }
                    writer.WriteEndElement();
                }
            }
        }
    }
}

The next class we'll want to implement is some sort of custom reader for iterating the results. Because this is a dependency of the ReadXml() method which we haven't implemented at this point, we'll do that first. This is a very simplistic design that consists mostly of throwing NotImplementedException (haha), but gets the job done for this example and you could certainly extend this concept. Basically the custructor takes a file path where the results are stored in an XML document and operates similiar to a datareader using GetCustomer().

 

namespace StreamService.Common {
    public class CustomerDataReader : DbDataReader, IDataReader, IDisposable, IDataRecord {
        private Dictionary<int, string> _ordinals;
        private XmlNodeList _nodes;
        private XmlDocument _document;
        private Customer _current;
        private int _count;
 
        public CustomerDataReader(string path) {
            _document = new XmlDocument();
            _document.Load(path);
            _nodes = _document.SelectNodes("//Customers/*");
 
            _ordinals = new Dictionary<int, string>();
            _ordinals.Add(0, "CustomerID");
            _ordinals.Add(1, "CompanyName");
            _ordinals.Add(2, "ContactName");
            _ordinals.Add(3, "Country");
            _ordinals.Add(4, "Phone");
        }
 
        public override int GetOrdinal(string name) {
            foreach (KeyValuePair<int, string> entry in _ordinals) {
                if (entry.Value == name) return entry.Key;
            }
            throw new ArgumentException("Name does not exist.");
        }
 
        public override bool Read() {
            if (_count < _nodes.Count) {
                XmlNode currentNode = _nodes[_count++];
 
                _current = new Customer();
                foreach (XmlNode child in currentNode.ChildNodes) {
                    typeof(Customer).GetProperty(child.Name).SetValue(_current, child.InnerText, null);
                }
                return true;
            }
            return false;
        }
 
        public Customer GetCustomer() {
            return _current;
        }
 
        public override void Close() {
            // ...
        }
        // NOTE: rest of the methods throw not implemented exceptions
    }
}

Now we are set to implement the ReadXml() method that is missing from our CustomerStream class as well as a method that allows the calling client to implement our custom datareader class...

 

//CustomerStream class methods...
public CustomerDataReader ExecuteCustomerDataReader() {
    CustomerDataReader reader = new CustomerDataReader(Environment.CurrentDirectory + "\\Customers.xml");
    return reader;
}
 
public void ReadXml(System.Xml.XmlReader reader) {
    using (XmlTextWriter xwriter = new XmlTextWriter(Environment.CurrentDirectory + "\\Customers.xml", Encoding.UTF8)) {
        xwriter.WriteRaw(@"<?xml version=""1.0"" encoding=""utf-8""?>");
        xwriter.WriteStartElement("Customers");
 
        while (reader.Read()) {
            if (reader.LocalName == "Customer") {
                xwriter.WriteStartElement("Customer");
                reader.ReadStartElement();
 
                while (reader.NodeType != XmlNodeType.EndElement) {
                    xwriter.WriteElementString(reader.LocalName, reader.ReadElementString());
                }
                xwriter.WriteEndElement();
            }
        }
        xwriter.WriteEndElement();
    }
}

Since our custom datareader class exposes a class type named Customer, here is the code for that...
 

namespace StreamService.Common {
    public class Customer {
        private string _phone;
        private string _country;
        private string _contactName;
        private string _companyName;
        private string _customerID;
 
        public Customer() { }
 
        public Customer(string customerID, string companyName, string contactName, string country, string phone) {
            _customerID = customerID;
            _companyName = companyName;
            _contactName = contactName;
            _country = country;
            _phone = phone;
        }
 
        public string CustomerID {
            get { return _customerID; }
            set { _customerID = value; }
        }
 
        public string CompanyName {
            get { return _companyName; }
            set { _companyName = value; }
        }
 
        public string ContactName {
            get { return _contactName; }
            set { _contactName = value; }
        }
 
        public string Country {
            get { return _country; }
            set { _country = value; }
        }
 
        public string Phone {
            get { return _phone; }
            set { _phone = value; }
        }
    }
}

The next step is to write our Web Service. This is very straight forward - all we have to do is turn off buffering and create a SqlDataReader to query the Customers table in the Northwind database. Note that the responsibility for closing the SqlDataReader and the SqlConnection lies within the CustomerStream class. This is so that we do not get disconnected after the GetCustomers WebMethod finishes executing and we are within the context of the server streaming down the data chunks (customer records) to the client...
 

namespace StreamService.Server {
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.DesignerCategory("Code")]
    [ToolboxItem(false)]
 
    public class TheWebService : System.Web.Services.WebService {
        [WebMethod]
        public CustomerStream GetCustomers() {
            //turn off buffering
            HttpContext.Current.Response.Buffer = false;
 
            //do *not* dispose of connection or datareader - our CustomerStream class does this
            SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString);
            using (SqlCommand command = new SqlCommand("SELECT CustomerID, CompanyName, ContactName, Country, Phone FROM Customers", connection)) {
                connection.Open();
                SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);
                CustomerStream stream = new CustomerStream(reader);
                return stream;
            }
        }
    }
}

Next, we need to create a client application to call the Web Service...
 

namespace StreamService.Client {
    class Program {
        static void Main(string[] args) {
            TheWebService ws = new TheWebService();
            CustomerStream stream = ws.GetCustomers();
 
            XmlSerializer ser = new XmlSerializer(typeof(Customer));
 
            using(CustomerDataReader reader = stream.ExecuteCustomerDataReader()){
                while (reader.Read()) {
                    Customer customer = reader.GetCustomer();
                    ser.Serialize(Console.Out, customer);
                }
            }
        }
    }
}

Lastly, we'll need to make a small tweak to the client's Reference.cs code (our proxy class generated by Visual Studio when we reference the Web Service) so that the type casted and returned is a CustomerStream object..
 

/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/GetCustomers", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public CustomerStream GetCustomers() {
    object[] results = this.Invoke("GetCustomers", new object[0]);
    return ((CustomerStream)(results[0]));
}

I hope you found this interesting and informative. Merry Christmas, Happy Holidays, etc!

posted @ 1:19 AM | Feedback (4)