Pergunta

Estou no processo de projetar meu aplicativo ASP.NET MVC e encontrei alguns pensamentos interessantes.

Muitas amostras que vi descrever e usar o padrão do repositório (IRepository) Então foi assim que eu fiz isso enquanto aprendia o MVC.

Agora eu sei o que tudo está fazendo, começando a olhar para o meu design atual e me pergunto se é o melhor caminho a percorrer.

Atualmente eu tenho um básico IUserRepository, que define métodos como FindById(), SaveChanges(), etc.

Atualmente, sempre que eu quiser carregar/consultar a tabela de usuários no banco de dados, faço algo parecido com o seguinte:

    private IUserRepository Repository;

    public UserController()
        : this(new UserRepository())
    { }

    [RequiresAuthentication]
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Edit(string ReturnUrl, string FirstRun)
    {
        var user = Repository.FindById(User.Identity.Name);

        var viewModel = Mapper.Map<User, UserEditViewModel>(user);
        viewModel.FirstRun = FirstRun == "1" ? true : false;

        return View("Edit", viewModel);
    }

    [AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken(Salt = "SaltAndPepper")]
    public ActionResult Edit(UserEditViewModel viewModel, string ReturnUrl)
    {
        //Map the ViewModel to the Model
        var user = Repository.FindById(User.Identity.Name);

        //Map changes to the user
        Mapper.Map<UserEditViewModel, User>(viewModel, user);

        //Save the DB changes
        Repository.SaveChanges();

        if (!string.IsNullOrEmpty(ReturnUrl))
            return Redirect(ReturnUrl);
        else
            return RedirectToAction("Index", "User");
    }

Agora, não entendo perfeitamente como o MVC funciona em relação à criação de um controlador quando um usuário cria um link (não tenho certeza se há 1 controlador por usuário ou 1 controlador por aplicativo), por isso não tenho certeza do melhor curso de ação.

Encontrei uma ótima pergunta sobre o uso de uma interface de repositório genérico IRepository<T> aqui e também parecem a ideia de uma estática RepositoryFactory em vários blogs. Basicamente, apenas 1 instância do repositório é mantida e é obtida por esta fábrica

Portanto, minha pergunta gira em torno de como as pessoas fazem isso nos aplicativos e o que é considerado uma boa prática.

As pessoas têm repositórios individuais com base em cada tabela (IUserRepository)?
Eles usam um genérico IRepository<T>?
Eles usam uma fábrica de repositório estático?
Ou algo mais completamente?

EDIT: Acabei de perceber que provavelmente deveria perguntar também:

Está tendo um privado IRepository Em cada controlador, um bom caminho a percorrer? ou devo instanciar um novo IRepository Cada vez que eu quero usá -lo?

Edição de recompensa: estou começando uma recompensa para obter mais algumas perspectivas (não que Tim não tenha sido útil).

Estou mais curioso para saber o que as pessoas fazem em seus aplicativos MVC ou o que eles acham uma boa ideia.

Foi útil?

Solução

Alguns problemas muito óbvios com a ideia de um genérico IRepository<T>:

  • Ele pressupõe que toda entidade use o mesmo tipo de chave, o que não é verdadeiro em quase nenhum sistema não trivial. Algumas entidades usarão Guids, outras podem ter algum tipo de chave natural e/ou composta. O Nibernate pode suportar isso razoavelmente bem, mas o LINQ para o SQL é muito ruim nisso - você precisa escrever uma boa quantidade de código de hackish para fazer o mapeamento automático de chaves.

  • Isso significa que cada repositório pode lidar apenas com exatamente um tipo de entidade e suporta apenas as operações mais triviais. Quando um repositório é relegado a um invólucro tão simples, ele tem muito pouco uso. Você também pode apenas entregar ao cliente um IQueryable<T> ou Table<T>.

  • Ele assume que você executa exatamente as mesmas operações em todas as entidades. Na realidade, isso estará muito longe da verdade. Claro, pode ser você quer conseguir isso Order pelo seu ID, mas é mais provável que você queira obter uma lista de Order objetos para um cliente específico e dentro de algum intervalo. A noção de um totalmente genérico IRepository<T> Não permite o fato de que você quase certamente desejará realizar diferentes tipos de consultas em diferentes tipos de entidades.

