I do not know about the finished software, but you can do this through operations on sets, with which Sharp can work.
You only need to download the necessary data and translate it into many (in principle, you can work with regular collections, but the execution time will be longer) https://msdn.microsoft.com/ru-ru/library/bb397727%28v=vs.110 % 29.aspx? F = 255 & MSPPError = -2147217396 - reference to the class of sets. After that, you need to perform certain actions to get a certain result:
To find out which tables were deleted in the new database - Take away from the set of tables of the old database the set of new
To find out which tables have been added - take away from the set of the old base many old ones
To find out the changes by columns, you need to get a lot of columns of the table. To get changes to the properties of the columns (changing the type, for example), you need to load a lot of properties. To find out the change in data (rows), you need to load into sets of strings, etc.
It is not so difficult and long as it may seem at first glance. And the time of the program will depend on the size of the base.
UPD:
Before you run, read to the end.
Here's a window ... Copy yourself its contents into a new window.
<Window x:Class="WpfApplication7.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication7" Title="MainWindow" Height="450" Width="525"> <Grid> <StackPanel> <DataGrid Height="100" ItemsSource="{Binding T1}" CanUserReorderColumns="False"/> <Button Content="Скопировать" Click="Button_Click"/> <DataGrid Height="100" ItemsSource="{Binding T2}" CanUserReorderColumns="False"/> <DataGrid IsReadOnly="True" Height="100" ItemsSource="{Binding T3}"/> </StackPanel> </Grid> </Window>
Here is the code
using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Windows; namespace WpfApplication7 { /// <summary> /// Логика взаимодействия для MainWindow.xaml /// </summary> public partial class MainWindow : Window { DataTable _t1; DataTable _t2; DataTable _t3; public MainWindow() { _t1 = new DataTable(); _t2 = new DataTable(); _t3 = new DataTable(); _t1.Columns.Add("id", typeof(int)).AutoIncrement = true; _t1.Columns.Add("field1", typeof(string)); _t1.Columns.Add("field2", typeof(string)); _t1.PrimaryKey = new DataColumn[] { _t1.Columns[0] }; _t2 = _t1.Clone(); _t3.Columns.Add("PK", typeof(string)); _t3.Columns.Add("Action", typeof(string)); T1 = _t1.DefaultView; T2 = _t2.DefaultView; T3 = _t3.DefaultView; _t1.RowChanged += _t1_RowChanged; _t1.RowDeleted += _t1_RowChanged; _t2.RowChanged += _t1_RowChanged; _t2.RowDeleted += _t1_RowChanged; DataContext = this; InitializeComponent(); } void _t1_RowChanged(object sender, DataRowChangeEventArgs e) { FindingChanges(); } void FindingChanges() { _t3.Rows.Clear(); var hs1 = new HashSet<string>(); var hs2 = new HashSet<string>(); // СТРОКИ ДОЛЖНЫ СРАВНИВАТЬСЯ ПО СОВОКУПНОСТИ PRIMARY KEY, ПОЭТОМУ ПОЛУЧАЕМ МАССИВ ВИДА "pk1;pk2...". В НАШЕМ СЛУЧАЕ ПРОСТО "pk" foreach (DataRow row in _t1.Rows) hs1.Add(GetPrimaryKeyToString(row)); foreach (DataRow row in _t2.Rows) hs2.Add(GetPrimaryKeyToString(row)); var hsDel = new HashSet<string>(hs1.Where(e => true)); var hsAdd = new HashSet<string>(hs2.Where(e => true)); // ВОТ ТУТ ВЫЧИТАНИЕ hsDel.ExceptWith(hs2); hsAdd.ExceptWith(hs1); foreach (var item in hsAdd) _t3.Rows.Add(item, "Added"); foreach (var item in hsDel) _t3.Rows.Add(item, "Deleted"); } // ЭТО ФУНКЦИЯ, КОТОРАЯ ВОЗВРАЩАЕТ ЗНАЧЕНИЕ ПЕРВИЧНОГО КЛЮЧА В ВИДЕ СТРОКИ string GetPrimaryKeyToString(DataRow row) { return string.Join(";", row.Table.PrimaryKey.Select(col => Convert.ToString(row[col]))); } public DataView T1 { get; set; } public DataView T2 { get; set; } public DataView T3 { get; set; } void Button_Click(object sender, RoutedEventArgs e) { CopyTable(); } void CopyTable() { _t2.Rows.Clear(); foreach(DataRow row in _t1.Rows) { var newRow = _t2.NewRow(); var i = 0; foreach (var col in row.ItemArray) newRow[i++] = col; _t2.Rows.Add(newRow); } } } }
hence all the methods are needed. Well, the contents of the designer in the form in which it is presented.
Do not pay attention to the initialization code tables. You are interested in the FindingChanges() method.
In order to compare the table line by line, we need the values of the primary keys (otherwise how else to understand that this or that row was added or deleted).
This code will be checked only for deletion and addition. To check the changes, you should compare all the matching rows from both tables for all parameters. I did not do this here.
Run the form. There is in order:
Table, "copy" button, Table, Table
The first table is the old version of the table.
The button copies the entire contents of the first table to the second.
The second table is a new table.
The third table displays the results of changes.
That is, after you fill in the first table and click copy, the third table will be empty. Now you start to do something with tables 1 and 2. For example, if you delete a row from table 1, this will mean that a row has been added to the new table (because there is no row in the old one). If you delete another row from table 2, it will mean that the row in the new table has been deleted. Well, in short, you will understand.
The code is probably not beautiful, but in my opinion it is understandable.
@symbol with the user's nickname, so that he would receive an alert - Bald