help me!

help me!

Old forum URL: forums.lhotka.net/forums/t/2697.aspx


zlonly posted on Saturday, April 14, 2007

Hi,everyone

   I'm refactoring my three tier sales management system with csla framework.Maybe I don't understand clearly this framework,when creating my business object--customer,salesorder,and so on,I met some dificulties.

   In Customer component,there are several search logics, getCustomerByID,getCustomerByCompanyCode .I place them in Factory method region:

public static Customer GetCustomerByID(int id)

{

   if (!CanBrowseCustomer())

      throw new System.Security.SecurityException("User not authorized to view a customer");

      return DataPortal.Fetch<Customer>(new Criteria(id));

}

public static Customer GetCustomerByCompanyCode(string companyCode)

{

   if (!CanBrowseCustomer())

      throw new System.Security.SecurityException("User not authorized to view a customer");

      return DataPortal.Fetch<Customer>(new Criteria(companyCode));

}

But I have two diferrent store procedures matchd them individualy,How can I code DataPortal_Fetch method to shift between two store procedures.

thanks very much!

albruan replied on Saturday, April 14, 2007

You need to set up your Criteria class something along the line of:

[Serializable()]
private class Criteria
{
   private string _companyCode;
   private int _id;

   public Criteria(string companyCode)
   {
      this._companyCode = companyCode;
   }

   public Criteria(int id)
   {
      this._id = id;
   }
}

Then, inside your DataPortal_Fetch method, you could check to see if criteria._companyCode is null, if it is then you'd run your sproc that utilizes the ID.  Something like the following:

using (SqlCommand cm = cn.CreateCommand())
{
   cm.CommandType = CommandType.StoredProcedure;

   if (criteria._companyName == null)
   {
     cm.CommandText = "GetCustomerByID";
     cm.Parameters.AddWithValue("@ID", criteria._id);
   }
   else
   {
      cm.CommandText = "GetCustomerByCompanyCode";
      cm.Parameters.AddWithValue("@CompanyCode", criteria._companyCode);
   }

   using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader()))
   {
      while (dr.Read())
         this.Add(Customer.GetCustomer(dr));
   }
}  //using

zlonly replied on Saturday, April 14, 2007

thanks,

I see you,but if there are more store procedures,such as getCustomerByLoginName,getCustomerByEmail,and so on,I think this solution is need refactoring and can't know how to select which procedures.

pls

albruan replied on Sunday, April 15, 2007

That isn't any problem.  All you need is a variation of what I suggested in my previous posting.  As far as passing in a variety of string arguments (such as CompanyCode, LoginName, Email, etc.), you could have something like the following in your Factory section:

public static Customer GetCustomer(string foo, string fooType)
{
   if (!CanBrowseCustomer())
      throw new System.Security.SecurityException("User not authorized to view a Customer");
   return DataPortal.Fetch<Customer>(new Criteria(foo, fooType));
}

Your criteria region could then be something like:

[Serializable()]
private class Criteria
{
   private string _foo;
   private string _fooType;
   private int _id;

   public Criteria(string foo, string fooType)
   {
      this._foo = foo;
      this._fooType = fooType;
   }

   public Criteria(int id)
   {
      this._id = id;
   }
}

Then, your DataPortal_Fetch method could be something along the line of:

using (SqlCommand cm = cn.CreateCommand())
{
   cm.CommandType = CommandType.StoredProcedure;

   if (criteria._foo == null)
   {
     cm.CommandText = "GetCustomerByID";
     cm.Parameters.AddWithValue("@ID", criteria._id);
   }
   else
   {
      switch (criteria._fooType)
      {
         case "CompanyCode":
            cm.CommandText = "GetCustomerByCompanyCode";
            cm.Parameters.AddWithValue("@CompanyCode", criteria._foo);
            break;
         case "LoginName":
            cm.CommandText = "GetCustomerByLoginName";
            cm.Parameters.AddWithValue("@LoginName", criteria._foo);
            break;
         case "Email":
            // same as the previous two cases
            break;
         default:
            break;
      }
   }

   using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader()))
   {
      while (dr.Read())
         this.Add(Customer.GetCustomer(dr));
   }
}  //using

With something like this, all you'd need to do if you're searching for a Customer by passing in a string argument, you'd call the xyz.GetCustomer method passing in not only the string argument, but a second one describing what type of string argument you're passing in.  For instance, let's say you're wanting to retrieve the Customer information for Coca-Cola by CompanyName; in that case, you'd call xyz.GetCustomer("Coca-Cola", "ByCompanyName").  Or, if you're wanting to retrieve it by CompanyCode, you'd call xyz.GetCustomer("KO", "ByCompanyCode").  KO is the ticker symbol for Coca-Cola on the New York Stock Exchange.  Or, if you're wanting to retrieve it by someone's e-mail, you'd call xyz.GetCustomer("johndoe @ na.ko.com", "ByEmail").  So on and so forth.

ajj3085 replied on Monday, April 16, 2007

It looks like you should have two different Criteria objects, an IdCriteria and a CompanyCodeCriteria

Then you would have two DP_F, methods, each one accepts one of the above criteria.

Once you have a result set back, you can call a method that simply takes the result set and loads the instance.

HTH
Andy

JoeFallon1 replied on Monday, April 16, 2007

Andy has nailed the key point - you can have more than 1 Criteria class.

Just give them different names and they are different types.

I moved all of mine to a central file so that every class can use the general Criteria classes like:

CriteriaCode, CriteriaKey, etc.

In 2.x you can overload Fetch and use strongly typed criteria which is one way to handle the branching logic. Another is to use a single Fetch with Criteria As Object and then test TypeOf Criteria and branch there. Also - be sure to move any common logic to a separate method.

e.g.

DataPortal_Fetch calls this overridable method:

DoFetch which sets up the connection and opens it and then passes it to these overridable methods:

FetchData(criteria, cn)

PostFetchData(criteria, cn)

FetchChildren(criteria, cn)

This way in my derived classes I can selectively override a given area to get the behavior I need in case my code gen class is not adequate.

One other branching technique I use is to include a String parameter in my Criteria classes called MethodName. This allows me to branch on method name in case the Criteria class is the same type in 2 different factory calls.

e.g. If TypeOf Crtieria Is CriteriaCode Then

         If MethodName="Method1" Then
            'do stuff here
         ElseIf MethodName="Method2" Then
            'do other stuff here
         End If

     End If

Joe

david.wendelken replied on Monday, April 16, 2007

Try this:

protected enum CriteriaType {Id, CompanyCode};

public static Customer GetCustomerByID(int id)

{

   if (!CanBrowseCustomer())

      throw new System.Security.SecurityException("User not authorized to view a customer");

      return DataPortal.Fetch<Customer>(new Criteria(CriteriaType.Id,id));

}

public static Customer GetCustomerByCompanyCode(string companyCode)

{

   if (!CanBrowseCustomer())

      throw new System.Security.SecurityException("User not authorized to view a customer");

      return DataPortal.Fetch<Customer>(new Criteria
                                                   (CriteriaType.CompanyCode,companyCode));

}

 

This way, users of the class don't have to mess with setting a criteria type.

 

zlonly replied on Wednesday, April 18, 2007

Thanks everyone ,I see you!

Copyright (c) Marimer LLC