Основы сущности с репозитория и единицы работы шаблон и архитектуры поко


Это архитектура с ef4, используя шаблон репозитория и единицы работы по образцу с поко. Я считаю, что я сделал несколько ошибок.

У меня есть решение с 5 проектов:

  • Приложение myapp.Общие
  • Приложение myapp.Данных.В ef4
  • Приложение myapp.Домен.Компания
  • Приложение myapp.Домен.Передача
  • Приложение myapp.Веб

Кроме того, я использую помощью StructureMap в этом приложении для того, чтобы ввести данные слоя.

Теперь я опишу то, что каждый проект содержит - с Источник:

Приложение myapp.Общие - содержит общие классы и интерфейсы

Интерфейс репозитория:

public interface IRepository<T> where T : class
{
    IQueryable<T> GetQuery(IEnumerable<Expression<Func<T, object>>> includes);

    IPaged<T> GetQuery(IQueryable<T> query,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, int pageNumber, int pageSize);

    IPaged<T> GetQuery(IEnumerable<T> query,
        Func<IEnumerable<T>, IOrderedEnumerable<T>> orderBy, int pageNumber, int pageSize);

    IEnumerable<T> GetObjectStateManagerChanges();

    void Insert(T entity);
    void MarkModified(T entity);
    void Delete(T entity);
    void Attach(T entity);
    void Detach(T entity);
    T GetOriginalEntity(Func<T, bool> predicate);
}

Единица работы интерфейс:

public interface IUnitOfWork : IDisposable
{
    int Commit();
}

Единица работы фабрики интерфейс:

public interface IUnitOfWorkFactory
{
    IUnitOfWork Create();
}

Единицы работы:

  public static class UnitOfWork
    {
        private const string HTTPCONTEXTKEY = "MyApp.Common.HttpContext.Key";

        private static IUnitOfWorkFactory _unitOfWorkFactory;
        private static readonly Hashtable _threads = new Hashtable();

        public static void Commit()
        {
            IUnitOfWork unitOfWork = GetUnitOfWork();

            if (unitOfWork != null)
            {
                unitOfWork.Commit();
            }
        }

        public static IUnitOfWork Current
        {
            get
            {
                IUnitOfWork unitOfWork = GetUnitOfWork();

                if (unitOfWork == null)
                {
                    _unitOfWorkFactory = ObjectFactory.GetInstance<IUnitOfWorkFactory>();
                    unitOfWork = _unitOfWorkFactory.Create();
                    SaveUnitOfWork(unitOfWork);
                }

                return unitOfWork;
            }
        }

        public static void Dispose()
        {
            IUnitOfWork unitOfWork = GetUnitOfWork();

            if (unitOfWork != null)
            {
                unitOfWork.Dispose();
            }
        }

        private static IUnitOfWork GetUnitOfWork()
        {
            if (HttpContext.Current != null)
            {
                if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
                {
                    return (IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY];
                }

                return null;
            }
            else
            {
                Thread thread = Thread.CurrentThread;
                if (string.IsNullOrEmpty(thread.Name))
                {
                    thread.Name = Guid.NewGuid().ToString();
                    return null;
                }
                else
                {
                    lock (_threads.SyncRoot)
                    {
                        return (IUnitOfWork)_threads[Thread.CurrentThread.Name];
                    }
                }
            }
        }

        private static void SaveUnitOfWork(IUnitOfWork unitOfWork)
        {
            if (HttpContext.Current != null)
            {
                HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork;
            }
            else
            {
                lock (_threads.SyncRoot)
                {
                    _threads[Thread.CurrentThread.Name] = unitOfWork;
                }
            }
        }
    }

Базовый репозиторий для всех лиц:

public abstract class BaseRepository<T> where T : class
{
    protected IRepository<T> Repository { get; set; }

    public IEnumerable<Expression<Func<T, object>>> Includes { private get; set; }


    public BaseRepository()
    {
        Repository = ObjectFactory.GetInstance<IRepository<T>>();
        Includes = null;
    }

    protected virtual IQueryable<T> GetQuery()
    {
        return Repository.GetQuery(Includes).AsEnumerable().AsQueryable();
    }

    protected virtual IQueryable<T> GetQuery(params Expression<Func<T, object>>[] includes)
    {
        return Repository.GetQuery(includes);
    }

    protected virtual IPaged<T> GetQuery(IEnumerable<T> query,
        Func<IEnumerable<T>, IOrderedEnumerable<T>> orderBy, int pageNumber, int pageSize)
    {
        return Repository.GetQuery(query, orderBy, pageNumber, pageSize);
    }


