Friday, November 18, 2011

EF 4.1: Upgrading database

Fortunately EF 4.1 can detect that model does not correspond to the schema.
An exception with following text is thrown:

The model backing the 'MyDbContext' context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance. For example, the DropCreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data.

However, even if you manually update the database to follow the model, this exception will still occur.
The reason is that EF does not check model schema completely: tables, columns, keys, indexes etc.

It calculates a hash of the model and compares with the hash of model which the database was built with.

The hash is stored at EdmMetadata table.

So, there are two ways of solving this issue:
1. Tell EF do not use this hash check. For this change your DbContext class like this:



2. Recalculate the hash of the new model and update it manually in the database:


will return new hash, you then can update EdmMetadata table with Sql.
I think that  this way is more correct, because gives you schema validation feature and still allows to make upgrade scripts:
1. Upgrade DB with SQL
2. Reset ModelHash value
3. Run

Sunday, November 13, 2011

Entity Framework 4.1 Code First: Fixing DateTime and datetime2 problem

Entity Framework 4.1 Code First maps all the DateTime properties of entities to "datetime" column by default.
Which means that it cannot store DateTime.MinValue value, because it does not fit to "datetime" SQL data type range.
You have to set SqlDateTime.MinValue value to the properties before storing. This is very annoying and looks bad.
SQL 2008 has better type for date-times called "datetime2", which actually can store DateTime.MinValue.
However EF4.1 does not use this type by default, at least it's code-first version.
Googling and Stackoverflowing gave me no solution, so I had to solve it by myself.

I found 2 ways to do this:

1. [Column(TypeName="datetime2")] - and you have to attach this attribute to all the DateTime properties of your model

2. Fluent API. In the

        protected override void OnModelCreating(DbModelBuilder modelBuilder)

method of any DbContext, you can do something like:

        modelBuilder.Entity<Person>().Property(s => s.CreationTime).HasColumnType("datetime2");

and again you have to do this for each property.

Both options has pros and const.
Option 1 looks bad because you bring DB layer to your Model layer, specifying a type of SQL server.
Option 2 looks like a configuration of DB layer. If sometimes EF will fix the issue, you will just have to fix the method, instead of changing entity classes.
I chose second one. But configuring all the DateTime properties manually seems to be a bad idea.
So, I created utility method for changing db-type of all DateTime properties of all the types in a DbContext.
Here it is:



    

Usage:
public class MyContext: DbContext
{
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            ContextUtils.FixDateTimeColumns(this, modelBuilder);
        }
}

Tuesday, November 08, 2011

Self-hosting WCF RIA Services for your Silverlight application without ASP.NET

Somebody says that self-hosting of WCF RIA services is not possible without IIS.
That's wrong.
This example will help you:

host = new DomainServiceHost(typeof(MyDomainService), uri);
host.Description.Behaviors.Remove<AspNetCompatibilityRequirementsAttribute>();
host.Open();

Silverlight: How to make dynamic load-on-demand TreeView with WCF source



Ok, this post is about experience of making on-demand tree for Silverlight.

As you (maybe) know Silverlight has a control for displaying a tree called TreeView. You have two ways of working with its nodes.

a) Old-school, via TreeViewItem objects.

b) Fashionable, via Hierarchical data template.

I implemented both of them. The last one was tricky one and I want to tell you about it, so about first one I will tell just in three words: use Expanded event.

And now fashionable b) case: Hierarchical data template.

Declaration of the template is simple:

<sdk:treeview.itemtemplate>
  <sdk:hierarchicaldatatemplate itemssource="{Binding Converter={StaticResource treeNodeUploaderConverter}}">
    <stackpanel orientation="Horizontal">
      <img source="{Binding Icon}" />
      <textblock margin="5,0" text="{Binding Text}"></textblock>
    </stackpanel>
  </sdk:hierarchicaldatatemplate>
</sdk:treeview.itemtemplate>

So, here we say that tree should have "hierarchical" data template with a special items source, and it should contain icon and text. Actually each node of the tree will be a DTO object containing at least Icon and Text fields. By the way, Icon field is a string, so specifying it to something like "/myicon.png" will force browser to make a request to the web-server, image will be cached then.

Usually "ItemsSource" property is bound to a field of the DTO containing list of sub-DTOs.

This is acceptable when you have a complete in-memory tree of DTOs. But what if you have a large tree? Let's implement a on-demand tree using "Converter" feature of binding.

