Participation in projects of migration from FoxPro to .Net:
Visual FoxPro was one of the best in the world development framework. But the time of FoxPro is over. Microsoft stopped development of Visual FoxPro in 2007 and stopped supporting in 2015. We have a big experience in creating solutions as in Visual FoxPro and as in .Net, and we can rewrite your projects to c# maximum effectively.
If you need to save behavior of your application maximum close to current, when migrating to .Net, we can pre-convert code, classes and screens. Pre-conversion is a possible first step of migration of VFP project to .NET and allows to:
As a result of pre-conversion you have got a buildable .NET-project. Next steps are manual layout correction and code rewriting to make project components alive step-by-step.
There is no universal way or pre-converter to pre-convert any project. There are a lot of styles of programming in VFP and we create individual solution for each project. But this solution is based on the set of common technologies we use.
Moving structure of classes.
Class-tree is similar to visual-tree in Visual FoxPro. But they are different (class-tree and visual-tree) in c# and WPF. Class-tree is a property of c#-language, whereas visual-tree is a property of WPF or Silverlight technology. So a process of moving of class-visual-tree from VFP to .NET is not ordinary. But we can do it. We use XAML format only for controls which defined directly in forms. Controls created as members of containers we declare and initialize in c# code of appropriate classes.
First pre-converter scans class-tree and creates the same class-tree in c# project. Then it scans properties of each class and each member of class and adds member-declaration to appropriate class in c#. After this pre-converter analyzes members-controls and adds each control to children-collection of UIElements of parent container and does it in constructor of current class. Also, it adds commented VFP definitions of properties near their c#-definitions.
Each control, inserted into appropriate container directly in current class, receives the name which equals to full path of object in visual-tree where dots are replaced with '_'. For example:
public class Class1 : Grid
{
public Grid Container1 = new Grid();
public Grid Container1_Container2 = new Grid();
public TextBox Container1_Container2_Text1 = new TextBox();
public Class1()
{
Children.Add(Container1);
Container1.Children.Add(Container1_Container2);
Container1_Container2.Children.Add(Container1_Container2_Text1);
So, Container1_Container2_Text1 is a member of Class1 class, but it appears to be the parent in visual-tree container if Container1_Container2, and Container1_Container2_Text1 is included into Container1_Container2.Children collection.
All methods of VFP-Text1 control become methods of Class1 and receive names of control + '_' + method like VS.NET does when generates event handlers. And you can't refer with "this"-keyword (or simply by member name) to Text1 from such methods, but you must add control name Container1_Container2_Text1. into references implicitly. Actually, we lose event-handler-assigning, and you have to restore event-handlers step-by-step with parameters, types and code of methods.
Pre-converter repeats a structure of directories of VFP project in .NET solution and adds its own directory.
Namespaces structure pre-converter generates equal to structure of directories in VFP project.
For each VCX-file pre-converter generates cs file.
For each PRG-file it generates cs-file. All class-definitions of PRG-file become class-definitions in cs-file. All procedures and functions of PRG-file, which aren't methods of classes, become methods of new static-class with name of PRG-file.
For each SCX-file pre-converter creates three files (like in MVVM):
For example:
PAYBYDATE.xaml lines
PAYBYDATE.xaml.cs lines
PAYBYDATE_VM.cs file in case when IdeaBlade Entity Framework used
Moving controls layout.
Pre-converter copies VFP controls into WPF with using of following mapping:
VFP class | WPF type | ----- | VFP class | WPF type | ----- | VFP class | WPF type |
CheckBox | CheckBox | ----- | Collection | Grid | ----- | ComboBox | ComboBox |
CommandButton | Button | ----- | CommandGroup | Grid | ----- | Container | Grid |
Control | UserControl | ----- | EditBox | TextBox | ----- | Form | BaseWin(our class) |
Grid | DataGrid | ----- | Hyperlink | Button | ----- | Image | Image |
Label | Label | ----- | Line | System.Windows.Shapes.Line | ----- | ListBox | ListBox |
OptionGroup | Grid | ----- | OptionButton | RadioButton | ----- | Page | TabItem |
PageFrame | TabControl | ----- | Shape | Border | ----- | Spinner | Spinner(our class) |
TextBox | TextBox | ----- | Timer | System.Timers.Timer | ----- | ToolBar | ToolBar |
And copies properties of controls as:
VFP control property | WPF class member | ----- | VFP control property | WPF class member |
Left | Margin( | ----- | Top | Margin(, |
Width | Width | ----- | Height | Height |
Visible | Visibility | ----- | Caption | Content or Header |
ForeColor | Foreground | ----- | BackColor | Background |
ControlSource | "{Binding... |
All other properties pre-converter moves as commented code into cs-file and places close to control definition.
As XAML allows placing controls only inside containers in visual-tree (not as members inside classes like VFP dynamically), we use XAML format only for controls which are defined directly in forms and which are placed in containers defined in XAML. Controls defined as members of other classes, pre-converter declares and initializes in c# code of appropriate classes. But in cases when controls placed inside Pages of PageFrame or children classes - we also define such controls inside pages in XAML code.
VFP code copying and commenting.
In VFP we use class-browser to view code of parent class. But we can't load and edit parent classes while children classes are still loaded into editors. Moreover, VFP doesn't provide a convenient way to navigate to definitions of objects, variables, classes .. like VS.NET provides.
If you plan to move code step-by-step and to rewrite it immediately, then you will spend a lot of time looking for needed code in VFP project.
Our pre-converters copy VFP code into appropriate method of appropriate C# class and does it for all methods of pre-converted classes and forms and for all property-definitions.
For example:
And now you can make your code alive step-by-step without scanning of VFP-project. VS.NET provides very powerful definition-navigation. Just right-click any class or object in code and select "Go to definition". VS will find out and open found definition wherever it is.
Views conversion
Pre-converter creates *_VM.cs file for each form. It scans DataEnvironment of form and finds cursor-objects. For each cursor object pre-converter finds appropriate view in DBC. Next steps depend on data-access-model you selected for your .NET project. Data access model can be:
In the second and third cases we create data-entity model before pre-conversion. Pre-converter finds entity-type with name equal to the cursor. When appropriate entity type is declared, pre-converter doesn't define additional types. But if there is no appropriate entity-type then pre-converter creates type with structure of view and places declaration into DBC.cs file. For example:
In the first case (when WCF-level access selected) these definitions will have [DataContract] directive, and you can copy such declaration into your WCF-Service.
After class for next cursor is defined or found in entity-types, pre-converter adds cursor declaration into *_VM.cs file of form as OvservableCollection. For example:
We don't use pre-conversion of the whole project if some parts of the project have become not actual, or if structure of classes must be optimized due to other reasons.
A common process of migration consists of the following steps:
C#.NET and VFP very differ. If you haven't got C# + .NET programmers, your FoxPro-programmers have to learn a lot of absolutely new and hard things. They need time to do it. And they need much time to do it perfectly and to reach the best performance and effectivity. We can do it faster, and your programmers could study new technologies using our decisions as an example.
Pre-conversion can speed up a process of migration. But creating of a program of pre-conversion from common components takes time too. A program of pre-conversion is individual for each project. So, the main aim of pre-conversion is to make sure that the whole code and components of VFP application were moved to .Net solution.
A common process of migration consists of the following steps:
Common data migration process consists of the following main steps:
But the real situation is more complex.
The first common problem concerns Primary Keys. FoxPro didn't require Primary Keys in all tables strongly. But Primary Keys are required for any client-service technology, including Entity Framework.
Usualy, even if DBF tables have keys, such keys: a) not always as single fields; b) aren't of uniqueidentifier type, which is absent in VFP.
We always suggest adding new primary keys of the uniqueidentifier type for all tables.
But there is one condition, which makes adding new primary keys possible. If we have changed Primary Keys in tables, we must change Foreign Keys in related tables and must populate new FK with correct values to save all relations. So, we must enumerate and collect all relations ( based on the old keys ) between all tables before creating of Primary and Foreign keys.
And sometimes the most difficult problem is: where to find relations. The possible locations are:
We provide such researching and have experience in this area.
We are able to solve any problem with data migration. But! Any data have specific properties. A lot of specific problems can be found only in the process of rewriting views into LINQ queries and during rewriting code. In many cases, when we test new query or program code, the first result may differ to the result, we have in the old one. Each data have its pitfalls, we have to find. For example, some tables have specific constant values of old keys to determine states of objects. Sometimes related fields have rows with zero key to implement LEFT OUTER without nulls. And etc.
So, we can maintain the process of data adapting during the whole project of migration, and we can solve problems with data when they occur.
If VFP project includes a lot of local views, probably these views must be implemented in a new program. But since we have begun to use technologies absolutely different from VFP, approaches also can differ. In some cases, when we use Entity Framework, we can replace simple views and queries with technology of Navigation Properties. And also, simple views can be replaced with LINQ queries to reduce a number of unnecessary objects in the database.
If the view is complex, joins many tables, uses subqueries, unions and selects a big data - in 90% of cases such view must be rewritten as SQL View in the database. SQL View will always be faster. This work is very simple for us, as the syntax of SQL queries in VFP is very close to SQL standard. We pre-convert views and change their syntax. Then we add new views into EntityModel without any problems.
But if a view is simple, selects not much data and must not be precompiled ( program doesn't call view hundreds times per second ), then we recommend to rewrite such view as LINQ query.
A result of such query has a structure similar to the structure of the result of appropriate view. And the result of such query can be updatable.
The following code example describes the process of rewriting VFP view as LINQ to entities.
SELECT h.id as invoice_id,;
h.code as invoice_code,;
h.date as invoice_date,;
c.code as company_code,;
c.name as company_name,;
p.code as product_code,;
p.name as product_name,;
i.price,;
i.quantity,;
i.price*i.quantity as sum ;
FROM invoice_headers h ;
JOIN invoice_items i ON h.id=i.doc_id ;
LEFT OUTER JOIN companies c ON h.company=c.id ;
LEFT OUTER JOIN products p ON i.item=p.id ;
WHERE h.date>=?FromDate AND h.date<=?ToDate
New class consists of three groups of members:
public
class
V_SHIPMENT
:
INotifyPropertyChanged
{
// Fields of Entity classes
public
invoice_headers
e_invoice_headers =
null
;
public
invoice_items
e_invoice_items =
null
;
public
companies
e_companies =
null
;
public
products
e_products =
null
;
// Properties represent flat structure of view result
public
Guid
invoice_id {
get
{
return
e_invoice_headers.id; }
set
{ e_invoice_headers.id =
value
;
NotifyPropertyChanged(
"invoice_id"
);
} }
public
String
invoice_code {
get
{
return
e_invoice_headers.code; }
set
{ e_invoice_headers.code =
value
.Trim();
NotifyPropertyChanged(
"invoice_code"
);
} }
public
DateTime
?
invoice_date {
get
{
return
e_invoice_headers.date; }
set
{ e_invoice_headers.date =
value
;
NotifyPropertyChanged(
"invoice_date"
);
} }
public
String
company_code {
get
{
return
e_companies.code; }
set
{ e_companies.code =
value
.Trim();
NotifyPropertyChanged(
"company_code"
);
} }
public
String
company_name {
get
{
return
e_companies.name; }
set
{ e_companies.name =
value
.Trim();
NotifyPropertyChanged(
"company_name"
);
} }
public
String
product_code {
get
{
return
e_products.code; }
set
{ e_products.code =
value
.Trim();
NotifyPropertyChanged(
"product_code"
);
} }
public
String
product_name {
get
{
return
e_products.name; }
set
{ e_products.name =
value
.Trim();
NotifyPropertyChanged(
"product_name"
);
} }
public
Decimal
?
price {
get
{
return
e_invoice_items.price; }
set
{ e_invoice_items.price =
value
;
NotifyPropertyChanged(
"price"
);
} }
public
Decimal
?
quantity {
get
{
return
e_invoice_items.quantity; }
set
{ e_invoice_items.quantity =
value
;
NotifyPropertyChanged(
"quantity"
);
} }
public
Decimal
?
sum {
get
{
return
e_invoice_items.price * e_invoice_items.quantity; }
set
{ } }
public
event
PropertyChangedEventHandler
PropertyChanged;
public
void
NotifyPropertyChanged(
string
propertyName)
{
if
(PropertyChanged !=
null
)
{
PropertyChanged(
this
,
new
PropertyChangedEventArgs
(propertyName));
}
}
}
public
static
IQueryable
<V_SHIPMENT
> vq_shipment(
testsEntities
ENT,
DateTime
FromDate,
DateTime
ToDate)
{
return
from
h
in
ENT.invoice_headers
join
i
in
ENT.invoice_items
on
h.id
equals
i.doc_id
join
_c
in
ENT.companies
on
h.company
equals
_c.id
into
__c
from
c
in
__c.DefaultIfEmpty()
join
_p
in
ENT.products
on
i.item
equals
_p.id
into
__p
from
p
in
__p.DefaultIfEmpty()
where
h.date >= FromDate && h.date <= ToDate
select
new
V_SHIPMENT
{
e_invoice_headers = h,
e_invoice_items = i,
e_companies = c,
e_products = p
};
}
And you can easily operate with the result of view-analog. For example:
DGrid.ItemsSource = new ObservableCollection <V_SHIPMENT>(DBViews .vq_shipment(ent, DateTime .MinValue, DateTime .MaxValue)); // Show in DataDrid
There are many converters which can convert VFP form into XAML for WPF. But there is no converter which can do it perfectly. Basic control arrangement methods in VFP very differ to WPF.
Most of the converters produce XAML where VFP control arrangement methods are used. As "Anchor" property is optional in VFP, but it is the main in WPF, generated XAML is based on using of atypical for WPF set of control properties: Margin.Left, Margin.Top, Width, Height, values of which are taken from the set of VFP control properties: Left, Top, Width, Height. But this is not good practice in WPF.
Base set of control properties allows aligning control inside Grid or Panel. They are: Margin, HorizontalAlignment and VerticalAlignment. And there are additional properties of WPF, which demand manual writing/correcting of XAML code to make Window behavior perfect.
Having big experience in creating an interface in WPF we can most effectively rewrite VFP forms as in XAML and as in C# code only, with usage of our pre-conversion ( to make sure that all controls moved ). And we can include controls from other frameworks and libraries of third-party companies like Telerik, like in the picture below.
(Personal data are hidden)
VFP code of any complexity can be rewritten in C# effectively.
But we must have as source VFP project and as Dot-Net solution(data model, database, VS solution).
We must be able to trace both VFP project and Dot-Net project on the equal data. We don't rewrite code only. We debug and optimize it.