Rails中的SessionStore

最近用Rails(版本为2.3.5,ruby版本为1.8.6),需要使用Session。考虑可伸缩性的问题,应该将Session存储在数据 库中。我使用的数据库为MySQL 5.0。Rails默认的SessionStore为文件,因此需要修改config下environment.rb的设置,即设置为:

config.action_controller.session_store = :active_record_store

谁知道设置好session_store后,网页(我使用的浏览器为Firefox)竟然无法打开,显示如下:image我明白这是Session的问题,但检查了数据库中的Session表以及Rails中对Session的配置,都没有任何问题。通过检查日志,发现如下错误提示:

Status: 500 Internal Server Error
  Mysql::Error: Data too long for column 'session_id' at row 1:
INSERT INTO `sessions` (`updated_at`, `session_id`, `data`, `created_at`)
VALUES('2010-07-23 01:17:05', 'BAh7CDoPc2Vzc2lvbl9pZCIlMDNlNTg0ZDg2NjJkZmFjZT
diMjUwN2ExNmYyNGNjNjAiCmZsYXNoSUM6J0FjdG
lvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7A
AY6CkB1c2VkewA6EF9jc3JmX3Rva2VuIjFWNStxMUc
4bThmUmJHQ3VHRGQ3Q2RJR2p1aStUejdtRmRtc
0xJY1RIOUNrPQ==--6f29ea421cd0f14d5e8de7093
5515c26021c254f', 'BAh7AA==\n', '2010-07-23 01:17:05')

问题的症结显然并非配置的问题,而是插入的session_id太大。我检查了Sessions数据表的Column,发现其定义如下:

image 可是,我在enviroment.rb中,设置了Session的secret,如下:

config.action_controller.session = {
  :key => '_session',
  :secret      => 'f914e9b1bbdb829688de8512f5fea7d8e83fb35b
fe2b56bcf1e6438d1d1b7837c532f8c2ece2a2
d0e37812e9b210824089b1810a4e238a61dfd922dc9dd62521'
}

即使将secret设置为更短的值同样如此。

最后,我终于找到了答案,真是啼笑皆非。很简单的办法,那就是清除掉浏览器中Cookie的值。一切Ok。现在,查看sessions表,则session_id的值为:

image

session_id的长度变得正常了。系统同样恢复正常。这真的是一个容易忽视的陷阱。留此存照!

标签:

RadRails插件在MyEclipse的安装

准备:在安装插件之前,建议先安装Ruby、Rails以及MySQL。我选择的Ruby安装包是rubyinstaller for windows版本,可以在http://rubyinstaller.org/网站上下载。安装过程非常简单,直接按照向导提示即可(注意,在安装过程中,一定要选中Enable RubyGems选项,该选项默认为选中):

clip_image002

安装完成后,进入命令窗口,输入命令:ruby –v,如果能够显示ruby的版本,则说明安装成功(安装程序已经为ruby添加了系统路径)。

clip_image004

可以看到,我安装的ruby版本为1.8.6。接下来,应该更新Gem系统,执行命令:gem update –system;然后,安装rails,rails版本为2.3.5,执行命令:gem install –v=2.3.5 rails。安装完毕后,可以执行命令rails –v检查rails的版本号。

现在,需要安装MySQL。注意安装MySQL的版本号,因为Ruby提供的针对MySQL的Adapter似乎只针对MySQL 5.0版本。我安装的版本号为5.0.18 for windows,可以在MySQL的官方网站上下载。安装过程这里不再赘述。如果要验证是否安装成功,可以在命令窗口下输入如下命令,假设用户名和密码均为root:
mysql –uroot -proot

如果安装正确,会出现如下界面:

clip_image006

接下来,还要安装ruby针对mysql的gem,我选择的gem为mysql-2.8.1-x86-mswin32.gem,下载到该文件后,假设保存到ruby的安装目录c:\ruby下,转到该目录后,执行命令:
gem install mysql-2.8.1-x86-mswin32.gem

clip_image008

系统会提示安装成功,但不知为何,再安装相关文档时,却提示了No definition错误。不过,这并不影响我们对mysql的使用。

下面,就可以在MyEclipse下安装RadRails的插件了。我选择的MyEclipse版本为8.5。安装步骤如下:

1、打开“MyEclipse”——“MyEclipse Configuration Center”

clip_image010

2、选择“Software”标签,点击左侧的“add site”:

clip_image012

弹出对话框:

clip_image014

3、输入站点名称(可任意,我输入的名称为Rails),和URL:

http://download.aptana.com/tools/radrails/plugin/install/radrails-bundle

4、此时,在左侧就会出现Personal Sites,在里面显示新添加的站点名称Rails:

clip_image016

5、点击该站点名称,系统会连接该URL,并Load数据,Load的过程可能会有1分钟左右的时间,然后就会在该站点出现Rails-Aptana RadRails树形结构,点击“Aptana RadRails”,则在屏幕右下方的Pending Changes中显示“Managed Changes:1 Change”:

clip_image018

6、点击“Apply 1 Change”,系统首先会验证MyEclipse:

clip_image020

clip_image022

然后,在弹出的“Accept Software Licenses”对话框,勾选接受,点击“Next”

clip_image024

然后,点击“Update”,系统就会执行更新:

clip_image026

这个过程会比较缓慢,MyEclipse会通过刚才输入的网站地址获取插件所需的软件。令人感到奇怪的是,在最开始的更新过程中,一切都非常正常,更新速度相对较快;但是到更新后期,就会出现错误:

clip_image028

在等待几分钟后,就会因为错误出现如下对话框:

clip_image030

此时,需要点击“Back”,回到更新对话框,然后,重新点击“Update”,它会重新进行更新(之前更新的内容会以较快速度完成,相当于断电续传),每次更新一个,然后重复出现错误提示,直到整个更新过程全部完毕。后期更新的过程可能会持续数个小时,需要耐心等待。该原因并非因为网络原因,即使网络带宽达到专享2M,仍然会出现类似问题。

更新完毕后(在我的机器上,这个过程可能持续了一到两个小时,因此在更新时需要耐心等待),会要求重新启动MyEclipse。

在安装好插件后,系统可能会提示自动安装Gem,如下图所示:

clip_image032

这些版本可能会带来一些问题,因此并不建议安装。

为了验证该插件是否安装成功,并能够与Ruby、Rails以及MySQL环境集成,需要建立一个Sample进行验证。

首先,将MyEclipse视图切换为RadRails:

clip_image034

然后,在Ruby Explorer下新建一个Rails Project:

clip_image036

在弹出的对话框中,输入项目名称Sample,并选择数据库为mysql:

clip_image038

点击Finish,系统就会自动执行Rail命令,生成Rails的相关文件夹和文件,目录结构如下所示:

clip_image040

下面,打开config目录下的database.yaml文件,修改development的设置如下(我在mysql中创建了数据库oa):

clip_image042然后,在Servers窗口下,启动Sample项目的服务器(使用WEBrick服务器),默认端口号为3000,然后,打开浏览器,输入地址:http://localhost:3000

clip_image044

点击about your application’s environment,如果能正确显示ruby等相关信息,则证明环境安装成功。

标签:

什么是好的代码

我希望能够编写优美的代码。

优美的代码就像一篇散文,易懂易读,而且看起来很漂亮。在《代码之美》一书中,收录了Ruby之父松本行宏的一篇文章,名为《把代码当作文章》,大约表达了同样的含义。Thoughtworks的一位工程师在《软件开发沉思录》一书中提出,每个类的方法最好不要超过5行。最初让我感觉很惊诧,继而觉得不可能。虽然这位工程师言之凿凿,提到在自己参与的项目中,所有代码都完全遵循了这一规范,我仍然表示怀疑。最近,阅读了Robert C. Martin的著作《代码整洁之道》(英文版名为Clean Code),看到Uncle Bob演示的代码,真是漂亮极了。仔细一看,这些好的代码在每个方法中大多数都没有超过5行。诀窍在哪里?那就是重构手法中最常用的Extract Method。进一步讲,如果我们能够为每个类与方法以及变量定义出好的名字,代码确实可以变成一篇散文。当然,是英文散文。

今天,我在重温.NET的序列化时,在MSDN上找到一篇演示Xml序列化的示范代码。或许是因为示范代码的缘故,这一段代码写得极其地不优雅,甚至显得有些丑陋:

public class Test {
    public static void Main() {
        // Read and write purchase orders.
        Test t = new Test();
        t.CreatePO("po.xml");
        t.ReadPO("po.xml");
    }

    private void CreatePO(string filename) {
        // Create an instance of the XmlSerializer class;
        // specify the type of object to serialize.
        XmlSerializer serializer =
        new XmlSerializer(typeof(PurchaseOrder));
        TextWriter writer = new StreamWriter(filename);
        PurchaseOrder po = new PurchaseOrder();

        // Create an address to ship and bill to.
        Address billAddress = new Address();
        billAddress.Name = "Teresa Atkinson";
        billAddress.Line1 = "1 Main St.";
        billAddress.City = "AnyTown";
        billAddress.State = "WA";
        billAddress.Zip = "00000";
        // Set ShipTo and BillTo to the same addressee.
        po.ShipTo = billAddress;
        po.OrderDate = System.DateTime.Now.ToLongDateString();

        // Create an OrderedItem object.
        OrderedItem i1 = new OrderedItem();
        i1.ItemName = "Widget S";
        i1.Description = "Small widget";
        i1.UnitPrice = (decimal)5.23;
        i1.Quantity = 3;
        i1.Calculate();

        // Insert the item into the array.
        OrderedItem[] items = { i1 };
        po.OrderedItems = items;
        // Calculate the total cost.
        decimal subTotal = new decimal();
        foreach (OrderedItem oi in items) {
            subTotal += oi.LineTotal;
        }
        po.SubTotal = subTotal;
        po.ShipCost = (decimal)12.51;
        po.TotalCost = po.SubTotal + po.ShipCost;
        // Serialize the purchase order, and close the TextWriter.
        serializer.Serialize(writer, po);
        writer.Close();
    }

    protected void ReadPO(string filename) {
        // Create an instance of the XmlSerializer class;
        // specify the type of object to be deserialized.
        XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));
        /* If the XML document has been altered with unknown
        nodes or attributes, handle them with the
        UnknownNode and UnknownAttribute events.*/
        serializer.UnknownNode += new
        XmlNodeEventHandler(serializer_UnknownNode);
        serializer.UnknownAttribute += new
        XmlAttributeEventHandler(serializer_UnknownAttribute);

        // A FileStream is needed to read the XML document.
        FileStream fs = new FileStream(filename, FileMode.Open);
        // Declare an object variable of the type to be deserialized.
        PurchaseOrder po;
        /* Use the Deserialize method to restore the object's state with
        data from the XML document. */
        po = (PurchaseOrder)serializer.Deserialize(fs);
        // Read the order date.
        Console.WriteLine("OrderDate: " + po.OrderDate);

        // Read the shipping address.
        Address shipTo = po.ShipTo;
        ReadAddress(shipTo, "Ship To:");
        // Read the list of ordered items.
        OrderedItem[] items = po.OrderedItems;
        Console.WriteLine("Items to be shipped:");
        foreach (OrderedItem oi in items) {
            Console.WriteLine("\t" +
            oi.ItemName + "\t" +
            oi.Description + "\t" +
            oi.UnitPrice + "\t" +
            oi.Quantity + "\t" +
            oi.LineTotal);
        }
        // Read the subtotal, shipping cost, and total cost.
        Console.WriteLine("\t\t\t\t\t Subtotal\t" + po.SubTotal);
        Console.WriteLine("\t\t\t\t\t Shipping\t" + po.ShipCost);
        Console.WriteLine("\t\t\t\t\t Total\t\t" + po.TotalCost);
    }

    protected void ReadAddress(Address a, string label) {
        // Read the fields of the Address object.
        Console.WriteLine(label);
        Console.WriteLine("\t" + a.Name);
        Console.WriteLine("\t" + a.Line1);
        Console.WriteLine("\t" + a.City);
        Console.WriteLine("\t" + a.State);
        Console.WriteLine("\t" + a.Zip);
        Console.WriteLine();
    }

    private void serializer_UnknownNode
    (object sender, XmlNodeEventArgs e) {
        Console.WriteLine("Unknown Node:" + e.Name + "\t" + e.Text);
    }

    private void serializer_UnknownAttribute
    (object sender, XmlAttributeEventArgs e) {
        System.Xml.XmlAttribute attr = e.Attr;
        Console.WriteLine("Unknown attribute " +
        attr.Name + "='" + attr.Value + "'");
    }
}

