7

Can anyone point me to an example of or briefly describe how one would go about creating a custom implementation of a WCF RIA Services DomainService using Linq to SQL as the data access layer but without the use of the .dbml file (this is because the Linq to SQL model is generated by a custom tool, is heavily cutomized, and is of a fairly large database with 50+ tables) and without the VS2010 wizard for creating a DomainService (the wizard is dependant on the .dbml file being available)

Here's a really simple shell of what I tried myself so far:

[EnableClientAccess()]
public class SubscriptionService : DomainService
{
    [Query(IsDefault = true)]
    public IQueryable<Subscription> GetSubscriptionList()
    {
        SubscriptionDataContext dc = new SubscriptionDataContext();
        var subs = dc.Subscription.Where(x => x.Status == STATUS.Active)
            .Select(x => 
            new Subscription
            {
                ID = x.ID,
                Name = x.Name
            }).ToList();

        return subs.AsQueryable();
    }

    public void InsertSubscription(Subscription sub)
    {
        if (!sub.ID.IsEmpty())
        {
            SubscriptionDataContext dc = new SubscriptionDataContext();
            Subscription tmpSub = dc.GetByID<Subscription>(sub.ID);
            if (tmpSub != null)
            {
                tmpSub.Name = sub.Name;
                dc.Save(tmpSub);
            }
            else
            {
                tmpSub = new Subscription();
                tmpSub.Name = sub.Name;
                dc.Save(tmpSub);
            }
        }
    }

    public void UpdateSubscription(Subscription sub)
    {
        if (!sub.ID.IsEmpty())
        {
            SubscriptionDataContext dc = new SubscriptionDataContext();
            Subscription tmpSub = dc.GetByID<Subscription>(sub.ID);
            if (tmpSub != null)
            {
                tmpSub.Name = sub.Name;
                dc.Save(tmpSub);
            }
        }
    }

    public void DeleteSubscription(Subscription sub)
    {
        if (!sub.ID.IsEmpty())
        {
            SubscriptionDataContext dc = new SubscriptionDataContext();
            Subscription tmpSub = dc.GetByID<Subscription>(sub.ID);
            if (tmpSub != null)
            {
                dc.Delete(tmpSub);
            }
        }
    }
}

This seems to work so far. Does anyone see any problem with this approach that I might be missing? I don't want to go too far down the wrong road with this if someone has already tried this way and found some major problems with it.

Thanks for everyone's input.

Dmitry Samuylov
  • 1,554
  • 2
  • 14
  • 37
  • Would suggest WCF Data Services in your scenario instead of RIA Services. – Jeff Aug 31 '11 at 20:19
  • @JeffN825 Would you mind elaborating as to why Data Services would be better? – Dmitry Samuylov Aug 31 '11 at 20:27
  • 3
    WCF Data Services allows you to expose arbitrary IQueryable ObjectContexts. In other words you can create an arbitrary context of whatever type you like (I'd suggest EntityFramework Code First). Then you can connect to it from your client without any generated code, simply using a DataServicesClient. – Jeff Aug 31 '11 at 21:23
  • @JeffN825 Thank you for the suggestion, I will look into WCF Data Services, as far as using EF, its not an option since this Silverlight application would be integrated into an existing ASP.NET web application and the idea is to re-use the existing data access layer. – Dmitry Samuylov Aug 31 '11 at 21:31
  • Ok, all the same, you can expose your LINQ to SQL DataContext as a queryable, updatable, WCF Data Service. – Jeff Aug 31 '11 at 22:21
  • 1
    Just curious why you're not using the LinqToEntitiesDomainService as your base class. I never use the wizard to generate my domain services. – Bryant Sep 16 '11 at 17:53
  • @Bryant I tried that but it did not work in my situation because the SubscriptionDataContext in question is heavily customized and I ran into problems implementing it that required me to make changes to the SubscriptionDataContext, which i cannot afford to do at this time since it is being used by a production application and too much refactoring and regression testing would be involved with that. Having said that, if you could post a sample of a LinqToEntitiesDomainService that you have done or a portion of it, it would be very useful to all (including me). Thanks! – Dmitry Samuylov Sep 17 '11 at 16:28
  • @Bryant Ok, I just realized that i made a mistake in the previous comment. I assumed you meant LinqToSqlDomainService. I can't use a LinqToEntitiesDomainService because as I mentioned in the question, I am using Linq to SQL not EntityFramework. Having said that, i know there is a LinqToSqlDomainService class and that is what i thought you meant. As i said in the previous comment, I did try using that class but ran into problems as described above. – Dmitry Samuylov Sep 17 '11 at 16:33
  • Looking at the source of LinqToSqlDomainService I don't see why you can use it. You should be able to override any of the methods that cause an issue. – Bryant Sep 20 '11 at 00:23
  • @Bryant Well, that seems to be the problem here, the process of trying to custom fit the LinqToSqlDomainService to my DataContext and vice versa was involved enough that it seems I might as well just do a completely custom implementation. Assumption is that we can't change the non-conforming Linq to SQL DataContext because it is being used by an existing production application. I'll post more code when i get further to see what hints/warnings i will get from people. – Dmitry Samuylov Sep 20 '11 at 02:18

1 Answers1

2

There is nothing wrong with doing it this way.

I've done basically the same thing.

you will need to create a property that returns an IQueryable for each query, and you will auto-magically get the skip/take/where stuff with RIA Services.

[EnableClientAccess()]
public class SubscriptionService : DomainService
{
    [Query(IsDefault = true)]
    public IQueryable<Subscription> GetSubscriptionList()
    {
        using(var dc = new SubscriptionDataContext())
             return from x in dc.Subscription
                    where x.Status == STATUS.Active
                    select new Subscription { ID = x.ID, Name = x.Name };
        // make sure you don't call .ToList().AsQueryable() 
        // as you will basically load everything into memory, 
        // which you don't want to do if the client is going to be using 
        // any of the skip/take/where features of RIA Services.  
        // If you don't want to allow this, 
        // simply return an IEnumerable<Subscription>
    }
 }

I assume that Subscription is a DTO and not a L2S class because you are instantiating it explicitly. Just make sure you DTOs have the correct attributes. i.e.

public class Subscription
{
    [Key]
    // you must have a key attribute on one or more properties...
    public int ID { get; set; }
}

If you have child elements in your DTO, use the Include and Association attributes :

public class User
{
    [Key]
    public int Id { get; set; }

    [Include]
    [Association("User_Subscriptions", "Id","UserId")]
    // 'Id' is this classes's Id property, and 'UserId' is on Subscription
    // 'User_Subscriptions' must be unique within your domain service,
    // or you will get some odd errors when the client tries to deserialize
    // the object graph.
    public IEnumerable<Subscription> Subscriptions { get; set; }
}

Also as a side note, you don't need the full object for your delete method, something like this would work and would keep the client from serializing the whole object and posting it back when you don't need too.

public void DeleteSubscription(int id)
{
    using(var dc = new SubscriptionDataContext())
    {
        var sub = dc.GetById<Subscription>(id);
        if( sub != null ) dc.Delete(sub);
    }
}
Master Morality
  • 5,837
  • 6
  • 31
  • 43