    protected virtual IPaged<T> GetQuery(IQueryable<T> query,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, int pageNumber, int pageSize)
    {
        return Repository.GetQuery(query, orderBy, pageNumber, pageSize);
    }

    protected virtual IEnumerable<T> GetObjectStateManagerChanges()
    {
        return Repository.GetObjectStateManagerChanges();
    }

    protected virtual void Insert(T entity)
    {
        Repository.Insert(entity);
    }

    protected virtual void MarkModified(T entity)
    {
        Repository.MarkModified(entity);
    }

    protected virtual void Delete(T entity)
    {
            Repository.Delete(entity);
    }

    protected virtual void Attach(T entity)
    {
        Repository.Attach(entity);
    }

    protected virtual void Detach(T entity)
    {
        Repository.Detach(entity);
    }

    protected virtual T GetOriginalEntity(Func<T, bool> predicate)
    {
        return Repository.GetOriginalEntity(predicate);
    }
}

Приложение myapp.Данных.В ef4 - держит ведения приложение.Общие и реализует все интерфейсы, используя эф путь. Он также включает в себя MyAppModel.edmx, который имеет модели для всех субъектов системы. MyAppModel.edmx-файла имеет "стратегия создания кода" по умолчанию. Сущность контейнер зовут MyAppEntities.

Реализация репозитория:

public class Repository<T> : IRepository<T> where T : class
{
    ObjectContext _context;
    IObjectSet<T> _objectSet;

    protected ObjectContext Context
    {
        get
        {
            if (_context == null)
            {
                _context = GetCurrentUnitOfWork<EFUnitOfWork>().Context;
            }

            return _context;
        }
    }

    protected IObjectSet<T> ObjectSet
    {
        get
        {
            if (_objectSet == null)
            {
                _objectSet = this.Context.CreateObjectSet<T>();
            }

            return _objectSet;
        }
    }

    public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork
    {
        return (TUnitOfWork)UnitOfWork.Current;
    }

    public virtual IQueryable<T> GetQuery(IEnumerable<Expression<Func<T, object>>> includes)
    {
        return ObjectSet.IncludeMultiple(includes);
    }

    public virtual IPaged<T> GetQuery(IQueryable<T> query,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, int pageNumber, int pageSize)
    {
        if (orderBy != null)
        {
            query = orderBy(query);
        }

        IPaged<T> page = new Paged<T>(query, pageNumber, pageSize);

        return page;
    }

    public virtual IPaged<T> GetQuery(IEnumerable<T> query,
        Func<IEnumerable<T>, IOrderedEnumerable<T>> orderBy, int pageNumber, int pageSize)
    {
        if (orderBy != null)
        {
            query = orderBy(query);
        }

        IPaged<T> page = new Paged<T>(query, pageNumber, pageSize);

        return page;
    }

    public virtual IEnumerable<T> GetObjectStateManagerChanges()
    {
        return this.Context.ObjectStateManager.
            GetObjectStateEntries(EntityState.Added | EntityState.Modified).
            Select(e => e.Entity).
            OfType<T>();
    }

    public virtual void Insert(T entity)
    {
        this.ObjectSet.AddObject(entity);
    }

    public virtual void Delete(T entity)
    {
        this.ObjectSet.DeleteObject(entity);
    }

    public virtual void MarkModified(T entity)
    {
        this.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    }

    public virtual void Attach(T entity)
    {
        ObjectStateEntry entry = null;
        if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == false)
        {
            this.ObjectSet.Attach(entity);
        }
    }

    public virtual void Detach(T entity)
    {
        ObjectStateEntry entry = null;
        if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == true)
        {
            this.ObjectSet.Detach(entity);
        }
    }

    public virtual T GetOriginalEntity(Func<T, bool> predicate)
    {
        T originalEntity = null;
        EFUnitOfWorkFactory factory = new EFUnitOfWorkFactory();
        using (EFUnitOfWork uow = (EFUnitOfWork)factory.Create())
        {
            originalEntity = uow.Context.CreateObjectSet<T>().Single(predicate);
        }
        return originalEntity;
    }
}

Расширение, которое используется при реализации репозитория:

public static class Extensions
    {
        public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query,
            IEnumerable<Expression<Func<T, object>>> includes)
            where T : class
        {
            if (includes != null)
            {
                query = includes.Aggregate(query,
                          (current, include) => current.Include(include));
            }

            return query;
        }
    }