看看CreatePO()和ReadPO(),多么地冗长。虽然这个实现极为简单,但对于代码的阅读者而言,想要一下子抓住该方法的中心思想,仍然比较困难。此外,方法中的注释也显得多余,因为,代码本身就可以给予很好的说明。

下面,是我对这段代码的重构,大家可以对比对比,是否更加容易阅读呢?

    public static class PurchaseOrderHandler {
        public static void CreatePurchaseOrder(string filename) {
            PurchaseOrder po = BuildPurchaseOrder();
            XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));
            using (var writer = new StreamWriter(filename)) {
                serializer.Serialize(writer, po);
            }
        }

        private static PurchaseOrder BuildPurchaseOrder() {
            Address address = CreateAddress();
            OrderedItem i1 = CreateOrderedItem();
            OrderedItem[] items = { i1 };

            PurchaseOrder po = new PurchaseOrder();
            po.ShipTo = address;
            po.OrderDate = System.DateTime.Now.ToLongDateString();
            po.OrderedItems = items;
            po.SubTotal = CalculateSubTotal(items);
            po.ShipCost = (decimal)12.51;
            po.TotalCost = po.SubTotal + po.ShipCost;

            return po;
        }

        private static decimal CalculateSubTotal(OrderedItem[] items) {
            decimal subTotal = new decimal();
            foreach (OrderedItem oi in items) {
                subTotal += oi.LineTotal;
            }

            return subTotal;
        }


        private static OrderedItem CreateOrderedItem() {
            OrderedItem i1 = new OrderedItem();
            i1.ItemName = "Widget S";
            i1.Description = "Small widget";
            i1.UnitPrice = (decimal)5.23;
            i1.Quantity = 3;
            i1.Calculate();
            return i1;
        }

        private static Address CreateAddress() {
            Address billAddress = new Address();
            billAddress.Name = "Bruce Zhang";
            billAddress.Line1 = "1 Main St.";
            billAddress.City = "Chong Qing";
            billAddress.State = "Chong Qing";
            billAddress.Zip = "400000";

            return billAddress;
        }

        public static void ReadPurchaseOrder(string filename) {
            XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));

            serializer.UnknownNode += new XmlNodeEventHandler(serializer_UnknownNode);
            serializer.UnknownAttribute += new XmlAttributeEventHandler(serializer_UnknownAttribute);

            FileStream fs = new FileStream(filename, FileMode.Open);

            PurchaseOrder po;
            po = (PurchaseOrder)serializer.Deserialize(fs);
            PurchaseOrderPrinter.PrintPurchaseOrder(po);
        }


        private static void serializer_UnknownNode
        (object sender, XmlNodeEventArgs e) {
            Console.WriteLine("Unknown Node:" + e.Name + "\t" + e.Text);
        }

        private static void serializer_UnknownAttribute
        (object sender, XmlAttributeEventArgs e) {
            System.Xml.XmlAttribute attr = e.Attr;
            Console.WriteLine("Unknown attribute " +
            attr.Name + "='" + attr.Value + "'");
        }

        private static class PurchaseOrderPrinter {
            public static void PrintPurchaseOrder(PurchaseOrder po) {
                PrintOrderDate(po);
                PrintAddress(po.ShipTo);
                PrintOrderedItem(po.OrderedItems);
                PrintOrderCost(po);
            }

            private static void PrintOrderCost(PurchaseOrder po) {
                Console.WriteLine("\t\t\t\t\t Subtotal\t" + po.SubTotal);
                Console.WriteLine("\t\t\t\t\t Shipping\t" + po.ShipCost);
                Console.WriteLine("\t\t\t\t\t Total\t\t" + po.TotalCost);
            }

            private static void PrintOrderDate(PurchaseOrder po) {
                Console.WriteLine("OrderDate: " + po.OrderDate);
            }

            private static void PrintOrderedItem(OrderedItem[] items) {
                Console.WriteLine("Items to be shipped:");
                foreach (OrderedItem oi in items) {
                    Console.WriteLine("\t" +
                    oi.ItemName + "\t" +
                    oi.Description + "\t" +
                    oi.UnitPrice + "\t" +
                    oi.Quantity + "\t" +
                    oi.LineTotal);
                }
            }

            private static void PrintAddress(Address a) {
                // Read the fields of the Address object.
                Console.WriteLine("Ship To:");
                Console.WriteLine("\t" + a.Name);
                Console.WriteLine("\t" + a.Line1);
                Console.WriteLine("\t" + a.City);
                Console.WriteLine("\t" + a.State);
                Console.WriteLine("\t" + a.Zip);
                Console.WriteLine();
            }
        }
    }

