.NET 學習

.NET 學習生活感想... 万事成蹉跎..... 贵在坚持 及时整理自己做过和学过的东西

博客园 首页 新随笔 联系 订阅 管理

原文:http://apobekiaris.blogspot.com/2011/01/easy-sequential-numbers.html

Easy Sequential numbers(易用的序列号)

Where they can be used?? In many places for example invoices, sales orders etc.
他们能用在哪里?例如,在许多地方,发票,销售订单等等。

One could tell that this is an easy task and maybe that assumption is right but let me write down some real world requirements that can prove the opposite to you.
有人告诉你这个任务很简单,也许那是正确的,但,让我们写一个些真正的需求,来证明那是错误的。

  1. Invoices Number Uniqueness should be quarantine in a multi user environment
    在一个多用户环境,独一无二的发票号应当是隔离的。
  2. Number number should be sequential
    数字号应当是连续的。
  3. Invoices numbers may have series (eg AB-1001 for invoices coming from store sales , EP-1001 for invoices coming from patient treatment)
    发票号,可能是个系列号(例如AB-1001 来自店面销售发票,EP-1001 来自住院发票)
  4. Starting number must be configurable. (eg need to start numbering at 5000 this year, and next year start at 7000)
    开始号必须能配置(例如,今年需要从5000开始,下一年从7000开始)
  5. There should be a way to reuse numbers of deleted invoices
    应当有一种方法可以重用删除的发票号。
  6. Storage (table schema) of invoice numbers should be controlled by the user
    发票号的存储(表配置)应当由用户控制。
  7. End user API should be very flexible and easy
    终端用户应用程序接口应当非常灵活和易用。
  8. Of course whatever you build has to be available in both win and web platforms
    当然,无论如何,你必须建立能在winweb两种平台上可用。

Now what you think? How much time do you need to build that?
现在,你在思考什么?你需要多少时间才能建立那些功能?

If you have some XAF experience it will be about only some hrs work to create a reusable code that fellow devs and future projects can utilize. So in this post i am going to go through the process step by step
如果你有一些的XAF开发经验,这就需要几个小时就可以建立一个可重用的代码,供开发者和将来的工程使用。因此,在本篇,我将循序渐进的说明如何实现这些。

Step1: Check Devexpress support center
第一步:查看Devexpress支持中心

SC has samples for thousand of cases so before building anything it is most advisable to check there.
支持中心有成千的案例,因此,在构建任何程序之前,最好去那里查看。

Searching revealed this one (这是一个搜索结果)
How to generate and assign a sequential number for a business object within a database transaction, while being a part of a successful saving process (XAF)

在一个数据库事务中如何给业务对象生成和标识一个序列号,当成功存储时(XAF)
That sample is using ExcplicitUnitOfWork (in other words explicit sql transaction) and that can guarantee requirement 1,2
那个例子用了ExcplicitUnitOfWork(换句话说,明确的sql事务),那能保证实现需求12

Step2: Refactor the sample to fit your needs
第二步:重构例子适应你的需求

Although the sample can be used as is in real world, has some requirements that are making it less flexible. It requires to inherit from a special BasePersistentObject class.
虽然例子可以在现实中用,但有一些需求,构建它是不太灵活。它需要继承一个指定类BasePersistentObject

To overcome that we can introduce an interface instead and push all depended code to our SequenceGenerator class
要克服那些问题,我们引入一个接口替代,将所有依赖代码放入我们的SequenceGenerator类。

    public interface ISupportSequenceObject {

        long Sequence { get; set; }

    }

Now we can inherit from any object as you see in the above code and we only have to write the code that is inside the OnSaving method! .

 现在,你在上面代码看到,我们可以从任何对象继承,我们只需在OnSaving方法写代码。

The best part is that we do not even spent one minute to think about the validity of the generating seq numbers code. That is Devexpress job and they are best in doing it. What we could do is just track the issue for changes (maybe bugs will raise from other users in future )
最好是我们花不到一分钟的时间来考虑生成序列号码的有效性。那是Devexpress最好要做的工作。我们只需跟踪更改的结果(可能来自未来其他用户的提升)

I am happy with this refactoring so lets move on to the other requirements.
我很高兴这样重构,这样让我关注其他需求。

Time spent 1.5hrs (花费1.5小时)

 

Invoices numbers may have series(可能有系列的发票号)

How about it?? Should be very easy if we understand what are SequenceGenerator does. It saves a number for a specific type in the database. So instead of a specific type we can refactor it to save a specific type and a series string. We can do that by introducing a new Prefix property to our  ISupportSequenceObject interface and refactor our SequenceGenerator to save that prefix as well .
怎么样?如果我们理解了SequenceGenerator能做什么,应当很容易。它保存一个特定类型的号码在数据库中。因此不是一个特定类型,我们重构它,保存一个特定类型和一个系列字符串。我们通过ISupportSequenceObject接口引入一个新的前缀属性,重构我的SequenceGenerator同样保存那个前缀

 

    public interface ISupportSequenceObject {

        long Sequence { get; set; }

        string Prefix { get; }

    }

 