Реализация IUnitOfWork:

public class EFUnitOfWork : IUnitOfWork, IDisposable
{
    public ObjectContext Context { get; private set; }
    public int Id { get; private set; }

    public EFUnitOfWork(ObjectContext context, int id)
    {
        Id = id;
        Context = context;
        Context.ContextOptions.LazyLoadingEnabled = false;
    }

    public int Commit()
    {
        return Context.SaveChanges();
    }

    public void Dispose()
    {
        if (Context != null)
        {
            Context.Dispose();
            Context = null;
        }

        GC.SuppressFinalize(this);
    }
}

Реализация IUnitOfWorkFactory:

public class EFUnitOfWorkFactory : IUnitOfWorkFactory
    {
        private static int Counter = 0;
        private static Func<ObjectContext> _objectContextDelegate;
        private static readonly Object _lockObject = new object();

        public static void SetObjectContext(Func<ObjectContext> objectContextDelegate)
        {
            _objectContextDelegate = objectContextDelegate;
        }

        public IUnitOfWork Create()
        {
            ObjectContext context;

            lock (_lockObject)
            {
                Counter++;
                context = _objectContextDelegate();
            }

            return new EFUnitOfWork(context, Counter);
        }
    }

Приложение myapp.Домен.Компании и приложение.Домен.Передача - содержит поко объектов, который должен содержать результаты запросов и бизнес-логики решение. Каждый проект отражает другие и разные потребности системы. Но, лиц из Приложение.Домен.Компании имеют связи в БД и в .с edmx в приложение myapp.Домен.Передачи сущностей.

Например: автомобиль , который существует в приложение myapp.Домен.Компания ссылается на VehicleTransmission , что существует в приложение myapp.Домен.Передачи. Кроме того, VehicleTransmission держит ведения автомобиля и поэтому приложение.Домен.Передача содержит ссылку на приложение.Домен.Компании. Оба проекта удерживайте ссылку на приложение.Общие. Важно помнить, что T_TBL_VEHICLE и T_TBL_VEHICLE_TRANSMISSION (и связей между ними) представлены в той же .файл edmx. Каждый .файл CS в рамках этих проектов содержит сущность Poco и менеджер репозитория.

Автомобиля.КС (от приложение.Домен.Компании)

