CSLA .NET and LINQ integration

CSLA .NET and LINQ integration

Old forum URL: forums.lhotka.net/forums/t/2445.aspx


JasonMcF posted on Wednesday, February 28, 2007

Hi,

I would just like to get some feedback/ideas on how to integrate LINQ into CSLA .NET. I am currently just at the prototype development stage but after reading some earlier forum posts in relation to LINQ and CSLA .NET I have just tried using DLinq to load data to/from the database and implemented the changes in the DataPortal_XYZ methods.

The code below shows how I have modified a mapping class (ProjectMapper, original code shown further down) to use LINQ/DLinq instead of ADO.NET.

PProject is the DLinq entity that was generated from my database (P_Project table) using the SQLMetal tool. The method Database.GetInstance() returns the current DataContext object.

This class still inherets from the SQLMapper class but only because the prototype is based on an existing application and the database I/U/D methods are being invoked from a DatabaseFacade class. It does not use any of the base class functionality like the original class did. I will obviously look at creating a new base mapper class.

    /// <summary>
    /// Project mapper.
    /// </summary>
    internal class ProjectMapper : SQLMapper<Project>
    {

        public ProjectMapper()
        {
        }

        /// <summary>
        /// Populate a database object with data from the corresponding business object
        /// </summary>
        /// <param name="proj">The source business object</param>
        /// <param name="dbProj">The destination data object</param>
        /// <returns>Database object updated with business data</returns>
        private PProject DBObject(Project proj, PProject dbProj)
        {
            dbProj.PProjectId = proj.Id;
            dbProj.No = proj.No;
            dbProj.Name = proj.Name;
            dbProj.Description = proj.Description;
            dbProj.ProjectManager = proj.ProjectManager;
            dbProj.LProjectTypeId = proj.Type.Id;

            return dbProj;
        }

        /// <summary>
        /// Populate a new database object with data from the corresponding business object
        /// </summary>
        /// <param name="proj">The source business object</param>
        /// <returns>Database object populated with business data</returns>
        private PProject DBObject(Project proj)
        {
            PProject dbProj = new PProject();

            return DBObject(proj, dbProj);
        }

        public override void Insert(Project obj)
        {
            // To use DLINQ we want to override the I/U/D methods and use
            // the DLINQ database context instead
            Database.GetInstance().PProject.Add(DBObject(obj));

        }

        public override void Update(Project proj)
        {
            // We need to copy changes from the business object to the database object so that
            // the data context accurately reflects data changes
            // TODO: CHeck if we can just use the context instead of doing a query, the object
            // should have already been fetched
            PProject dbProj = Database.GetInstance().PProject.Single<PProject>(c => c.PProjectId == proj.Id);

            // Copy parameter values from the business object to the DB project object
            // this will implicitly update the object in the data context
            if (dbProj != null)
            {
                dbProj = DBObject(proj, dbProj);
            }

        }

        public override void Delete(Project proj, object criteria)
        {
            // Fetch the database object to be deleted based on the id of the existing object
            // We do not need to replicate changes from the business object to the database object
            // because it will just be deleted
            PProject dbProj = Database.GetInstance().PProject.Single<PProject>(c => c.PProjectId == proj.Id);

            // Remove the project from the context
            if (dbProj != null)
            {
                Database.GetInstance().PProject.Remove(dbProj);
            }

        }

        public override void Fetch(Project obj, object criteria)
        {
            Project.Criteria crit = (Project.Criteria)criteria;

            // Get the db context and select the single record from the database that
            // matches the criteria (ProjectId - unique)
            PProject proj = Database.GetInstance().PProject.Single<PProject>(c => c.PProjectId == crit.ProjectId);

            // Populate the business layer object from the data access object
            if (proj != null)
            {
                obj.Fetch(proj.PProjectId,
                    proj.No,
                    proj.Name,
                    proj.Description,
                    proj.ProjectManager,
                    ProjectTypeList.GetList().GetProjectType(proj.LProjectTypeId));
            }
        }

    }