阅读代码时,我们可以先关注最主要的方法,即CreatePurchaseOrder()和ReadPurchaseOrder()方法。如果并不希望了解过多构造PO对象的细节,通过阅读这样简短的方法,可以很容易地抓住这两个方法的实现,那就是通过构建一个PO对象,进行序列化,而在反序列化时,将获得的PO对象信息打印出来。

其实,糟糕的代码不一定就是初学者的“专利”,让我们看看NHibernate中的一段代码:

public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
{
    Init();
    log.Info("building session factory");

    properties = new Dictionary<string, string>(cfg.Properties);
    interceptor = cfg.Interceptor;
    this.settings = settings;
    sqlFunctionRegistry = new SQLFunctionRegistry(settings.Dialect, cfg.SqlFunctions);
    eventListeners = listeners;
    filters = new Dictionary<string, FilterDefinition>(cfg.FilterDefinitions);
    if (log.IsDebugEnabled)
    {
        log.Debug("Session factory constructed with filter configurations : " + CollectionPrinter.ToString(filters));
    }

    if (log.IsDebugEnabled)
    {
        log.Debug("instantiating session factory with properties: " + CollectionPrinter.ToString(properties));
    }

    try
    {
        if (settings.IsKeywordsImportEnabled)
        {
            SchemaMetadataUpdater.Update(this);
        }
        if (settings.IsAutoQuoteEnabled)
        {
            SchemaMetadataUpdater.QuoteTableAndColumns(cfg);
        }
    }
    catch (NotSupportedException)
    {
        // Ignore if the Dialect does not provide DataBaseSchema
    }

    #region Caches
    settings.CacheProvider.Start(properties);
    #endregion

    #region Generators
    identifierGenerators = new Dictionary<string, IIdentifierGenerator>();
    foreach (PersistentClass model in cfg.ClassMappings)
    {
        if (!model.IsInherited)
        {
            IIdentifierGenerator generator =
                model.Identifier.CreateIdentifierGenerator(settings.Dialect, settings.DefaultCatalogName,
                                                           settings.DefaultSchemaName, (RootClass) model);

            identifierGenerators[model.EntityName] = generator;
        }
    }
    #endregion

    #region Persisters

    Dictionary<string, ICacheConcurrencyStrategy> caches = new Dictionary<string, ICacheConcurrencyStrategy>();
    entityPersisters = new Dictionary<string, IEntityPersister>();
    implementorToEntityName = new Dictionary<System.Type, string>();

    Dictionary<string, IClassMetadata> classMeta = new Dictionary<string, IClassMetadata>();

    foreach (PersistentClass model in cfg.ClassMappings)
    {
        model.PrepareTemporaryTables(mapping, settings.Dialect);
        string cacheRegion = model.RootClazz.CacheRegionName;
        ICacheConcurrencyStrategy cache;
        if (!caches.TryGetValue(cacheRegion, out cache))
        {
            cache =
                CacheFactory.CreateCache(model.CacheConcurrencyStrategy, cacheRegion, model.IsMutable, settings, properties);
            if (cache != null)
            {
                caches.Add(cacheRegion, cache);
                allCacheRegions.Add(cache.RegionName, cache.Cache);
            }
        }
        IEntityPersister cp = PersisterFactory.CreateClassPersister(model, cache, this, mapping);
        entityPersisters[model.EntityName] = cp;
        classMeta[model.EntityName] = cp.ClassMetadata;

        if (model.HasPocoRepresentation)
        {
            implementorToEntityName[model.MappedClass] = model.EntityName;
        }
    }
    classMetadata = new UnmodifiableDictionary<string, IClassMetadata>(classMeta);

    Dictionary<string, ISet<string>> tmpEntityToCollectionRoleMap = new Dictionary<string, ISet<string>>();
    collectionPersisters = new Dictionary<string, ICollectionPersister>();
    foreach (Mapping.Collection model in cfg.CollectionMappings)
    {
        ICacheConcurrencyStrategy cache =
            CacheFactory.CreateCache(model.CacheConcurrencyStrategy, model.CacheRegionName, model.Owner.IsMutable, settings,
                                     properties);
        if (cache != null)
        {
            allCacheRegions[cache.RegionName] = cache.Cache;
        }
        ICollectionPersister persister = PersisterFactory.CreateCollectionPersister(cfg, model, cache, this);
        collectionPersisters[model.Role] = persister;
        IType indexType = persister.IndexType;
        if (indexType != null && indexType.IsAssociationType && !indexType.IsAnyType)
        {
            string entityName = ((IAssociationType) indexType).GetAssociatedEntityName(this);
            ISet<string> roles;
            if (!tmpEntityToCollectionRoleMap.TryGetValue(entityName, out roles))
            {
                roles = new HashedSet<string>();
                tmpEntityToCollectionRoleMap[entityName] = roles;
            }
            roles.Add(persister.Role);
        }
        IType elementType = persister.ElementType;
        if (elementType.IsAssociationType && !elementType.IsAnyType)
        {
            string entityName = ((IAssociationType) elementType).GetAssociatedEntityName(this);
            ISet<string> roles;
            if (!tmpEntityToCollectionRoleMap.TryGetValue(entityName, out roles))
            {
                roles = new HashedSet<string>();
                tmpEntityToCollectionRoleMap[entityName] = roles;
            }
            roles.Add(persister.Role);
        }
    }
    Dictionary<string, ICollectionMetadata> tmpcollectionMetadata = new Dictionary<string, ICollectionMetadata>(collectionPersisters.Count);
    foreach (KeyValuePair<string, ICollectionPersister> collectionPersister in collectionPersisters)
    {
        tmpcollectionMetadata.Add(collectionPersister.Key, collectionPersister.Value.CollectionMetadata);
    }
    collectionMetadata = new UnmodifiableDictionary<string, ICollectionMetadata>(tmpcollectionMetadata);
    collectionRolesByEntityParticipant = new UnmodifiableDictionary<string, ISet<string>>(tmpEntityToCollectionRoleMap);
    #endregion

    #region Named Queries
    namedQueries = new Dictionary<string, NamedQueryDefinition>(cfg.NamedQueries);
    namedSqlQueries = new Dictionary<string, NamedSQLQueryDefinition>(cfg.NamedSQLQueries);
    sqlResultSetMappings = new Dictionary<string, ResultSetMappingDefinition>(cfg.SqlResultSetMappings);
    #endregion

    imports = new Dictionary<string, string>(cfg.Imports);

    #region after *all* persisters and named queries are registered
    foreach (IEntityPersister persister in entityPersisters.Values)
    {
        persister.PostInstantiate();
    }
    foreach (ICollectionPersister persister in collectionPersisters.Values)
    {
        persister.PostInstantiate();
    }
    #endregion

    #region Serialization info

    name = settings.SessionFactoryName;
    try
    {
        uuid = (string) UuidGenerator.Generate(null, null);
    }
    catch (Exception)
    {
        throw new AssertionFailure("Could not generate UUID");
    }

    SessionFactoryObjectFactory.AddInstance(uuid, name, this, properties);

    #endregion

    log.Debug("Instantiated session factory");

    #region Schema management
    if (settings.IsAutoCreateSchema)
    {
        new SchemaExport(cfg).Create(false, true);
    }

    if ( settings.IsAutoUpdateSchema )
    {
        new SchemaUpdate(cfg).Execute(false, true);
    }
    if (settings.IsAutoValidateSchema)
    {
         new SchemaValidator(cfg, settings).Validate();
    }
    if (settings.IsAutoDropSchema)
    {
        schemaExport = new SchemaExport(cfg);
    }
    #endregion

    #region Obtaining TransactionManager
    // not ported yet
    #endregion

    currentSessionContext = BuildCurrentSessionContext();

    if (settings.IsQueryCacheEnabled)
    {
        updateTimestampsCache = new UpdateTimestampsCache(settings, properties);
        queryCache = settings.QueryCacheFactory.GetQueryCache(null, updateTimestampsCache, settings, properties);
        queryCaches = new ThreadSafeDictionary<string, IQueryCache>(new Dictionary<string, IQueryCache>());
    }
    else
    {
        updateTimestampsCache = null;
        queryCache = null;
        queryCaches = null;
    }

    #region Checking for named queries
    if (settings.IsNamedQueryStartupCheckingEnabled)
    {
        IDictionary<string, HibernateException> errors = CheckNamedQueries();
        if (errors.Count > 0)
        {
            StringBuilder failingQueries = new StringBuilder("Errors in named queries: ");
            foreach (KeyValuePair<string, HibernateException> pair in errors)
            {
                failingQueries.Append('{').Append(pair.Key).Append('}');
                log.Error("Error in named query: " + pair.Key, pair.Value);
            }
            throw new HibernateException(failingQueries.ToString());
        }
    }
    #endregion

    Statistics.IsStatisticsEnabled = settings.IsStatisticsEnabled;

    // EntityNotFoundDelegate
    IEntityNotFoundDelegate enfd = cfg.EntityNotFoundDelegate;
    if (enfd == null)
    {
        enfd = new DefaultEntityNotFoundDelegate();
    }
    entityNotFoundDelegate = enfd;
}