public class TreeNodeUploaderConverter: IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    ObservableCollection<object> result = null;
    if (value is TreeNodeDTO) {
      TreeNodeDTO dto = (TreeNodeDTO)value;
      result = new ObservableCollection<object>();
      dto.Subnodes = result;
      TreeNodeStub stub = new TreeNodeStub();
      stub.Parent = dto;
      result.Add(stub);
    }
    else if (value is TreeNodeStub) {
      TreeNodeStub stub = (TreeNodeStub)value;
      TreeNodeDTO parent = (TreeNodeDTO)stub.Parent;
      result = parent.Subnodes;
      MyDataContext context = ContextFactory.GetContext();
      context.GetTreeNodes(parent, op => client_GetTypesTreeNodesCompleted(op), parent);
    }
    return result;
  }

  void client_GetTypesTreeNodesCompleted(InvokeOperation<TreeNodeDTO[]> op)
  {
    var parent = (TreeNodeDTO)op.UserState;
    parent.Subnodes.Clear();
    foreach(var dto in op.Value) {
      dto.Parent = parent;
      parent.Subnodes.Add(dto);
    }
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotSupportedException();
  }
}

Short explanation: when a tree node is created, the tree immediately accesses its property specified in ItemsSource.
This is needed to determine whether the expansion indicator should be displayed or not.
We suppose that in the beginning the tree contains some DTOs on top level.
Then accessing the converter will cause to returning an ObservableCollection with one "stub" element inside.
This means that we always display expansion indicator and we do not make a server request in this stage.
Actually you can change this behaviour - you can send a server-request on this stage, but imagine - you open a node and it has 3 subnodes, tree will access ItemsSource 3 times one by one, do you want to send 3 reqests to server?
I'm not, so I sacrifice state of expansion indicator and I will display it always while node is not opened, that's why TreeNodeStub is used, this tiny object will always be added to sub-node collection.
TreeNodeStub always knows its parent, so when it will be about to display, i.e. its subnodes accessed - we change the collection of parent, and TreeNodeStub gets out.
Since the collection is ObservableCollection the tree will be automatically refreshed.
If you want you can make a trick - create a Text property on TreeNodeStub which returns always a "Loading..." string and you will see this message when opening subnodes.
Wcf Ria DataContext is used in this example, however you can use a normal Wcf.

Happy TreeViewing!

Tuesday, August 03, 2010

How to host Silverlight without running ASP.NET



Hello everybody!
Today's tip is "How to host Silverlight application without ASP.NET, but only using WCF".

So, to run your app you have to type an address to browser, something like "http://localhost/Default.aspx" and web-server should return at least Default.aspx, XAP-file and then maybe some media files to be displayed in your Silverlight application.
By-default when you create Silverlight app, Visual Studio creates a web-project in one of 3 forms, but imagine that you want to host your server as a Windows service, then web-project is not acceptable format of the server part.

The first attempt that I tried to solve this is to use HttpListener class to listen a port and return needed files. Actually this works, but there is a much nicer solution, especially if your Silverlight app uses WCF to communicate with its server.

As mentioned before to start-up the app you don't need to have some server logic, just return to browser some files:
* Default.aspx, which is actually a static file, containing <object ...> tag to specify your app details to Silverlight engine, so ASP.NET is not needed to run it.
* Silverlight.js, which is a Javascript file, also static
* YourApp.xap, which is zip-archive of compiled dlls of your Silverlight project

The idea is to put all these 3 files as resources embedded to your server application and start a WCF service which will return them by web-requests. This is possible!

Unlike Default.aspx and Silverlight.js which are really static and never changed, xap-file is always changed when I changed my Silverlight app. Do I have to replace it as server resource each time when it's get compiled? No. Visual Studio can do this:

Choose your server project, click with right-mouse button, choose "Add...->Existing item...".
Then open /Bin/Debug folder of Silverlight project, choose xap-file, and click "Add as link". This will create a link of external file, instead of copying file to your project.



Then go to properties of added link and set "Embedded Resource" value to "Build Action" field.
You also should change build order of your projects in the solution properties, so Silverlight project is built before server project. Then latest xap-file will be embedded to your server project.

Second step is implementing and configuring WCF-service, which will return your embedded resources. This is easy:

