Christopher Galpin
Christopher Galpin

Reputation: 1098

Reusable generic LightSwitch screen with WCF RIA Services

I'm new to WCF RIA Services, and have been working with LightSwitch for 4 or so months now. I created a generic screen to be used for editing lookup tables all over my LightSwitch application, mostly to learn how to create a generic screen that can be used with different entity sets on a dynamic basis.

The screen is pretty simple:

Generic LookupTypesList in LightSwitch

Opened with arguments similar to this: Application.ShowLookupTypesList("StatusTypes", "StatusTypeId"); which correspond to the entity set for the lookup table in the database.

Here's my WCF RIA service code:

using System.Data.Objects.DataClasses;
using System.Diagnostics;
using System.Reflection;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.ServiceModel.DomainServices.EntityFramework;
using System.ServiceModel.DomainServices.Server;

namespace WCF_RIA_Project
{
    public class LookupType
    {
        [Key]
        public int TypeId { get; set; }
        public string Name { get; set; }
    }

    public static class EntityInfo
    {
        public static Type Type;
        public static PropertyInfo Key;
        public static PropertyInfo Set;
    }

    public class WCF_RIA_Service : LinqToEntitiesDomainService<WCSEntities>
    {

        public IQueryable<LookupType> GetLookupTypesByEntitySet(string EntitySetName, string KeyName)
        {
            EntityInfo.Set = ObjectContext.GetType().GetProperty(EntitySetName);
            EntityInfo.Type = EntityInfo.Set.PropertyType.GetGenericArguments().First();
            EntityInfo.Key = EntityInfo.Type.GetProperty(KeyName);

            return GetTypes();
        }

        [Query(IsDefault = true)]
        public IQueryable<LookupType> GetTypes()
        {
            var set = (IEnumerable<EntityObject>)EntityInfo.Set.GetValue(ObjectContext, null);
            var types = from e in set
                        select new LookupType
                        {
                            TypeId = (int)EntityInfo.Key.GetValue(e, null),
                            Name = (string)EntityInfo.Type.GetProperty("Name").GetValue(e, null)
                        };
            return types.AsQueryable();
        }

        public void InsertLookupType(LookupType lookupType)
        {
            dynamic e = Activator.CreateInstance(EntityInfo.Type);
            EntityInfo.Key.SetValue(e, lookupType.TypeId, null);
            e.Name = lookupType.Name;

            dynamic set = EntityInfo.Set.GetValue(ObjectContext, null);
            set.AddObject(e);
        }

        public void UpdateLookupType(LookupType currentLookupType)
        {
            var set = (IEnumerable<EntityObject>)EntityInfo.Set.GetValue(ObjectContext, null);
            dynamic modified = set.FirstOrDefault(t => (int)EntityInfo.Key.GetValue(t, null) == currentLookupType.TypeId);
            modified.Name = currentLookupType.Name;
        }

        public void DeleteLookupType(LookupType lookupType)
        {
            var set = (IEnumerable<EntityObject>)EntityInfo.Set.GetValue(ObjectContext, null);
            var e = set.FirstOrDefault(t => (int)EntityInfo.Key.GetValue(t, null) == lookupType.TypeId);
            Debug.Assert(e.EntityState != EntityState.Detached, "Entity was in a detached state.");
            ObjectContext.ObjectStateManager.ChangeObjectState(e, EntityState.Deleted);
        }
    }
}

When I add an item to the list from the running screen, save it, then edit it and resave, I receive data conflict "Another user has deleted this record."

Data Conflict: "Another user has deleted this record."

I can workaround this by reloading the query after save, but it's awkward.

If I remove, save, then readd and save an item with the same name I get unable to save data, "The context is already tracking a different entity with the same resource Uri."

Unable to save data: "The context is already tracking a different entity with the same resource Uri."

Both of these problems only affect my generic screen using WCF RIA Services. When I build a ListDetail screen for a specific database entity there are no problems. It seems I'm missing some logic, any ideas?

Upvotes: 1

Views: 996

Answers (2)

Christopher Galpin
Christopher Galpin

Reputation: 1098

I've learned that this the wrong approach to using LightSwitch.

There are several behind-the-scenes things this generic screen won't fully emulate and may not be do-able without quite a bit of work. The errors I've received are just one example. LightSwitch's built-in conflict resolution will also fail.

LS's RAD design means just creating a bunch of similar screens is the way to go, with some shared methods. If the actual layout needs changed across many identical screens at once, you can always find & replace the .lsml files if you're careful and make backups first. Note that modifying these files directly isn't supported.

Upvotes: 1

Michael Washington
Michael Washington

Reputation: 3075

I got that error recently. In my case I create a unique ID in my WCF RIA service, but in my screen behind code I must explicitly set a unique ID when I create the object that will later be passed to the WCF RIA Service insert method (this value will then be overwritten with the unique counter ID in the table of the underlying database).

See the sample code for this project: http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/157/A-Visual-Studio-LightSwitch-Picture-File-Manager.aspx

Upvotes: 0

Related Questions