Question

I have a dilemma about organizing my projects. I'm building an application for sending newsletters. I divided it into three projects in my solution: Newsletter.UI (WPF), Newsletter.DAL and Newsletter.Services. In Newsletter.DAL there are classes representing entities generating by EF enhanced in additional files (they are partial classes) - overriden ToString(). In Newsletter.UI there is of cource WPF project for presentation. The problem for me begins with Newsletter.Services.

For now I created MailingListService.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newsletter.DAL;

namespace Newsletter.Services
{
    public class MailingListService
    {
        private NewsletterEntities _context;

        public MailingListService()
        {
            _context = new NewsletterEntities();
        }

        public List<string> GetAllMailingListsNames()
        {
            var query = from m in _context.MailingLists select new { m.Name };
            List<string> names = new List<string>();
            foreach (var m in query)
                names.Add(m.Name);
            return names;
        }

        public List<MailingList> GetAllMailingLists()
        {
            var query = from m in _context.MailingLists select m;
            return query.ToList();       
        }
    }
}

MessageService.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newsletter.DAL;
using System.Data.Entity;

namespace Newsletter.Services
{
    public class MessageService
    {
        private NewsletterEntities _context;

        public MessageService()
        {
            _context = new NewsletterEntities();
        }

        public List<Message> GetAllMessages()
        {
            return (from m in _context.Messages select m).ToList();
        }

        public static Message GetMessageByID(int id)
        {
            using (NewsletterEntities context = new NewsletterEntities())
            {
                Message message = (from m in context.Messages where m.MessageID == id select m).FirstOrDefault();
                return message;
            }
        }
    }
}

RecipientService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newsletter.DAL;

namespace Newsletter.Services
{
    public class RecipientService
    {
        private NewsletterEntities _context;
        public RecipientService()
        {
            _context = new NewsletterEntities();
        }
        public void addRecipient(Recipient newRecipient)
        {
            _context.Recipients.AddObject(newRecipient);
        }
    }
}

However, this gets problematic. When I open a window for creating a recipient, I create a MailingListService instance to load mailing lists' names. Then when I try to create a new Recipient, I create a RecipientService instance and try to add a recipient. I get an error that I cannot use the context in different places.

How to solve this? Is my approach bad? What should it be (what should be in Services)? I don't want to get into such errors in future. I don't want to learn the MVVM approach now, I need to do this more or less the way I am doing it.

Was it helpful?

Solution

Several approaches are possible:

  1. Use one context instance for a business transaction. The context should be created outside the services and be passed to the services through their methods or their constructors. This would require a layer of higher level services (or façades) that orchestrate the lower level services (like the ones you've got now). An IoC container could help you create and inject a context with a life cycle that is bound to that of a façade service instance.

  2. Detach/attach. In your AddRecipient method first detach the recipient (after checking if it is attached) and attach it to the current context. However: now the unit of work is split up between 2 context instances and you'll need a TransactionScope to keep it transactionally sound.

  3. Aggregate at a higher level, e.g. by creating a MailService that deals with mailing lists, messages, and recipients.

  4. A mixed approach. Service with their own context instance and (preferably stateless) services that have methods with a context argument in their method signatures (like public Recipient AddRecipient(NewsletterEntities context, string name, string email). (By passing name etc. the service is made responsible for creating new Recipient objects or returning existing ones if they're already there).

Just a few thoughts :).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top