Wiki Link: [discussion:1812]
Question on databinding  

May 30 2009 at 6:25 PM
Hi:

I was trying to push a solution to a DataTable, but I hit a problem I don't completely understand. Here is the program:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.OleDb;
using System.Data.Linq;
using System.Text;
using Microsoft.SolverFoundation.Services;
using System.IO;
 
namespace OML1
{
    class Test
    {
        /// <summary>
        /// Called by the OS
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Test t = new Test();
            t.Solve();
        }
 
        /// <summary>
        /// Holds the OML model
        /// </summary>
        string strModel = @"Model[
              Parameters[Sets,I],
              Parameters[Reals,p[I]],
 
              Decisions[Reals[0,Infinity],x[I]],
 
              Constraints[
                 Foreach[{i,I}, x[i]==p[i]]
              ]
           ]";
 
 
        /// <summary>
        ///  SFS
        /// </summary>
        SolverContext context;
 
 
        /// <summary>
        ///  Constructor
        /// </summary>
        public Test()
        {
            context = SolverContext.GetContext();
        }
 
 
        /// <summary>
        /// Solve the problem
        /// </summary>
        public void Solve()
        {
            context.LoadModel(FileFormat.OML, new StringReader(strModel));
 
 
            DataTable dt = new DataTable();
            dt.Columns.Add("index", System.Type.GetType("System.String"));
            dt.Columns.Add("value", System.Type.GetType("System.Double"));
            dt.Rows.Add("a", 1);
            dt.Rows.Add("b", 2);
 
            DataTable dt2 = new DataTable();
            dt2.Columns.Add("index", System.Type.GetType("System.String"));
            dt2.Columns.Add("value", System.Type.GetType("System.Double"));
            dt2.Rows.Add("a", -1);
            dt2.Rows.Add("b", -1);
 
 
            Parameter p = context.CurrentModel.Parameters.First(q => q.Name == "p");
            p.SetBinding(dt.AsEnumerable(), "value", new string[] { "index" });
 
            Decision x = context.CurrentModel.Decisions.First(d => d.Name == "x");
            // next statement gives:
            // The property or field 'value' was not found
            x.SetBinding(dt2.AsEnumerable(), "value", new string[] { "index" }); // <<<<<<< exception
 
 
            Solution solution = context.Solve();
            Console.Write("{0}", solution.GetReport());
 
            context.PropagateDecisions();
 
        }
 
    }
 
}
 

The statement where I try to bind dt2 to x gives an exception with the message:

The property or field 'value' was not found

I have problem interpreting this error, as I really see a column 'value'. Note that the same thing works fine for parameter bindings.

Note: Of course it is not a show stopper (I can get the binding to work with a List data structure, so I can update the DataTable myself as a work around).

Thanks!

----------------------------------------------------------------
Erwin Kalvelagen
Amsterdam Optimization Modeling Group
erwin.kalvelagen@gmail.com
http://amsterdamoptimization.com
----------------------------------------------------------------

Coordinator
Jun 2 2009 at 2:54 PM
Hey Erwin,

It looks like you have found a bug. Currently, decision bindings only can be made against properties and fields, but there is no reason why it should not work with DataColumns too. We will open a work item and fix this issue in a future release. Also, we will try to improve our error messages in this area.

As you said, you can work around this problem by using Lists. Direct use of LINQ is the most flexible, powerful way to do Solver Foundation data binding. The idea is similar to what I described in this blog post: http://blogs.msdn.com/natbr/archive/2009/05/05/creating-parameterized-solver-foundation-models-using-linq-to-sql.aspx. First create a .dbml file with the DataContext, then use it for data binding. If the DataContext is called "data" then your sample would like something like this. (The full sample is on my blog for now: http://blogs.msdn.com/natbr/archive/2009/06/02/solver-foundation-linq-to-sql-example.aspx

   context.LoadModel(FileFormat.OML, new StringReader(strModel)); 
 
   Parameter p = context.CurrentModel.Parameters.First(q => q.Name == "p");
   p.SetBinding(data.P, "value", new string[] { "index" });
   Decision x = context.CurrentModel.Decisions.First(d => d.Name == "x");
   x.SetBinding(data.X, "value", new string[] { "index" }); 
 
   Solution solution = context.Solve();
   Console.Write("{0}", solution.GetReport());
   context.PropagateDecisions();

