October 2008 - Posts

ADO.NET Data Services Hooking POC V4

I don't like rights management with ADO.NET Data Services.

Indeed, you can use a method with ChangeInterceptor attribute to manage CUD operations and a QueryInterceptor attribute to manage Read operations. But if it's a right logic, I think it should be in the entity class (in the BLL) and not in the service.

With my POC, you just need the entity type implements one or two interfaces:

public interface IEntityNeedRightToRead

{

    bool CanRead(string login);

}

public interface IEntityNeedRightsToWrite

{

    bool CanChange(string login, string propertyName);

    bool CanChangeLinks(string login, string propertyName);

    bool CanAdd(string login);

    bool CanDelete(string login);

}

Moreover, in my sample, I have the Login property in my entity types so it's easy to return the default value of a property if you don't want to expose it for the connected people.

The next step is, in this case, to exclude the property if it is in ETags.

C#4: dynamic keyword

With C#4, there is a new keyword: dynamic. This keywork allow you to use an object without defining its type and to “try” to call any method. This allows you for example to call a javascript method, to do interop COM and of course to use dynamic languages which use the DLR (Dynamic Language Runtime). It also can simplify reflection.

Since C#2.0, the .NET Framework includes generic. We can define some constraints on generic types as to be a class, a struct, to inherit a class, to implement an interface, to have a no parameter public constructor. But sometimes, it isn’t efficient.

This is a basic example:

public interface I1

{

    void MyMethod();

}

 

public class C1 : I1

{

    public void MyMethod()

    {

    }

}

  

public static class MyMethodEncapsulation

{

    public static void MyMethod<T>(T item) where T : I1

    {

        item.MyMethod();

    }

}

 

In the previous code, we can do item.MyMethod() because the item type implements I1.

Now the question is how to do the same if you haven’t I1? Indeed, it isn’t possible to define a constraint “T has e method MyMethod without parameter and which returns nothing”.

With C#2 (and 3), you have too possibilities: use a delegate or use reflection.

public static void MyMethod<T>(T item, Action<T> myMethod)

{

    myMethod(item);

}

 

MyMethodEncapsulation.MyMethod(new C1(), c1 => c1.MyMethod());

The delegate is very interesting because there is compilation verification.

public static void MyMethod<T>(T item)

{

    var getMethod = typeof(T).GetMethod("MyMethod");

    if (getMethod == null)

        throw new InvalidOperationException();

    getMethod.Invoke(item, new object[0]);

}

 

MyMethodEncapsulation.MyMethod(new C1());

 

Using reflection is interesting because the call is easier.

 

With C#4 and dynamic, we can write this:

public static void MyMethod<T>(T item)

{

    dynamic di = item;

    di.MyMethod();

}

 

This code is shorter and easier to read.

But what is the executed code?

If we look at it with reflector, we have the following:

public static void MyMethod<T>(T item)

{

    object di = item;

    if (<MyMethod>o__SiteContainer0<T>.<>p__Site1 == null)

    {

        <MyMethod>o__SiteContainer0<T>.<>p__Site1 =

            CallSite<Action<CallSite, object>>.Create(

                new CSharpCallPayload(RuntimeBinder.GetInstance(), false, false"MyMethod", typeof(object), null));

    }

    <MyMethod>o__SiteContainer0<T>.<>p__Site1.Target(<MyMethod>o__SiteContainer0<T>.<>p__Site1, di);

}

 

<MyMethod>o__SiteContainer0<T> is a static class (and so also <>p__Site1). So there is a cache.

The idea of CallSite is to overlap delegates to execute.

 

Now a real life example:

Philippe wanted to develop a TryGet extension method on SPBaseCollection. In fact, SPListCollection, SPUsers, SPWebs inherit SPBaseCollection and have a string indexer. The problem is the fact that the indexer isn’t defined in the base class.  

In compilation, indexer is replaced by get_Item

Philippe do this:

private static Dictionary<Type, MethodInfo> s_GetItemMethodInfos = new Dictionary<Type, MethodInfo>();

public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)

{

    result = default(T);

    try

    {

        MethodInfo methodInfo = null;

        Type spCollectionType = spBase.GetType();

        if (s_GetItemMethodInfos.ContainsKey(spCollectionType) == false)

        {

            methodInfo = spCollectionType.GetMethod("get_Item",

                            BindingFlags.InvokeMethod | BindingFlags.ExactBinding | BindingFlags.Instance |

                            BindingFlags.Public | BindingFlags.DeclaredOnly,

                            null,

                            new Type[] { typeof(string) },

                            null);

            s_GetItemMethodInfos.Add(spCollectionType, methodInfo);

        }

        else

            methodInfo = s_GetItemMethodInfos[spCollectionType];

 

        if (methodInfo == null)

            return false;

 

        result = (T)methodInfo.Invoke(spBase, new object[] { name });

        return true;

    }

    catch (Exception ex)

    {

        Trace.Write(String.Format("Extension Method TryGet : {0}", ex.InnerException.Message), "Monitorable");

        return false;

    }

}

 

