где бриз вписывается в более сложную архитектуру

Я пытаюсь вписать breezeJS в мою существующую архитектуру. У меня есть структура, как

  1. Представление на основе html/JS/Angular :: с использованием горячего полотенца angular.
  2. контроллеры веб-API ::, которые вызывает представление.
  3. Уровень служб ::, который вызывается из веб-API. Сюда подходит любая бизнес-логика.
  4. Единица работы :: И (если) бизнес-логика требует общения с базой данных для CRUD, она вызывает UOW.
  5. Шаблон репозитория :: UOW фактически оборачивает репозитории. и репозитории, в свою очередь, общаются с DbContexts.

До сих пор я мог преобразовать обычную реализацию репозиториев в реализацию с использованием

public EFContextProvider<MyContext> DbContext { get; set; }

вместо просто DbContext, и я также выставляю метаданные, используя строковое свойство в UOW, а IQueryables возвращаются с использованием DbContext.Context.SomeEntity

Вопрос 1: Я на правильном пути?? Вопрос 2. Большинство примеров предлагают один метод SaveChanges, который дает вам все измененные объекты и сохраняет их сразу. Что, если я хочу запустить некоторую бизнес-логику перед добавлением, обновлением и удалением. я хочу вызвать метод службы AddSomething и хочу, чтобы объект определенного типа отправлялся в AddSomething и запускал некоторую бизнес-логику перед сохранением. Как я могу собрать это вместе.

мой код выглядит так

  [BreezeController]//This is the controller 
public class BreezeController : ApiController
{
    private readonly ISomeService someService;
    public BreezeController(ISomeService someService)
    {
        this.someService = someService;
    }
    // ~/breeze/todos/Metadata 
    [HttpGet]
    public string Metadata()
    {
        return someService.MetaData();
    }

    // ~/breeze/todos/Todos
    // ~/breeze/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt 
    [HttpGet]
    public IQueryable<Node> Nodes()
    {
        return nodesService.GetAllNodes().AsQueryable();
    }

    // ~/breeze/todos/SaveChanges
    //[HttpPost]
    //public SaveResult SaveChanges(JObject saveBundle)
    //{
    //    return _contextProvider.SaveChanges(saveBundle);
    //}

Ниже приведен сервис

 public class SomeService : BaseService, ISomeService
{
    private readonly IUow Uow;

    public SomeService(IUow Uow)
        : base(Uow)
    {
        this.Uow = Uow;
    }
    public IEnumerable<Something> GetAllNodes()
    {
        return Uow.Somethings.GetAll();
    }
}

каждый сервис может выставлять одно свойство через базу. на самом деле это метаданные

public class BaseService : IBaseService
{
    private readonly IUow Uow;
    public BaseService(IUow Uow)
    {
        this.Uow = Uow;
    }
    public string MetaData()
    {
        return Uow.MetaData;
    }
}

и мой UOW выглядит так

    public class VNUow : IUow, IDisposable
{
    public VNUow(IRepositoryProvider repositoryProvider)
    {
        CreateDbContext();

        repositoryProvider.DbContext = DbContext;
        RepositoryProvider = repositoryProvider;       
    }

    // Code Camper repositories

    public IRepository<Something> NodeGroup { get { return GetStandardRepo<Something>(); } }
   } }
    public IRepository<Node> Nodes { get { return GetStandardRepo<Node>(); } }
    /// <summary>
    /// Save pending changes to the database
    /// </summary>
    public void Commit()
    {
        //System.Diagnostics.Debug.WriteLine("Committed");
        DbContext.Context.SaveChanges();
    }
    public string MetaData   // the Name property
    {
        get
        {
            return DbContext.Metadata();
        }
    }
    protected void CreateDbContext()
    {
    //    DbContext = new VNContext();

        DbContext = new EFContextProvider<VNContext>();
        // Load navigation properties always if it is true
        DbContext.Context.Configuration.LazyLoadingEnabled = false;


        // Do NOT enable proxied entities, else serialization fails
        DbContext.Context.Configuration.ProxyCreationEnabled = true;

        // Because Web API will perform validation, we don't need/want EF to do so
        DbContext.Context.Configuration.ValidateOnSaveEnabled = false;

        //DbContext.Configuration.AutoDetectChangesEnabled = false;
        // We won't use this performance tweak because we don't need 
        // the extra performance and, when autodetect is false,
        // we'd have to be careful. We're not being that careful.
    }

    protected IRepositoryProvider RepositoryProvider { get; set; }

    private IRepository<T> GetStandardRepo<T>() where T : class
    {
        return RepositoryProvider.GetRepositoryForEntityType<T>();
    }
    private T GetRepo<T>() where T : class
    {
        return RepositoryProvider.GetRepository<T>();
    }

    private EFContextProvider<VNContext> DbContext { get; set; }

    #region IDisposable

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (DbContext != null)
            {
                DbContext.Context.Dispose();
            }
        }
    }

    #endregion
}

в конце Repository Implementaion выглядит так

   public class EFRepository<T> : IRepository<T> where T : class
{
    public EFRepository(EFContextProvider<VNContext> dbContext)
    {
        if (dbContext == null)
            throw new ArgumentNullException("dbContext");
        DbContext = dbContext;
        DbSet = DbContext.Context.Set<T>();
    }

    protected EFContextProvider<VNContext> DbContext { get; set; }

    protected DbSet<T> DbSet { get; set; }

