Sunday, February 2, 2014

Requirements for using EF in a layered project

A layered project will typically have EF model in different project
and UI and other logic in different projects. There are some wierd things
required to do this.



Steps to be followed when accessing EF model in a different project.

1. Add a reference to "System.Data.Entity" in the refering project.
2. Add the following line to web.config of refering application
 <add assembly="System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
This is required in addition to step 1 above.
3. Add the same connection string in refering project as that of the
refered project.
4. In both the connections, change the file path to actual full path.



=============================================================
A TUTORIAL FOR THE ABOVE
=============================================================

1. Create a new mvc app, say MvcApplication1.

2. Create a new class lib project, say ClassLibrary1.

3. Add a new "Service Based Database"  to class library project, say Database1.mdf.

4. Add a new table to the .mdf file, say "Table1". Create some fields and add a couple of rows.

5. Add new ADO.NET Entity Data Model, say "Model1.edmx" and generate it from database.
   By default, it will create "Database1Entities" and corresponding connection string.
   For now, do not touch the connection string.
   Accept the defaults and go ahead.
6. In the default class, now add a method to return all records from table1 :

namespace ClassLibrary1
{
    public class Class1
    {
        public List<Table1> GetTable1()
        {
            return (new Database1Entities()).Table1.ToList();
        }
    }
}

7. Build ClassLibrary1.

8. In MVCApplication1, access Class1.GetTable, may be in homecontroller.
namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Message"] = "!";
            ClassLibrary1.Class1 cls = new ClassLibrary1.Class1();
            return View(cls.GetTable1());
        }

        public ActionResult About()
        {
            return View();
        }
    }
}

9. Now build solution and run.

10. The first error you will encounter will be :

The type 'System.Data.Objects.DataClasses.EntityObject' is defined in an assembly
that is not referenced. You must add a reference to assembly
'System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.

To solve this error, right click MvcApplication1, and "Add Reference" to
"System.Data.Entity"

Build and run the project.

11. When you run the project, you will encounter next error:
"The specified named connection is either not found in the
configuration, not intended to be used with the EntityClient
provider, or not valid."

To solve this error, copy the connection string from class library's
app.config to MvcApplication's outer web.config.

(If there are two connection strings found in app.config,you have
to copy the one which has metadata information.)

Build and run the project.

12. When you run, you will encounter the following error:

"The underlying provider failed on Open.", with following InnerException:
"An attempt to attach an auto-named database for file
e:\Documents and Settings\rajesh\My Documents\Visual Studio 2008\Projects\MvcApplication1\MvcApplication1\App_Data\Database1.mdf failed.
 A database with the same name exists, or specified file cannot be opened, or it is located on UNC share."


This error is due the path of the mdf file, which MVC or ASP.NET normally try to find
in App_Data.

To solve this error, go to properties of mdf file and copy the
"Primary File Path".

Replace the path of the mdf file with this one in both places, i.e. class lib and mvc app.

Build and run the app.

13. Now when you run the code which accesses the library function,
you will get following error :


CS0012: The type 'System.Data.Objects.DataClasses.EntityObject' is
defined in an assembly that is not referenced. You must add a
reference to assembly
'System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.

To solve this error, add the following line in the <assemblies> section of MvcApplication1 :

<add assembly="System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>


Build and run the app.

Everything should work as expected.

Tuesday, January 7, 2014

knockout options binding : setting intial value

Consider the knockout options binding. Take example 3 from the knockout documentation (http://knockoutjs.com/documentation/options-binding.html). Suppose in this example, we want to assign an initial selected value to, say country "USA".

I tried to modify the given sample as follows ( note the selectedCountry : it is given a default value below)

<p>
    Your country:
    <select data-bind="options: availableCountries, optionsText: 'countryName', value: selectedCountry, optionsCaption: 'Choose...'"></select>
</p>
 
<div data-bind="visible: selectedCountry"> <!-- Appears when you select something -->
    You have chosen a country with population
    <span data-bind="text: selectedCountry() ? selectedCountry().countryPopulation : 'unknown'"></span>.
</div>
 
<script type="text/javascript">
    // Constructor for an object with two properties
    var Country = function(name, population) {
        this.countryName = name;
        this.countryPopulation = population;
    };
 
    var viewModel = {
        availableCountries : ko.observableArray([
            new Country("UK", 65000000),
            new Country("USA", 320000000),
            new Country("Sweden", 29000000)
        ]),
        selectedCountry : ko.observable(this.availableCountries()[1]) // Nothing selected by default
    };
</script>


This does not work.

On the other hand, the following code (which uses function syntax to declare viewModel) perfectly works :


    <p>
    Your country:
    <select data-bind="options: availableCountries, optionsText: 'countryName', value: selectedCountry, optionsCaption: 'Choose...'"></select>
</p>

<div data-bind="visible: selectedCountry"> <!-- Appears when you select something -->
    You have chosen a country with population
    <span data-bind="text: selectedCountry() ? selectedCountry().countryPopulation : 'unknown'"></span>.
</div>

<script type="text/javascript">
    // Constructor for an object with two properties
    var Country = function(name, population) {
        this.countryName = name;
        this.countryPopulation = population;
    };

    var viewModel = function() {
        this.availableCountries = ko.observableArray([
            new Country("UK", 65000000),
            new Country("USA", 320000000),
            new Country("Sweden", 29000000)
        ]);
        this.selectedCountry = ko.observable(this.availableCountries()[1]);
    };

    ko.applyBindings(new viewModel());

</script>


This means that we have to find some better syntax in first approach to declare initial selection value !