The original ProjectMapper class:

    /// <summary>
    /// Project mapper.
    /// </summary>
    internal class ProjectMapper : SQLMapper<Project>
    {

        public ProjectMapper() : base("P_Project")
        {
            // Add table and mapping information
            base.Column_PK("P_ProjectId", "Id");
            base.Column("No", "No");
            base.Column("Name", "Name");
            base.Column("Description", "Description");
            base.Column("ProjectManager", "ProjectManager");
            base.Column("L_ProjectTypeId", "Type.Id");
            base.Column_WHERE("P_ProjectId");
        }

        public override void Fetch(Project obj, object criteria)
        {
            Project.Criteria crit = (Project.Criteria)criteria;

            using (SqlConnection cn = DBHelper.GetOpenConnection())
            {
                using (SqlCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = base.GetSQL_SELECT();
                    cmd.Parameters.AddWithValue("@P_ProjectId", crit.ProjectId);
                    using (SafeDataReader dr = new SafeDataReader(cmd.ExecuteReader()))
                    {
                        while (dr.Read())
                        {
                            obj.Fetch(
                                dr.GetGuid("P_ProjectId"),
                                dr.GetString("No"),
                                dr.GetString("Name"),
                                dr.GetString("Description"),
                                dr.GetString("ProjectManager"),
                                ProjectTypeList.GetList().GetProjectType(dr.GetInt32("L_ProjectTypeId")));
                        }
                    }
                }
            }
        }

    }

Any ideas thoughts on what I have done here?

I am still considering how to integrate the transaction support that DLinq provides and what sort of saving procedure I should use (i.e. handling how doing a SubmitUpdate on the DataContext submits all pending changes).

Thanks in advance for any feedback.

Cheers, Jason

JasonMcF replied on Wednesday, February 28, 2007

I should also add that the SQLMapper class I mention is part of the data access layer that we have written and which provides generic creation of SQL based on mapping information. It is still the implementation of the DataPortal_XYZ methods that have been updated.

The DataPortal_XYZ methods in the business layer Project class are:

        /// <summary>
        /// Fetch method --> fill object with data.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="no"></param>
        /// <param name="name"></param>
        /// <param name="description"></param>
        /// <param name="projectManager"></param>
        /// <param name="type"></param>
        public void Fetch(
            Guid id,
            string no,
            string name,
            string description,
            string projectManager,
            ProjectTypeInfo type)
        {
            mId.Value = id;
            mNo.Value = no;
            mName.Value = name;
            mDescription.Value = description;
            mProjectManager.Value = projectManager;
            mType.Value = type;

            // Mark object as old.
            MarkOld();
        }

        /// <summary>
        /// Call from the DataPortal.
        /// Fetch the object --> delegate to the database facade.
        /// </summary>
        /// <param name="criteria"></param>
        protected override void DataPortal_Fetch(object criteria)
        {
            DatabaseFacade.GetInstance().Fetch(this, criteria);
        }

        /// <summary>
        /// Call from the DataPortal.
        /// Insert the object --> delegate to the database facade.
        /// </summary>
        protected override void DataPortal_Insert()
        {
            DatabaseFacade.GetInstance().Insert(this);

            MarkOld();
        }

        /// <summary>
        /// Call from the DataPortal.
        /// Update the object --> delegate to the database facade.
        /// </summary>
        protected override void DataPortal_Update()
        {
            if (!this.IsDirty) return;

            DatabaseFacade.GetInstance().Update(this);

            MarkOld();
        }

        /// <summary>
        /// Call from the DataPortal.
        /// Delete the object --> delegate to the database facade.
        /// </summary>
        /// <param name="criteria"></param>
        protected override void DataPortal_Delete(object criteria)
        {
            if (!this.IsDirty || this.IsNew) return;

            DatabaseFacade.GetInstance().Delete(this, criteria);

            MarkNew();
        }

        /// <summary>
        /// Call from the DataPortal.
        /// Delete the object --> delegate to the database facade.
        /// </summary>
        protected override void DataPortal_DeleteSelf()
        {
            if (!this.IsDirty || this.IsNew) return;

            DatabaseFacade.GetInstance().Delete(this, null);

            MarkNew();
        }

Hope that clears up any confusion that might have arisen from my first post.

Cheers, Jason

Copyright (c) Marimer LLC