I think he should test if ex.InnerException isn’t null in the catch but it isn’t the subject here.

With C#4, we can easily do this:

public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)

{

    result = default(T);

    dynamic dynamicSpBase = spBase;

    try

    {

        result = dynamicSpBase.get_Item(name);

        return true;

    }

    catch (Exception ex)

    {

        Trace.Write(String.Format("Extension Method TryGet : {0}", ex.InnerException.Message), "Monitorable");

        return false;

    }

}

 

It’s a shame that you can’t do dynamicSpBase[name] but it’s just the first CTP…

It’s important to know that if the method doesn’t exist, we will have a Microsoft.CSharp.RuntimeBinder.RuntimeBinderException.

The previous code really executed is the following:

public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)

{

    result = default(T);

    object dynamicSpBase = spBase;

    try

    {

        if (<TryGet>o__SiteContainer0<T>.<>p__Site1 == null)

        {

            <TryGet>o__SiteContainer0<T>.<>p__Site1 = CallSite<Func<CallSite, object, T>>.Create(

                new CSharpConversionPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(),

                                            typeof(T),

                                            CSharpConversionPayload.ConversionKindEnum.ImplicitConversion));

        }

        if (<TryGet>o__SiteContainer0<T>.<>p__Site2 == null)

        {

            <TryGet>o__SiteContainer0<T>.<>p__Site2 = CallSite<Func<CallSite, object, string, object>>.Create(

                new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(),

                                      false,

                                      false,

                                      "get_Item",

                                      typeof(object),

                                      null));

        }

        result = <TryGet>o__SiteContainer0<T>.<>p__Site1.Target(

            <TryGet>o__SiteContainer0<T>.<>p__Site1,

            <TryGet>o__SiteContainer0<T>.<>p__Site2.Target(

                <TryGet>o__SiteContainer0<T>.<>p__Site2,

                dynamicSpBase,

                name));

        return true;

    }

    catch (Exception ex)

    {

        Trace.Write(string.Format("Extension Method TryGet : {0}", ex.InnerException.Message), "Monitorable");

        return false;

    }

}

 

result is affected by the cast to T (CSharpConversionPayload) of the get_Item method call (CSharpCallPayload).

So the dynamic allows you a gain in productivity but also in the code readability (when you use it intelligently).

You will probably see later that reflection is just one of the possibilities offered by the dynamic keyword.With C#4, dynamic is usable by objects, javascript, Python, Ruby and COM.

Posted by Matthieu MEZIL | 4 comment(s)
Filed under: ,

Length too important, SP truncates instead of returning an error

Imagine that we have a stored procedure with a varchar(3) parameter.

If we call it with "AZERTY" as parameter, the SP is executed as with "AZE" as parameter.

I find it really bad!

I think we should have a sql error and then if a .NET adapter calls it, an exception in its call.

Posted by Matthieu MEZIL | with no comments
Filed under: ,

ADO.NET Data Services Hooking POC V3 .1

I just finished implementing the IUpdatable interfaceSmile

With ADO.NET Data Services, in the client part, you can use some LINQ queries which will be translated to an url. It’s really great to be able to use LINQ. However, this query is restrictive. For example, extension methods defined in the Queryable class aren’t allowed with ADO.NET Data Services (NotSupportedException).

When you add a web reference to your service, you will have some entity classes generated from the service metadata. If you have a relationship defined with a List<T>, or IEnumerable<T>, etc., in the server, the client generated code will use a System.Collections.ObjectModel.Collection. This class has a Count property. But when you want to use this on a LINQ query, you have a DataServiceQueryException. My ADO.NET Data Services Hooking POC solves it. Smile

ADO.NET Data Services Hooking POC V3

The third version of my ADO.NET Data Services Hooking POC is released.

The IUpdatable interface is partially implemented. I am waiting for an answer to my thread to implement ClearChanges and ResetResource methods.

Note that it’s a POC, so my tests are very primitive. If you find a bug, let me know and please send me a code to reproduce it at matthieu.mezil at winwise.fr.

I will use this post to answer some questions:

Why having a class hierarchy like this for my entities?

  • EmployeeBase (abstract)
    • Employee
    • ManagerBase (abstract)
      • Manager
      • TeamManager

With ADO.NET Data Services, you can’t have inheritance between the classes you expose. So if you want to expose some employees and some managers, you can’t have Manager inheriting Employee.

Why defining relationships in the exposed classes and not in the base?

An employee has a Manager property and a Manager has also a Manager property. But if you define the Manager property in EmployeeBase, you have a NullReferenceException when you want to get the metadata. It’s a framework bug which will probably be fixed in next version.

Bug with IQueryable and yield syntax: System.BadImageFormatException "An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)"

I find a bug if you mix IQueryable and yield syntax.

After Diego has changed a little bit my sample to reproduce it, this is the code he sent to the C# team:

class Program

{

    static void Main(string[] args)

    {

        try

        {

            GetCategoryProductsUsingQueryable(() => new MyProduct(), 1).ToList();

            Console.WriteLine("Queryable succeeds");

        }

        catch (Exception e)

        {

            LogException(e);

        }

    }

 