public class Vehicle
    {
        public virtual long Id { get; set; }
        public virtual string VehicleNumber { get; set; }

        public virtual DateTime? PurchaseDate { get; set; }
        public virtual string InsuranceAgency { get; set; }
        public virtual DateTime? InsuranceValidity { get; set; }
        public virtual string Comments { get; set; }
        public virtual IList<Worker> Workers { get; set; }        

        public string GetPhoneNumber()
        {
            string phoneNumber = null;
            if (PhoneId.HasValue)
            {
                phoneNumber = Phone.PhoneNumber1;
                if (string.IsNullOrEmpty(phoneNumber))
                {
                    phoneNumber = Phone.PhoneNumber2;
                }
            }
            return phoneNumber;
        }

        public string GetManufacturerName()
        {
            string name = null;
            if (ManufacturerId.HasValue)
            {
                name = Manufacturer.Name;
            }
            return name;
        }
    }

    public class VehicleRepository : BaseRepository<Vehicle>
    {
        private bool IsCounterExists(Guid companyId, long counter)
        {
            return GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false).
                AsEnumerable().
                Any(x => x.Code.ToNullable<long>() == counter);
        }

        public void Attach(Vehicle entity)
        {
            base.Attach(entity);
        }

        public void MarkModified(Vehicle entity)
        {
            base.MarkModified(entity);
        }

        public void Delete(Vehicle entity)
        {
            base.Delete(entity);
        }

        public void Insert(Vehicle entity)
        {
            if (entity.VehicleNumber != null) { entity.VehicleNumber.Trim(); }
            if (entity.VehicleNumber == null || entity.VehicleNumber == string.Empty)
            {
                throw new MissingFieldException(
                    CompanyExceptionMessage.MissingFieldException_Vehicle_Number.Message);
            }
            if (string.IsNullOrEmpty(entity.Code))
            {
                StaffCounterRepository rep = new StaffCounterRepository();
                entity.Code = rep.GetAndAdvanceCounter(entity.CompanyId,
                    StaffCounterEnum.VEHICLE,
                    counter => IsCounterExists(entity.CompanyId, counter)).ToString();
            }

            int equals = GetQuery().
                 Where(x => x.CompanyId == entity.CompanyId).
                 Where(x => x.IsDeleted == false).
                 Where(x =>
                     (x.Code != null && x.Code == entity.Code) ||
                     (x.VehicleNumber == entity.VehicleNumber)
                     ).Count();
            if (equals > 0)
            {
                throw new ExistsNameOrCodeException(
                    CompanyExceptionMessage.ExistsNumberOrCodeException_Vehicle);
            }
            base.Insert(entity);
        }

        public Vehicle Get(Guid companyId, long vehicleId)
        {
            return GetQuery().Where(x => x.CompanyId == companyId).
                Single(x => x.Id == vehicleId);
        }

        public IQueryable<Vehicle> Get(Guid companyId)
        {
            return GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false).
                OrderBy(x => x.VehicleNumber);
        }

        public Vehicle Get(Guid companyId, string code)
        {
            return GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false).
                FirstOrDefault(x => x.Code == code);
        }

        public IQueryable<Vehicle> Search(Guid companyId, string vehicleNumber)
        {
            var query = GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false);
            if (vehicleNumber != null)
            {
                query = query.Where(x => x.VehicleNumber.Contains(vehicleNumber));
            }
            return query.OrderBy(x => x.VehicleNumber);
        }

        public IEnumerable<Vehicle> Get(Guid companyId, bool? freeVehicles,
            bool includeContractorVehicles)
        {
            IEnumerable<Vehicle> query = GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false);

            if (freeVehicles == true)
            {
                query = query.Where(x => x.Workers.Count == 0);
            }
            else if (freeVehicles == false)
            {
                query = query.Where(x => x.Workers.Count > 0);
            }
            query = query.AsEnumerable();

            if (includeContractorVehicles == true)
            {
                WorkerRepository rep = new WorkerRepository();
                IEnumerable<Vehicle> contractorsVehicles = rep.Get(companyId).
                    Where(x => x.ContractorVehicleNumber != null &&
                        x.ContractorVehicleNumber != string.Empty).
                    AsEnumerable().Select(x => new Vehicle()
                    {
                        VehicleNumber = x.ContractorVehicleNumber
                    });
                query = query.Union(contractorsVehicles);
            }

            return query;
        }        

        public IQueryable<Vehicle> Get(Guid companyId, VehicleStatusEnum? status,
            string code, string vehicleNumber)
        {
            var query = GetQuery().
                Where(x => x.CompanyId == companyId).
                Where(x => x.IsDeleted == false);

            if (status.HasValue)
            {
                long? statusId = VehicleStatusWrapper.GetId<VehicleStatusWrapper>(status.Value);
                query = query.Where(x => x.StatusId == statusId);
            }
            if (!string.IsNullOrEmpty(code))
            {
                query = query.Where(x => x.Code.Contains(code));
            }
            if (!string.IsNullOrEmpty(vehicleNumber))
            {
                query = query.Where(x => x.VehicleNumber.Contains(vehicleNumber));
            }
            return query;
        }
    }

VehicleTransmission.КС (от приложение.Домен.Передачи)

public class VehicleTransmission
    {
        public long? VehicleId { get; set; }
        public string VehicleNumber { get; set; }
        public long? SubcontractorId { get; set; }
        public bool WorkerOnHold { get; set; }

        public Vehicle Vehicle { get; set; }

        public void SetVehicleId(long? vehicleId)
        {
            VehicleId = vehicleId;
        }

        //Possibility to set vehicle that is not exists on the db
        public void SetVehicleNumber(string vehicleNumber)
        {
            VehicleId = null;
            VehicleNumber = vehicleNumber;
        }
    }

    public class VehicleTransmissionRepository : BaseRepository<VehicleTransmission>
    {
        public IQueryable<VehicleTransmission> Get()
        {
            return GetQuery();
        }
}

Приложение myapp.Веб - содержится в веб-приложении. Содержит ссылки на все остальные проекты.

В глобальной.эйсакс, в событий Application_Start я настроить помощью StructureMap:

    ObjectFactory.Configure(x =>
    {
        x.For<IUnitOfWorkFactory>().Use<EFUnitOfWorkFactory>();
        x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
        x.For(typeof(IEnumRepository<,>)).Use(typeof(EnumRepository<,>));
    });
    EFUnitOfWorkFactory.SetObjectContext(() => new MyAppEntities());

Кроме того, в Application_EndRequest я делаю:

UnitOfWork.Dispose();

У меня также есть веб-сервисы, где есть я использую репозитории и называть UnitOfWork.Совершить(), когда это необходимо.