[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ResourceDataService
{
 private const string prefix = "ProjectNamespace.SubFolder1.SubFolder2.";

 [OperationContract]
 [WebGet(UriTemplate = "{fileName}")]
 public Stream GetResourceFile(string fileName)
 {
  if (fileName.ToUpperInvariant().EndsWith(".ASPX"))
   WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";

  return typeof(ResourceDataService).Assembly.GetManifestResourceStream(prefix+fileName);
 }
}


* This source code was highlighted with Source Code Highlighter.

Just don't forget to change "prefix" constant to appropriate value, it usually equals to the default namespace of the project, plus dot-separated names of folders where resources are placed.
The additional check of "ASPX" file extension and changing content type is made because otherwise browser (Google Chrome in my case) will try to save the aspx-file instead of opening it.

Now let's configure the service. I will show an example of configuration in code, but it can be easily converted to app.config-style configuration:

ResourceDataService rds = new ResourceDataService(resourceAssembly, resourcePrefix);

Uri uri = new Uri(string.Format("http://localhost:{0}", port));
resourceDataServiceHost = new ServiceHost(rds, uri);

ServiceEndpoint httpEndpoint = resourceDataServiceHost.AddServiceEndpoint(typeof(ResourceDataService), new WebHttpBinding(), "");
httpEndpoint.Behaviors.Add(new WebHttpBehavior());

resourceDataServiceHost.Open();

* This source code was highlighted with Source Code Highlighter.

That's it.

What if I open a WCF data-service for my Silverlight app on the same port? Will it conflict with my ResourceDataService?
No, it will not. At least for following configuration: set uri in format "http://localhost:port/WcfDataService", set HttpGetEnabled to true and HttpGetUrl to null on service behavior and use HttpTransport+BinaryMessageEncoding as a custom binding.



Всем привет!
Сегодняшний совет: "Как хостить Silverlight-приложение без ASP.NET, используя только WCF".
Итак, чтобы запустить ваше приложение вам нужно ввести адрес в браузер, что-то типа "http://localhost/Default.aspx", а веб-сервер должен вернуть вам как минимум Default.aspx, XAP-файл, и затем, возможно, какие-то медиа-файлы для отображения на вашем Silverlight-приложении.
По-умолчанию, когда вы создаете приложение на Silverlight, Visual Studio создает веб-проект в одном из трех видов, но представьте, что вы хотите хостить сервер в виде сервиса Windows, тогда веб-проект не подходит как формат серверной части.

Первая попытка, которую я предпринял для решения этой проблемы — использовать класс HttpListener для прослушивания порта и возврата необходимых файлов. На самом деле такой подход работает, но есть намного более красивое решение, особенно если ваше Silverlight-приложение использует WCF для связи с его сервером.

Как было сказано ранее, для запуска приложения вам не нужна какая-то серверная логика, просто верните браузеру несколько файлов:
* Default.aspx, который на самом деле является статическим файлом, содержащим тег <object ...> для указания информации о приложении движку Silverlight, так что ASP.NET не нужен для его выполнения.
* Silverlight.js, который также является статическим Javascript-файлом
* YourApp.xap, который является zip-архивом скомпилированных сборок вашего Silverlight-приложения

Идея в том, чтобы сохранить эти три файла как ресурсы, встроенные в ваше серверное приложение, и запустить WCF-сервис, который будет возвращать их по веб-запросам.
Это возможно!

В отличии от Default.aspx и Silverlight.js, которые являются по-настоящему статичными и никогда не меняются, xap-файл меняется всегда, когда я меняю мое приложение на Silverlight. Нужно ли мне заменять его как серверный ресурс, каждый раз, как он компилируется? Нет. Это может сделать Visual Studio:

Выберите ваш серверный проект, кликните правой кнопкой мыши, выберите "Add...->Existing item...". Затем откройте папку /Bin/Debug приложения Silverlight, выберите xap-файл и нажмите на "Add as link". Это создаст ссылку на внешний файл, вместо копирования файла в ваш проект.



Затем откройте свойства добавленной ссылки и установите значение "Embedded Resource" (Встроенный ресурс) в поле "Build Action".
Вы также должны изменить порядок построения ваших проектов в свойствах солюшена (solution), так, чтобы проект Silverlight строился раньше серверного проекта. Тогда самый последний xap-файл будет включен в ваш серверный проект.

Второй шаг это реализация и конфигурирование WCF-сервиса, который будет возвращать ваши серверные ресурсы. Это легко:

[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ResourceDataService
{
 private const string prefix = "ProjectNamespace.SubFolder1.SubFolder2.";

 [OperationContract]
 [WebGet(UriTemplate = "{fileName}")]
 public Stream GetResourceFile(string fileName)
 {
  if (fileName.ToUpperInvariant().EndsWith(".ASPX"))
   WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";

  return typeof(ResourceDataService).Assembly.GetManifestResourceStream(prefix+fileName);
 }
}


* This source code was highlighted with Source Code Highlighter.

Только не забудьте сменить константу "prefix" на соответствующее значение, оно обычно совпадает с дефолтовым неймспейсом проекта плюс имена папок, где лежат ресурсы, разделенные точкой. Дополнительная проверка на расширение "ASPX" и изменение типа содержимого сделаны так, потому что иначе браузер (Google Chrome в моем случае) попытается сохранить aspx-файл, а не открыть его.

Теперь сконфигурируем сервис. Я покажу пример конфигурации в коде, но его можно легко переделать в конфигурацию в app.config:

ResourceDataService rds = new ResourceDataService(resourceAssembly, resourcePrefix);

Uri uri = new Uri(string.Format("http://localhost:{0}", port));
resourceDataServiceHost = new ServiceHost(rds, uri);

ServiceEndpoint httpEndpoint = resourceDataServiceHost.AddServiceEndpoint(typeof(ResourceDataService), new WebHttpBinding(), "");
httpEndpoint.Behaviors.Add(new WebHttpBehavior());

resourceDataServiceHost.Open();

* This source code was highlighted with Source Code Highlighter.

Готово.

Что если я открою WCF-датасервис для моего Silverlight-приложения на том же порту? Будет ли он конфликтовать с ResourceDataService?
Нет, не будет. По-крайней мере для такой конфигурации: установите URI в формате "http://localhost:port/WcfDataService", установите HttpGetEnabled в true, а HttpGetUrl в null на поведении сервиса, и используйте HttpTransport+BinaryMessageEncoding в качестве кастомной привязки.

Thursday, July 29, 2010

Debug Silverlight application runned on Google Chrome


During the week I was busy with a Silverlight application. I had to make a special hosting program for it, so no IIS needed. And I faced the problem that it was not possible to debug my Silverlight code - breakpoint just don't get filled in red, symbols are not loaded.
I tried to debug attaching to chrome.exe process which had a title of my start page, but I did not work.
Googling a bit the solution was found:

1. When attaching set "Attach to" to "Silverlight code" value.
2. Attach chrome.exe process which has "Silverlight" in the Type column.

Second point is required because Silverlight is run not on the process where your start page works, but on the another dedicated process.




В течение недели я был занят написанием приложения на Silverlight. Мне пришлось сделать специальную программу-хостинг, так что IIS не требовался. И я столкнулся с тем, что мой Silverlight-код невозможно было отладить - брейкпоинты не "краснели", символы отладки не загружались. Подключение отладчика к процессу chrome.exe с заголовком моей стартовой страницы не давало результатов. Немного погуглив решение было найдено:

1. При подключении отладчика в поле "Attach to" установите значение "Silverlight code".
2. Подключайте отладчик к тому процессу chrome.exe, который имеет строку "Silverlight" в колонке "Type".

Второй пункт требуется потому, что Silverlight запускается не в том процессе, в котором работает стартовая страница, а в другом специально выделенном процессе.

Monday, July 26, 2010

Windows 7: How to run program as Administrator. Fast way.


Inspired by the blog of my collegue Marat Faskhiev (http://faskhiev.blogspot.com/) I decided to continue posting to my own blog. The goal is to share some tips, tricks and ideas and also to increase my level of written language and ability to describe ideas in words.
The blog will be written in english and russian language, I'll try to translate between them as good as I can =)

So, tip of the day is: "Fast way of running program as Administrator on Windows 7".
1. Run a program with your regular account
2. Click with right mouse button on its thumbnail from the taskbar
3. On the context menu click again with right button on the item with the program name
4. On the second context menu click on "Run as administrator". Voila!




Вдохновившись блогом моего коллеги Марата Фасхиева (http://faskhiev.blogspot.com/), я решил продолжить постить записи в мой блог. Цель — поделиться наблюдениями и идеями, а также повысить свой уровень выражения мыслей в письменном виде. Блог будет вестись на английском и русском языках, буду переводить записи как смогу =)

Итак, сегодняшний совет: "Быстрый способ запустить программу под аккаунтом Администратора на Windows 7".

1. Запустите программу под вашим обычным аккаунтом
2. Кликните правой кнопкой по ее иконке на панели задач
3. В контекстном меню снова кликните правой кнопкой мыши по пункту, в котором написано имя программы
4. Во втором контекстом меню выберите "Запустить как администратор". Готово!

Monday, February 27, 2006

Another bug in .NET Framework

System.Type.InvokeMember(...) throws IndexOutOfRangeException when calling method that that takes a variable number of parameters, i.e. "params object[] initParams".
This bug was found in frameworks v1.1.4322 (1.1) and v2.0.50727 (2.0).

At 2006-02-27 the Microsoft Sub-status for this bug was "Working on solution".
See bug details.