    private static void LogException(Exception e)

    {

        Console.WriteLine(e.GetType());

        Console.WriteLine(e.Message);

    }

 

 

    private static List<Category> Categories =

        new List<Category> {

            new Category {

                CategoryID = 1,

                Products = new List<Product> {

                    new Product {

                        ProductID = 1 } } } };

 

 

    static IEnumerable<P> GetCategoryProductsUsingQueryable<P>(Func<P> newP, int categoryId) where P : IProduct

    {

        var categ = Categories.AsQueryable().FirstOrDefault(c => c.CategoryID == categoryId);

        if (categ == null)

            yield break;

        foreach (var p in categ.Products)

            yield return Load(newP(), p);

    }

 

    static P Load<P>(P item, Product p) where P : IProduct

    {

        item.ID = p.ProductID;

        return item;

    }

}

 

public interface IProduct

{

    int ID { get; set; }

}

 

public class MyProduct : IProduct

{

    public int ID { get; set; }

}

 

public class Category

{

    public int CategoryID { get; set; }

    public List<Product> Products { get; set; }

}

public class Product

{

    public int ProductID { get; set; }

}

In this case, the console output is:

System.BadImageFormatException
An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B) 

ADO.NET Data Services Hooking POC v2

I continue my POC development.

In my previous version, I didn't supported keys in url. I do now. So I can do something like this:

http://localhost:50683/WebDataService.svc/Teams(guid'adbae845-989e-4aae-8273-0b4325556812')/Employees

I hope this POC will become more than a POC. ADO.NET Data Services has some limitations with EF:

My POC tries to resolve these problems.

The next version will probably implement the IUpdatable interface to allow data updates.

ADO.NET Data Services Hooking POC

When you use ADO.NET Data Services, you will reduce the flow between the client and the server to get only the entities you really want. If you use EF with ADO.Net Data Services, you will also reduce the flow between the Server and the DB.

EF (particularly in the first version) doesn’t support all cases. So sometimes, you need to develop your own entity classes which encapsulate your EF ones.

In this case, with ADO.NET Data Services, the flow between the Server and the DB isn’t optimized. I made this POC to do this scenario.

When I did it, I realized that the ADO.NET Data Services Framework is extremely closed and most of classes / interfaces / enums are internal. I think that something will change in V2. For example, you have an internal interface System.Data.Services.Providers.IDataServiceProvider only implemented by System.Data.Services.Providers.BaseServiceProvider which is an internal abstract class. I think in a future version, the interface will be public. Else I don’t understand why the interface is used for.

To be able to hook the normal ADO.NET Data Services way, I have two choices:

·         I rewrite a lot of the ADO.NET Data Services internal classes

·         I use, by reflection, the Internal classes

The first choice is probably the best one because as the classes are internal, MS can change their members in the future version but, as it’s just a POC, I choose the second choice which is fastest.

EF: how to do a recursive include?

I have a table Employees with three columns: EmployeeID(uniqueidentifier, PK), Name (nvarchar) and ManagerID(uniqueidentifier, FK to EmployeeID).

I want to be able to get all the sub employees of an employee. 

As confirmed me Jeff, this scenario isn't supported by EF v1.

With T-SQL, we can do this since SQL Server 2005 and the CTE as I said in my comment.

So my idea is to define myself the sql query in my edm.

For this, I add the following function in my SSDL:

<Function Name="GetSubEmployees" IsComposable="false">

    <CommandText>

        WITH SubEmployees (EmployeeID, Name, ManagerID) AS

        (

        SELECT e.EmployeeID, e.Name, e.ManagerID

        FROM Employees AS e

        WHERE e.ManagerID = @ManagerID

        UNION ALL

        SELECT e.EmployeeID, e.Name, e.ManagerID

        FROM Employees AS e

        INNER JOIN SubEmployees AS se ON e.ManagerID = se.EmployeeID

        )

        SELECT EmployeeID, Name, ManagerID FROM SubEmployees

    </CommandText>

    <Parameter Name="ManagerID" Type="uniqueidentifier" />

</Function>

Then I just need to import it into my CSDL. Note that it's possible with the designer. The following function import will be added into the CSDL:

<FunctionImport Name="GetSubEmployees" EntitySet="Employees" ReturnType="Collection(TestCTEModel.Employees)">

    <Parameter Name="ManagerID" Type="Guid" />

</FunctionImport>

with the following mapping in the MSL:

<FunctionImportMapping FunctionImportName="GetSubEmployees" FunctionName="TestCTEModel.Store.GetSubEmployees" />

I now have a method GetSubEmployees in my context.

Then, you can extend the Employees class (partial) to add the properties AllSubEmployees:

partial class Employees

{

    public IEnumerable<Employees> AllSubEmployees

    {

        get

        {

            using (var context = new TestCTEEntities())

            {

                foreach (var e in context.GetSubEmployees(Id))

                    yield return e;

            }

        }

    }

}