    public virtual IQueryable<T> GetAll()
    {
        return DbSet;
    }
    public virtual IQueryable<T> GetAllEagerLoad(params Expression<Func<T, object>>[] children)
    {
        children.ToList().ForEach(x => DbSet.Include(x).Load());
        return DbSet;
    }
    public virtual IQueryable<T> GetAllEagerLoadSelective(string[] children)
    {
        foreach (var item in children)
        {
            DbSet.Include(item);
        }
        return DbSet;
    }
    public virtual IQueryable<T> GetAllLazyLoad()
    {
        return DbSet;
    }
    public virtual T GetById(int id)
    {
        //return DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate<T>(id));

        return DbSet.Find(id);
    }
    public virtual T GetByIdLazyLoad(int id, params Expression<Func<T, object>>[] children)
    {
        children.ToList().ForEach(x => DbSet.Include(x).Load());

        return DbSet.Find(id);
    }
    public virtual void Add(T entity)
    {
        DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
        if (dbEntityEntry.State != EntityState.Detached)
        {
            dbEntityEntry.State = EntityState.Added;
        }
        else
        {
            DbSet.Add(entity);
        }
    }

    public virtual void Update(T entity)
    {
        DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
        if (dbEntityEntry.State == EntityState.Detached)
        {
            DbSet.Attach(entity);
        }
        dbEntityEntry.State = EntityState.Modified;
    }

    public virtual void Delete(T entity)
    {
        DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
        if (dbEntityEntry.State != EntityState.Deleted)
        {
            dbEntityEntry.State = EntityState.Deleted;
        }
        else
        {
            DbSet.Attach(entity);
            DbSet.Remove(entity);
        }
    }

    public virtual void Delete(int id)
    {
        var entity = GetById(id);
        if (entity == null) return; // not found; assume already deleted.
        Delete(entity);
    }
}

person Raas Masood    schedule 19.11.2014    source источник


Ответы (2)


Breeze поддерживает «Именованные сохранения», когда вы указываете имя конкретной конечной точки сервера (т. е. ваш метод обслуживания) для каждого сохранения. Видеть:

http://www.getbreezenow.com/documentation/saving-changes

Это будет выглядеть примерно так на вашем клиенте.

var saveOptions = new SaveOptions({ resourceName: "CustomSave1" }); 
em.saveChanges(entitiesToSave, saveOptions).then(function (saveResult) {
  // .. do something interesting.

}

и на вашем сервере

[HttpPost]
public SaveResult CustomSave1(JObject saveBundle) {
  ContextProvider.BeforeSaveEntityDelegate = CustomSave1Interceptor;
  return ContextProvider.SaveChanges(saveBundle);
}

private Dictionary<Type, List<EntityInfo>> CustomSave1Interceptor(Dictionary<Type, List<EntityInfo>> saveMap) {
  // In this method you can
  //   1) validate entities in the saveMap and optionally throw an exception
  //   2) update any of the entities in the saveMap
  //   3) add new entities to the saveMap
  //   4) delete entities from the save map.
  // For example
  List<EntityInfo> fooInfos;
  if (!saveMap.TryGetValue(typeof(Foo), out fooEntities)) {
     // modify or delete any of the fooEntites    
     // or add new entityInfo instances to the fooEntities list.
  }

}
person Jay Traband    schedule 20.11.2014

Большая часть этого вопроса является широким вопросом, и ответы будут в первую очередь основаны на мнении... тем не менее, вот мои два цента: будьте проще. Тщательно подумайте, действительно ли вам нужны 3, 4 и 5, особенно если вам нужно реализовать UoW или шаблон репозитория самостоятельно. EF DbContext реализует и то, и другое, вы можете использовать его напрямую в своих контроллерах, если хотите.

Если у вас есть пользовательская логика, которую необходимо выполнить до сохранения изменений, используйте один из методов перехватчика: BeforeSaveEntity или BeforeSaveEntites. Вот документация для этих методов:

http://www.getbreezenow.com/documentation/contextprovider#BeforeSaveEntity

person Jeremy Danyow    schedule 19.11.2014
comment
Перейдите на сайт breeze и взгляните на некоторые образцы, чтобы понять, как настроить свой проект. Как я уже сказал, шаблоны UoW и Repo реализуются с помощью dbcontext. Вам, вероятно, не нужны все слои универсальных классов, если они ничего не делают, кроме как работают с API dbcontext. Кроме того, сервер breeze может располагаться непосредственно поверх dbcontext. Используйте перехватчики, именованные сохранения и т. д., чтобы добавить пользовательскую логику. - person Jeremy Danyow; 22.11.2014
comment
@JeremyDanyow, если вы удалите слои 3,4,5 - где бы вы написали код, связанный с бизнес-логикой? Я предполагаю, что ответ находится в контроллере веб-API, это не кажется хорошей идеей. Если бы вы могли предоставить пример кода для слоя, было бы здорово! - person RuSh; 06.01.2015
comment
@sharru- то, что вы хотите сделать, это тщательно подумать, действительно ли нужны 3, 4 и 5. Например, OP 4 и 5 являются универсальными классами, которые отображают DbContext. Никакой добавленной стоимости, и они не место для бизнес-логики. № 3 — это место, где ОП хочет поместить бизнес-логику. Это можно сделать несколькими способами, в зависимости от того, что это за логика. Например, стандартная проверка EF будет работать для простых требований проверки. В более сложных сценариях может потребоваться какой-либо диспетчер/уровень/служба проверки, который можно вызывать через контроллер/перехватчики ветра. Надеюсь это поможет! - person Jeremy Danyow; 06.01.2015