O ponto principal do padrão do repositório é criar um abstração sobre padrões de acesso a dados comuns. Eu acho que alguns programadores ficam entediados com a criação de repositórios, então eles dizem: "Ei, eu sei, criarei um über-repositório que possa lidar com qualquer tipo de entidade!" O que é ótimo, exceto que o repositório é praticamente inútil para 80% do que você está tentando fazer. É bom como uma classe/interface base, mas se essa é a extensão total do trabalho que você faz, está apenas sendo preguiçoso (e garantindo dores de cabeça futuras).


Idealmente, eu posso começar com um repositório genérico que se parece mais disso:

public interface IRepository<TKey, TEntity>
{
    TEntity Get(TKey id);
    void Save(TEntity entity);
}

Você notará que isso não tenha um List ou GetAll Função - Isso porque é absurdo pensar que é aceitável recuperar os dados de uma tabela inteira imediatamente em qualquer lugar do código. É quando você precisa começar a entrar em repositórios específicos:

public interface IOrderRepository : IRepository<int, Order>
{
    IEnumerable<Order> GetOrdersByCustomer(Guid customerID);
    IPager<Order> GetOrdersByDate(DateTime fromDate, DateTime toDate);
    IPager<Order> GetOrdersByProduct(int productID);
}

E assim por diante - você entendeu a ideia. Dessa forma, temos o repositório "genérico" para que, se realmente precisamos da semântica de recuperação por ID incrivelmente simplista, mas em geral nunca vamos realmente passar isso, certamente não para uma classe de controlador.


Agora, quanto aos controladores, você deve fazer isso direito, caso contrário, você praticamente negou todo o trabalho que acabou de fazer ao reunir todos os repositórios.

Um controlador precisa Pegue seu repositório do mundo exterior. A razão pela qual você criou esses repositórios é para que você possa fazer algum tipo de inversão de controle. Seu objetivo final aqui é poder trocar um repositório por outro - por exemplo, fazer testes de unidade ou se você decidir mudar de LINQ para SQL para a estrutura da entidade em algum momento do futuro.

Um exemplo desse princípio é:

public class OrderController : Controller
{
    public OrderController(IOrderRepository orderRepository)
    {
        if (orderRepository == null)
            throw new ArgumentNullException("orderRepository");
        this.OrderRepository = orderRepository;
    }

    public ActionResult List(DateTime fromDate, DateTime toDate) { ... }
    // More actions

    public IOrderRepository OrderRepository { get; set; }
}

Em outras palavras, o controlador tem Não tenho idéia de como criar um repositório, nem deveria. Se você tem alguma construção de repositório acontecendo lá, está criando um acoplamento que você realmente não deseja. A razão pela qual os controladores de amostra do ASP.NET MVC possuem construtores sem parâmetros que criam repositórios de concreto é que os sites precisam ser capazes de compilar e executar sem forçá -lo a configurar toda uma estrutura de injeção de dependência.

Mas em um local de produção, se você não estiver passando na dependência do repositório por meio de um construtor ou propriedade pública, está perdendo seu tempo com repositórios, porque os controladores ainda estão bem acoplados à camada de banco de dados. Você precisa ser capaz de escrever código de teste como este:

[TestMethod]
public void Can_add_order()
{
    OrderController controller = new OrderController();
    FakeOrderRepository fakeRepository = new FakeOrderRepository();
    controller.OrderRepository = fakeRepository; //<-- Important!
    controller.SubmitOrder(...);
    Assert.That(fakeRepository.ContainsOrder(...));
}

Você não pode fazer isso se o seu OrderController está saindo e criando seu próprio repositório. Esse método de teste não deve fazer acesso a dados, apenas garante que o controlador esteja invocando o método de repositório correto com base na ação.