our previous front end API was left as simple as it was b4 as you see bellow

 

Starting number must be configurable. (eg need to start numbering at 5000 this year, and next year start at 7000)
必须能配置开始号码。(例如,今年需要从5000开始,明年从7000开始)

Ok that sounds very similar to our previous req. In essence the year is just a serie, so we could just change our class to
好的,听起来跟以前的需求很相似。本质上今年只是一个系列,因此,我们仅更改我们的类。

        string ISupportSequenceObject.Prefix {

            get { return Serie.ToString()+DateTime.Today.Year; }

        }

and now our numbers are unique per serier+year!. And how can we control the starting number?

Very easy as everything in XAF!. Sequence numbers are saved using a persistent object, so the thing we need to do is just create a new Sequence number manual and the other numbers will follow since they are sequential.
现在,我们的数字,每个系列+年是唯一的。如何控制开始数字?在XAF中是非常容易的。序列号是用持久对象保存,因此这需要手动创建一个新的序列号,其他数字将由序列号生成。

            var unitOfWork = new UnitOfWork();

            var sequenceObjects = Enum.GetValues(typeof (SerieEnum)).Cast<SerieEnum>().Select(

                serie => SequenceGenerator.CreateSequenceObject(serie.ToString() + 2012, unitOfWork));

            foreach (var sequenceObject in sequenceObjects) {

                sequenceObject.NextSequence = 7000;

            }

            unitOfWork.CommitChanges();

Time spent 30min (用时30分钟)

There should be a way to reuse numbers of deleted invoices
应当有方法重用删除的发票号

 

What does that mean? When an object that supports sequences (ISupportSequence object) is deleted we need to store the deleted sequence number and allow end user through UI to reuse it at a later time.
什么意思?当删除一个到序列号的对象,我们需要存储删除的序列号,允许终端用户稍后通过界面重用它。

 

We already have an SequenceObject that stores the sequence number what we miss is an one to many relation with an object that stores deleted numbers. The following class will do the job
我们已经有一个SwquenceObject,存储一个一对多对象的已删除的序列号。下面类将处理这项工作。

 

    public class SequenceReleasedObject : XpandBaseCustomObject {

        public SequenceReleasedObject(Session session)

            : base(session) {

        }

        private SequenceObject _sequenceObject;

        public SequenceObject SequenceObject {

            get {

                return _sequenceObject;

            }

            set {

                SetPropertyValue("SequenceObject", ref _sequenceObject, value);

            }

        }

        private long _sequence;

        public long Sequence {

            get {

                return _sequence;

            }

            set {

                SetPropertyValue("Sequence", ref _sequence, value);

            }

        }

    }

 