这是类SessionFactoryImpl(它实现了ISessionFactoryImplementor接口)的构造函数,其目的时是通过Configuration以及Setting中的某些值,去初始化SessionFactoryImpl,然后构建该类的对象。坦白说,我从来没有看过如此“浩瀚无垠”的构造函数。幸好,Visual Studio提高了Region,否则,更让人头疼。(我在想,既然代码的编写者已经利用了Region来分割实现,为何不进一步将其分割为小的方法呢?)

看这样的代码,我们能够轻易读懂吗?

拙劣代码可谓遗患无穷。在《程序员修炼之道》一书中,提到了所谓“破窗效应”,即“没修复的破窗,导致更多的窗户被打破”。丑陋的代码如果只有一个小的片段,看似无关紧要,就像一幢大楼的一扇破窗一般容易让人忘记。随着时间的推移,当这些丑陋代码不知不觉蔓延到整个项目中时,我们才发现这一幢大楼已经满目疮痍了。“一屋不扫,何以扫天下”,程序员应该从小处着手,未来才可能写出优雅的代码。

标签:

WCF 4.0中的WS-Discovery

在WS-*标准和规范中,WS-Discovery是在2008年才加入了OASIS标准。WS-Discovery在标准被定义为Web Service Dynamic Discovery,其目的是为定位服务定义Discovery协议,主要应用在为客户端动态搜索一个或多个目标服务。OASIS为WS-Discovery提供了两种操作模式:ad hoc和managed模式。