Isso ainda não é DI, lembre -se, isso é apenas fingindo/zombando. Onde Di entra em cena é quando você decide que o Linq para SQL não está fazendo o suficiente para você e você realmente quer o HQL em Nibernate, mas vai levar você 3 meses para portar tudo, e você quer ser capaz de ser capaz de Faça este repositório de cada vez. Então, por exemplo, usando uma estrutura DI como Ninject, tudo que você precisa fazer é mudar isso:

Bind<ICustomerRepository>().To<LinqToSqlCustomerRepository>();
Bind<IOrderRepository>().To<LinqToSqlOrderRepository>();
Bind<IProductRepository>().To<LinqToSqlProductRepository>();

Para:

Bind<ICustomerRepository>().To<LinqToSqlCustomerRepository>();
Bind<IOrderRepository>().To<NHibernateOrderRepository>();
Bind<IProductRepository>().To<NHibernateProductRepository>();

E aí está você, agora tudo o que depende de IOrderRepository está usando a versão Nibernate, você só teve que mudar uma linha de código em oposição a potencialmente centenas de linhas. E estamos executando as versões LINQ para SQL e Nibernate lado a lado, portando a funcionalidade sobre peça por peça sem nunca quebrar nada no meio.


Então, para resumir todos os pontos que fiz:

  1. Não confie estritamente em um genérico IRepository<T> interface. A maior parte da funcionalidade que você deseja de um repositório é específico, não genérico. Se você quiser incluir um IRepository<T> Nos níveis superiores da hierarquia de classe/interface, tudo bem, mas os controladores devem depender de específico Repositórios para que você não tenha que alterar seu código em 5 lugares diferentes quando descobrir que o repositório genérico está ausente métodos importantes.

  2. Os controladores devem aceitar repositórios de fora, não criar seus próprios. Esta é uma etapa importante para eliminar o acoplamento e melhorar a testabilidade.

  3. Normalmente, você deseja conectar os controladores usando uma estrutura de injeção de dependência, e muitos deles podem ser perfeitamente integrados ao ASP.NET MVC. Se isso for demais para você absorver, pelo menos você deve usar algum tipo de provedor de serviços estático para que você possa centralizar toda a lógica de criação do repositório. (A longo prazo, você provavelmente achará mais fácil aprender e usar uma estrutura DI).

Outras dicas

modelo web:

.

Modelo da Web refere-se ao novo elemento de recurso disponível no SharePoint 2010, que nos fornece maneira flexível de definir a definição (Onet.xml arquivo), que será usado apenas no tempo de provisionamento, quando o site for criada. Não há referências à definição no tempo de execução, que fornecer facilidade de manutenção para a definição.

Fonte < / a>

modelo:

.

modelos de site do SharePoint são definições pré-construídas projetadas em torno de um necessidade particular de negócios. Você pode usar esses modelos como eles são para Crie seu próprio site do SharePoint e personalize o site tanto quanto você gosta. Você provavelmente está familiarizado com os modelos de site padrão, Como o site da equipe, o site do blog e o site de trabalho em grupo, conforme mostrado aqui.

Além dos modelos padrão, você pode criar seu próprio site Modelo com base em um site que você criou e personalizado no SharePoint. Este é um recurso poderoso no SharePoint que permite criar um solução personalizada e, em seguida, compartilhar essa solução com seus pares, o organização mais ampla ou organizações externas. Você também pode empacotar o site e abri-lo em outro ambiente ou aplicativo como Microsoft Visual Studio e personaliza ainda mais lá.

Fonte

site:

.

Uma coleção de sites consiste em um site de nível superior e um ou mais sites abaixo dela. Cada site de nível superior e quaisquer sites abaixo no site estrutura são baseadas em um modelo de site e podem ter outros configurações e conteúdo exclusivo. Partition seu conteúdo de coleta de site em sites separados para obter o controle mais fino da aparência, conteúdo e recursos das várias páginas da coleta do seu site.

fonte < / p >.

Aqui está como estou usando. Estou usando o IREPOSITE para todas as operações que são comuns a todos os meus repositórios.

public interface IRepository<T> where T : PersistentObject
{
    T GetById(object id);
    T[] GetAll();
    void Save(T entity);
}