До сих пор, это моя архитектура. Я действительно хочу знать, если я сделал все правильно и где мои подводные камни.



46631
31
задан 24 октября 2011 в 08:10 Источник Поделиться
Комментарии
3 ответа

Без понимания слишком много вашего сценария, вот несколько советов:

Я не настолько знаком с основами сущности, но единый интерфейс для Ву обычно имеют следующие особенности:

public interface UnitOfWork : IDisposable {
bool IsInTransaction { get; }
bool IsDirty { get; } // Same as your MarkModified()
void BeginTransaction();
void Commit();
void Rollback();
}

Где ошибка и обработки логов? Ниже приведен пример простой фантик можно использовать.

// Example call
ExceptionHandler.Try("Could not load Vechicle", () => _vehicleRepository.Insert(vehicle));

// Example class
public class ExceptionHandler
public void Try(string errorMessage, Action func)
{
try
{
func();
}
catch (Exception e)
{
Handle(e, errorMessage); // handle and log exception
}
}

И изменить название общего проекта что-то реже..;)

12
ответ дан 27 октября 2011 в 08:10 Источник Поделиться

Это на самом деле очень похожа на мою реализацию...одно из основных отличий заключается в том, что я не реализовать управление жизненным циклом для ВУ. Я разберусь, что с внедрением зависимостей (Ninject, используя контекст запроса), который производит тот же результат.

Еще одно важное различие в моей реализации заключается в том, что у меня есть ObjectContextResolver и ObjectContextFactory. Единица работы и репозиториев используйте ObjectContextResolver , чтобы получить доступ к текущему контексту, где ObjectContextFactory используется ObjectContextResolver для создания новых экземпляров условиях, когда никто не существует. В ObjectContextFactory также использует ObjectContextActivationInfo , который является оболочкой для строки подключения и настройки, что позволяет мне легко варьировать способ контекст создается.

У меня тоже есть два типа хранилищ...у меня EntityRepository , которая очень похожа на вашу реализацию и позволяет мне запрос и работать с конкретными лицами. У меня тоже есть ContextRepository предоставляющий GetSet способ так что я могу запросить любой тип сущности в моей модели.

Я также позволяю моим единицу объема работ, чтобы быть немного более гибким и позволит приложению-потребителю определить начало и конец блока работы. Типичный пример использования выглядит так...

using(var unitOfWork = this.UnitOfWorkFactory.NewUnitOfWork())
{
try
{
var myVehicle = this.VehicleService.GetVehicleById(123);
myVehicle.VehicleNumber = this.MyExternalWebService.GetNewVehicleNumber();
myVehicle.DateModified = DateTime.Now();

unitOfWork.Commit();
}
catch(Exception ex)
{
// The Web Service Call failed, don't save the changes
unitOfWork.Rollback();
}

}

Это также позволяет отдельной единицы работы происходит с помощью одного запроса, который является важным, если некоторые вещи должны спастись, прежде чем других вещей может вам создал...это часто случается, когда сторонние сервисы или наследие хранит данные используются в сочетании с эф.

Потому что я обернул свой код в блок using, как только операция будет завершена, он будет автоматически удален.

У меня тоже есть откат() метод на мой UnitOfWork, который позволяет вносить изменения, чтобы быть возвращены в тех случаях, когда какой-то код не удалось, и вы не хотите сохранить изменения.

Одним из недостатков этой архитектуры (как твоя и моя) - это невозможность выполнения операций с использованием UnitOfWork параллельно. Мне недавно нужно выполнить два запроса эф через мои рамки параллельно и не смог сделать так, потому что код по сути заменяет текущую UnitOfWork поскольку архитектура строится вокруг одного HTTP-запроса.

Я обдумывал способы продлить мой нынешний подход, чтобы позволить это.

5
ответ дан 18 октября 2012 в 05:10 Источник Поделиться

Рамках субъект-это единица работы в системе, так что вам не придется изобретать колесо. Только одно вы должны создать: универсальный базовый репозиторий:

interface IRepository<TEntity>
where TEntity : class
{
//Add<TEntity>, Remove<TEntity> ...
}

class /* don't have to be abstract */ BaseRepository<TObjectContext, TEntity> : IRepository<TEntity>
where TObjectContext : ObjectContext
where TEntity : class
{
public RepositoryBase(TObjectContext context)
{
//... store context
}

//implementations
}

И тогда вы можете создать специализированные хранилища.

4
ответ дан 18 октября 2012 в 10:10 Источник Поделиться