ad hoc模式根据类型在托管目标服务的范围内查找目标服务。客户端会以多播的形式发送一个Probe(探测)消息,如果服务匹配该信息,则以单播方式直接将响应发送到客户端。为了能够根据名称定位目标服务,客户端会以相同的多播组发送一个Resolve(解析)消息,同样的,匹配该消息的服务会直接以单播方式响应客户端。消息交换的流程如下图所示:

More...

标签:

使用扩展方法对调用进行验证

利用C# 3.0提供的扩展方法技术,可以为已经编译好的程序集类型增加新的方法,从而应对新的扩展。除了在可扩展性方面所具有的优势之外,如果能够合理地结合泛型与类型推断,扩展方法还可以有效降低代码的重复,提高程序的可重用性。例如,这样的方法实现:

public class CustomerDAL

{

    public IEnumerable<Customer> FindCustomers(string roleName)

    {

        return from customer

            in context.Customer

               where customer.RoleName.Equals(roleName)

               select customer;

    }

}

More...

标签:

.Net编码技巧与资源

说明:本文收录.NET编码的诸多有用技巧和资源,以备项目开发中查询所用。本文内容部分来源于网络,且内容将不断更新。

1、如何获得国家名

2、读写INI配置文件

3、将字符串转换为长整型

4、获得机器的IP地址