Having the storage object we then need to grad the deleted ISupportSequence object and create a new SequenceReleasedObject
有了存储对象,然后,我们需要研究已删除的ISupportSequence对象并创建一个新的SequenceReleasedObject.

 

    public class Invoice : BaseObject, ISupportSequenceObject {

        public Invoice(Session session) : base(session) {

        }

        protected override void OnDeleted() {

            base.OnDeleted();

            SequenceGenerator.ReleaseSequence(this);

        }

 

Now that we have our data stored in the database, we are going to use XAF to allow user to restore a deleted number.
现在,在数据库中,我们有我们的数据存储,我们将用XAF,允许用户恢复一个已经删除的号码。

 

I am thinking of a control (PropertyEditor in XAF terms) that could be used to render the sequence property of the ISupportSequence object. Also that control should have a button to the right, that on click is going to display a SequenceReleasedObject list . User will select something from the list and when the transaction is committed the SequenceReleasedObject value will replace ISupportSequenceObject value and will be deleted.
我想到一个控件(XAF中的属性编辑控件),能用了显示ISupportSequence对象的序列号属性。此外那个控件右边应当有一个按钮,当点击它时,将显示一个SequenceReleasedObject列表。用户将从列表中选择一些,当SequenceReleasedObject的值替换ISupportSequenceObject的值,这个事务提交后,对应的序列号将从列表中删除。

 

Sounds hard to you? In fact it is not so much. You have to remember to use the tools that XAF provides for you. Here is what I mean . XAF already has a similar property editor to render aggregated object properties
听起来比较难,实际上没那么困难,你必须记得用XAF给你提供的工具。这是我的意思。XAF已经有了一个相似的熟悉编辑,显示聚集对象属性。

When editor's button is clicked then a detailview of the aggregated object is shown. We could just use the same editor and replace that part. Instead of displaying a detailview we could just display a listview of SequenceReleaseObjects. Exactly the same process we could follow for the web platform.

当点击编辑器按钮,将显示一个聚集对象的详细视图。我们刚好用同样的编辑器,更换一部分。而不是显示一个详细视图,我们只需显示一个SequenceReleaseObjects的列表。很明显,对于Web平台,同样的处理我们可以效仿。

Now we have our property editors, but lets make dev job even more easier. Lets create a platform independent marker attribute that will handle the property editor type assignment.

 现在,我们有我们的属性编辑器,但,让我们是开发者的工作更容易。让我们创建一个平台,依赖一个标记特性,处理属性编辑器类型任务。

First we create a marker attribute like
首先,我们创建一个像这样的特性标记

    [AttributeUsage(AttributeTargets.Property)]

    public class SequencePropertyAttribute : Attribute {

    }

and a marker interface that will be implemented by both our editors
一个标记接口,通过我的编辑器实施

    public interface IReleasedSequencePropertyEditor {

    }

 

write a simple controller that will do the assignment as
写一个简单的控件,处理这个任务

    public class CustomAttibutesController : WindowController {

        public override void CustomizeTypesInfo(ITypesInfo typesInfo) {

            base.CustomizeTypesInfo(typesInfo);

            var memberInfos = typesInfo.PersistentTypes.SelectMany(info => info.OwnMembers);

            foreach (var memberInfo in memberInfos) {

                HandleSequencePropertyAttribute(memberInfo);

            }

        }

 

        void HandleSequencePropertyAttribute(IMemberInfo memberInfo) {

            var sequencePropertyAttribute = memberInfo.FindAttribute<SequencePropertyAttribute>();

            if (sequencePropertyAttribute != null) {

                var typeInfo = ReflectionHelper.FindTypeDescendants(XafTypesInfo.Instance.FindTypeInfo(typeof(IReleasedSequencePropertyEditor))).Single();

                memberInfo.AddAttribute(new CustomAttribute("PropertyEditorType", typeInfo.FullName));

            }

        }

    }

and of course decorate our property
当然,要修饰我们的属性

 

private long _sequence;

        [SequenceProperty]

        public long Sequence {

            get {

                return _sequence;

            }

            set {

                SetPropertyValue("Sequence", ref _sequence, value);

            }

        }

Time spent 3hr (用时3小时)

Storage (table schema) of invoice numbers should be controlled by the user
发票号码存储(表配置),应当由用户控制

Does the above remind you anything? It sure does to me. Reminds me the exact same problem we have with the Devexpress support center sample . It was not based on interfaces so if we extract an interface from our Sequence class (the one that store the numbers in the db) like
上面提醒你什么没?确实提醒了我。提醒我完全相同的问题,我们有Devexpress 支持中心实例。它没有基于接口,因此,如果我们从我们的序列号类中提取一个接口(在数据库中存储序列号)

    public interface ISequenceObject {

        string TypeName { get; set; }

        long NextSequence { get; set; }

    }

and refactor again our SequenceGenerator to replace our references to SequenceObject persistent class with the new interface. Now the end user can control the schema of the table cause the only thing he has to do is implement the ISequenceObject to any object he wants.

 再次重构我们的SequenceGenerator替换我们引用的SequenceObject持久化新接口。现在,终端用户能控制表配置,唯一要做的是给想要的任何对象实现ISequeceObject接口。

Time spent 1hr(用时1小时)

 

Conclusion(总结)

We have spent almost 6 hrs to implement all requirements but nothing was in vain. Cause the effort produce something very reusable. Bellow is our final, very easy to use and flexible approach.
我们实现了所有需求用了将近6个小时,但没有白费。因为努力产出了一些可重复使用的东西。大吼一声,我们结束了,非常易用和灵活的方法。

    public class Invoice : BaseObject, ISupportSequenceObject {

        public Invoice(Session session) : base(session) {

        }

        protected override void OnDeleted() {

            base.OnDeleted();

            SequenceGenerator.ReleaseSequence(this);

        }

 

        protected override void OnSaving() {

            base.OnSaving();

            if (Session.IsNewObject(this))

                Sequence = (int)SequenceGenerator.GenerateSequence(this);

 

        }

 

 

        private long _sequence;

        [SequenceProperty]

        public long Sequence {

            get {

                return _sequence;

            }

            set {

                SetPropertyValue("Sequence", ref _sequence, value);

            }

        }

        private SerieEnum _serie;

        public SerieEnum Serie {

            get {

                return _serie;

            }

            set {

                SetPropertyValue("Serie", ref _serie, value);

            }

        }

 

        string ISupportSequenceObject.Prefix {

            get { return Serie.ToString()+DateTime.Today.Year; }

        }

    }

 

that is the power of XAF! everything else should live in a reusable framework, like Xpand !
这些都归功于强大的XAF!一个一切都可以重用的框架,像Xpand

 

欢迎转载,转载请注明出处:http://www.cnblogs.com/Tonyyang/

posted on 2011-01-27 09:13  Tonyyang  阅读(1230)  评论(0编辑  收藏  举报
欢迎转载,转载请注明出处:http://www.cnblogs.com/Tonyyang/