e eu também uso um itrepository dedicado para cada agregar para a operação que são distintas deste repositório. Por exemplo, para o usuário, usarei o iUserRepository para adicionar método distinto do UserRepository:

public interface IUserRepository : IRepository<User>
{
    User GetByUserName(string username);
}

A implementação será assim:

public class UserRepository : RepositoryBase<User>, IUserRepository
{
    public User GetByUserName(string username)
    {
        ISession session = GetSession();
        IQuery query = session.CreateQuery("from User u where u.Username = :username");
        query.SetString("username", username);

        var matchingUser = query.UniqueResult<User>();

        return matchingUser;
    }
}


public class RepositoryBase<T> : IRepository<T> where T : PersistentObject
{
    public virtual T GetById(object id)
    {
        ISession session = GetSession();
        return session.Get<T>(id);
    }

    public virtual T[] GetAll()
    {
        ISession session = GetSession();
        ICriteria criteria = session.CreateCriteria(typeof (T));
        return criteria.List<T>().ToArray();
    }

    protected ISession GetSession()
    {
        return new SessionBuilder().GetSession();
    }

    public virtual void Save(T entity)
    {
        GetSession().SaveOrUpdate(entity);
    }
}

Do que no UserController parecerá como:

public class UserController : ConventionController
{
    private readonly IUserRepository _repository;
    private readonly ISecurityContext _securityContext;
    private readonly IUserSession _userSession;

    public UserController(IUserRepository repository, ISecurityContext securityContext, IUserSession userSession)
    {
        _repository = repository;
        _securityContext = securityContext;
        _userSession = userSession;
    }
}

Do que o repositório é instanciado usando o padrão de injeção de dependência usando a fábrica de controladores personalizados. estou a usar StructureMap como minha camada de injeção de dependência.

A camada de banco de dados é Nibernate. ISession é o gateway para o banco de dados nesta sessão.

Eu sugiro que você olhe CodecampServer Estrutura, você pode aprender muito com isso.

Outro projeto que você pode aprender é Quem pode me ajudar. O que eu ainda não cavo o suficiente nele.

A imagem a seguir pode fornecer uma visão geral de todo o esquema do aplicativo:

Digite a descrição da imagem aqui

seguindo você pode ler a definição para cada tipo de aplicativo hospedado:

app hospedado do SharePoint

.

Aplicativos ou aplicativos hospedados do SharePoint, onde todos os componentes são hospedados em uma fazenda no Office 365 SharePoint. Os aplicativos hospedados do SharePoint são instalados em um site do SharePoint 2013, chamado Host Web. Eles têm seus recursos hospedados em um subsite isolado de uma Web do host, chamado de Web App. É importante saber a diferença entre webs host e teias de aplicativos. A Figura 1 ilustra a arquitetura básica de um aplicativo hospedado do SharePoint. Digite a descrição da imagem aqui

provedor hospedado aplicativos hospedados

.

Aplicativos hospedados pelo provedor para o SharePoint incluem componentes implantados e hospedados fora do farm do SharePoint. Eles são instalados para a Web do host, mas seus componentes remotos estão hospedados em outro servidor. A Figura 2 ilustra a arquitetura básica de um aplicativo hospedado de provedor. Digite a descrição da imagem aqui

aplicativos autohosted

.

Autohosted Apps para o SharePoint são aplicativos hospedados em nuvem cujo controle remoto Os componentes são provisionados e implantados para você no Windows Azure. Como Com um aplicativo hospedado provedor, um aplicativo AutoHosted para o SharePoint pode Interaja com um site do SharePoint, mas também usa recursos e Serviços que estão localizados em um site remoto hospedado pelo Windows Azure. As provisões de instalação do SharePoint 2013 e implementam estes Recursos para você. Digite a descrição da imagem aqui

Para mais informações, por favor: http://msdn.microsoft.com/en-us/library/office/fp179887(v=office.15).aspx

Você pode encontrar um excelente Repovoso genérico Biblioteca que foi escrita para permitir que seja usada como um WebForms ASP: ObjectDataSource no CodePlex: Multitierlinqtosql

Cada um dos meus controladores possui repositórios privados para as ações necessárias para apoiar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top