5、Url重写

More...

标签:

开发WCF/Silverlight须知

ByteBlocks的博客文章中总结了开发WCF/Silverlight的注意事项,这样的经验之谈字字千钧,可以让后来的开发者少走许多弯路。

绑定的选择

毫无疑问,我们应该选择BasicHttpBinding,这也是Silverlight仅仅支持的一种绑定。

More...

标签:

WF基础知识问答

Shivprasad koirala在CodeProject上发表了一篇文章Windows Workflow Foundation FAQ,介绍了WF的基础知识。这对于理清WF的整个脉络有一定帮助,摘译如下。

什么是Windows工作流基础?

WWF(张逸注:微软的官方简称为WF)是一种编程模型,用于在Windows中构建支持工作流的应用程序。WF程序集的命名空间为System.Workflow。

More...

标签:

WCF中的Dispose

在我翻译的InfoQ新闻《WCF的问题和Using语句块》中提到了释放客户端资源(其中包括端口、通道)和关闭连接的问题。新闻并没有很深入地讨论,所以我想再补充一些内容。

毫 无疑问,在.NET Framework中,一个资源(尤其是非托管资源)通常都需要实现IDisposable接口。一旦实现了该接口,我们就可以使用using语句来管理 资源,这是最便捷的方式。但是,一旦在using语句中抛出了异常,就可能不会正确完成资源的回收,尤其是连接,很可能会一直打开,既占用了通道和端口, 还可能出现资源的浪费,从而影响系统的性能和稳定性。

More...

标签:

聚焦WCF行为的扩展

WCF以其灵活的可扩展架构为开发者提供了方便,其中对行为的扩展或许是应用中最为常见的。自 定义对行为的扩展并不复杂,但仍有许多细节需要注意。在服务端,一般是对DispatchRuntime和DispatchOperation进行扩展, 扩展点包括了对参数和消息的检查,以及操作调用程序,它们对应的接口分别为 IParameterInspector,IDispatchMessageInspector以及IOperationInvoker。而在客户端,则 是对ClientRuntime和ClientOperation进行扩展,扩展点包括对参数和消息的检查,对应的接口分别为 IParameterInspector和IClientMessageInspector。这些接口类型均被定义在 System.ServiceModel.Dispatcher命名空间下,其中IParameterInspector接口可以同时作用在服务端和客户端。

More...

标签:

分页:«123»