We will also upload this as a sample soon. Thanks again for the feedback. I'm sorry that you hit this problem.
Best, Nathan


Jun 2 2009 at 7:59 PM
No problem! Yes, direct LINQ is certainly preferably, as your example shows. I had data available in Access so I tried that through OleDbDataAdapter. I believe LINQ is more geared towards SQL Server than to Access.

Thanks again!

----------------------------------------------------------------
Erwin Kalvelagen
Amsterdam Optimization Modeling Group
erwin.kalvelagen@gmail.com
http://amsterdamoptimization.com
----------------------------------------------------------------

Coordinator
Jun 5 2009 at 4:18 PM
Edited Jun 5 2009 at 4:19 PM
You are correct in that as far as I know there is no currently supported LINQ-to-Access provider. Having said that, there are a number of additional LINQ providers to satisfy your data querying needs, and SQL is one of many viable providers. The list includes:
• LINQ to SQL
• LINQ to XML
• LINQ to ADO.NET Data Services
• LINQ to Objects
• etc

In other words, there are many opportunities for the .NET developer to query data, and Solver Foundation is committed in supporting the LINQ effort.

More information on LINQ can be found at the following sites:
http://msdn.microsoft.com/en-us/netframework/aa904594.aspx
http://en.wikipedia.org/wiki/Language_Integrated_Query
http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx

-John Oberon

Aug 11 2009 at 9:40 AM

I am trying to build a transportation model in SFS and bind it to SQL Express DB.
I followed exactly the guidelines in Nathan's post, and got the following error:

No coercion operator is defined between types 'System.Nullable`1System.Single' and 'Microsoft.SolverFoundation.Common.Rational'.

Can you please shed some light on this error?

Best,
John

Coordinator
Aug 11 2009 at 5:04 PM
Hi John - I think the issue relates to converting the input data into the data type required by Solver Foundation. Solver Foundation uses the Rational data type to prevent any conversion errors when bringing data into the system. In my example, I was using (non-nullable) double data - relying on the fact that Rational supports implicit conversion from double. In your case, there are two problems: 1) the DB column appears to be nullable, 2) it is single precision rather than double precision. (The C# syntax is "float?".) The Rational data type does not have implicit conversion from nullable types, nor from "float", hence the error.

If you are able to transform the DB data into double before binding, then you should be okay. For example, you might be able to tag the LINQ expression ".Cast<double>()" on the end. (Or if that doesn't work, ".Cast<float>()" might.

Hope this helps, Nathan

Aug 11 2009 at 7:32 PM
Thank you Nathan,
It worked! you really helped me,

One more simple and annoying thing - when I write (exactly like in your post):

Solution solution = context.Solve(new SimplexDirective());
Report report = solution.GetReport();
Console.WriteLine(report);

from some reason I get the following errors ('Transport' is my namespace):

Error 1 Cannot convert type 'Microsoft.SolverFoundation.Services.Solution' to 'Transport.Solution'

Error 2 'Transport.Solution' does not contain a definition for 'GetReport' and no extension method 'GetReport' accepting a first argument of type 'Transport.Solution' could be found (are you missing a using directive or an assembly reference?)

What could be the reason for that?
John

Coordinator
Aug 11 2009 at 8:35 PM
Glad to hear it! There is probably another class called "Solution" somewhere in your project - either one that you wrote or one that was generated by Visual Studio. In either case, you can either write the fully qualified name for the Solver Foundation object:

Microsoft.SolverFoundation.Services.Solution solution = context.Solve(new SimplexDirective());

OR you could stick a "using" at the top of your source file:

using MSFSolution = Microsoft.SolverFoundation.Services.Solution;
//...
MSFSolution solution = context.Solve(new SimplexDirective());

OR you could just take advantage of .Net 3.0 stuff and do:

var solution = context.Solve(new SimplexDirective());

Any of those three should work - I guess the last one is the easiest. Nathan

Aug 11 2009 at 9:34 PM
Thank you Nathan,
It was the first thing, I had a table named solution...

You are a true savior :-)


Updating...
Page view tracker