Within my SessionView model I'm trying to assign data from the database into a local variable and then assign this data to the relevant public properties (rather than doing it in the controller).
I'm trying to achieve this by using the following code, but it crashes out when the data is interrogated, presumably because I'm defining the same model within it...
public class SessionView : BaseViewModel
{
public int SessionId { get; set; }
private SessionView data
{
get
{
return (from s in db.Sessions
where s.SessionId == SessionId
select new SessionView
{
CourseId = s.CourseId
// ... lots of other properties
}).FirstOrDefault();
}
set { }
}
public int CourseId { get { return data.CourseId; } set { } }
// ... lots of other properties
}
Is there some clever way I can achieve this without erroring?
Thank you.
I think the best way is to put the data retrieval in the constructor:
In this way, everytime someone reference in this class you only retrieve data once which lessen the retrieval overhead and optimized your code.
In simpler terms, everytime I use the variable data i will query it always from the database.
public class SessionView : BaseViewModel
{
private SessionView _sessionView;
public int SessionId { get; set; }
public SessionView()
{
_sessionView = new SessionView();
_sessionView.data = from s in db.Sessions
where s.SessionId == SessionId
select new SessionView
{
CourseId = s.CourseId
// ... lots of other properties
}).FirstOrDefault();
}
private SessionView data
{
get
{
return _sessionView.data
}
set { }
}
public int CourseId { get { return data.CourseId; } set { } }
// ... lots of other properties
}
Where did you read that Controllers should be thin versus fat Models? Business logic isn't great in the controllers because it would be harder to reuse than if in a business logic tier, but don't confuse that with just database access; that definitely should stay away from the models if you can help it.
This kind of work, assigning a database value to your model - that is exactly what the controller in MVC is meant for. I would opt for something like this.
Model
public class SessionView : BaseViewModel
{
public int SessionId { get; set; }
public int CourseId { get; set; }
// ... lots of other properties
}
Controller
public class HomeController : Controller
{
public ActionResult Index(){
var context = new MyContext();
var firstSession = context.Sessions.First();
var viewModel = new SessionView
{
SessionId = firstSession.SessionId,
CourseId = firstSession.CourseId,
//keep populating here if you need
};
return View(viewModel);
}
}
I think your issue is that the data-loading on your model is actually happening in the view, which is a bad practice in MVC, instead you should do it on the controller, by using a service, something like:
public class SessionController : Controller
{
private readonly ISessionsService sessionService;
public SessionController(ISessionsService sessionsService)
{
this.sessionService = sessionService;
}
public ActionResult SessionData(int sessionId)
{
var sessionData = sessionService.GetById(sessionId);
/// do whatever validation you might require here
var model = new SessionView(sessionData); // you could even pass the sessionId if required here
return View(model);
}
}
you can get your service Dependency-Injected into your controller. I think this is the preferred way to do this on MVC
Related
I have a dataset currently that has 4 columns for values lets call them odd_low, odd_high, and even_low, even_high and I want to have two columns in the grid (LOW and HIGH) and have the values set based on the value of another column which will simply be 'O' or 'E' - This column is named side
Here's a quick sample (right now the column is bound to the odd fields only)
columns.Add(model => model.ODD_LOW).Titled("Low House #").Sortable(sortable);
columns.Add(model => model.ODD_HIGH).Titled("High House #").Sortable(sortable);
columns.Add(model => model.SIDE).Titled("Side").Sortable(sortable);
My guess is that I'll need to accomplish this using a script, but I'm not sure how to dynamically access the rows and fields.
I don't see the need for a dynamic datagrid in the scenario you discribed.
When using MVC, it's a good practice to use a ViewModel object to represent data. This way, your controller is resposible for getting the data from your business / service layer and map the result to your ViewModel object.
Using this aproach you can create an object with your view's properties and just bind it to your gridview and other controlls.
Your ViewModel object would look like this:
public class MyViewModelItem {
public int LowHouse { get; set; }
public int HighHouse { get; set; }
public char Side { get; set; }
}
public class MyViewModel {
// Your other view's properties
public List<MyViewModelItem> List { get; set; }
}
And your controller like this:
public class MyController : Controller {
private readonly IMyService myService;
public MyController()
{
myService = new MyService(); // Consider Dependency Injection
}
public ActionResult Index() {
var data = myService.List();
var myModel = MapMyModel(data);
return View(myModel);
}
private MyViewModel MapMyModel(IEnumerable<YOUR_ENTITY> data) {
var myModel = new MyViewModel();
myModel.List = new List<MyViewModelItem>();
foreach (var item in data)
{
myModel.List.Add(new MyViewModelItem {
LowHouse = item.ODD_LOW,
HighHouse = item.ODD_HIGH,
Side = [your logic]
})
}
}
}
References:
Use ViewModels to manage data & organize code in ASP.NET MVC applications
ASP.NET MVC View Model Patterns
I'm trying to achieve maybe something that might be impossible.
We have a big MVC 5 application. I created a small MVC project to simulate and explain what I want to apply into that big MVC project.
I have a controller that has unique Id. In this sample project the unique Id is regenerated for each request. In the MVC project, it is a bit more complex and different. However it's not relevant in the scope of this example.
public class FooController : Controller
{
public string UniqueId = Guid.NewGuid().ToString("N");
public ActionResult Index()
{
var worker = new WorkerA();
worker.DoWork();
return View();
}
}
The FooController creates WorkerA which creates WorkerB which creates WorkerC and so on. The workers are not the same. They don't have the same interface/implementation. To make the example simple I made them look similar.
Here's the Workers:
public class WorkerA
{
public string UniqueId = string.Empty;
public void DoWork()
{
var worker = new WorkerB();
worker.DoWork();
//...
//...
}
}
public class WorkerB
{
public string UniqueId = string.Empty;
public void DoWork()
{
var worker = new WorkerC();
worker.DoWork();
}
}
I want to have inject the property UniqueId into the worker without having to passing it as a parameter.
I want to avoid having to do this:
public WorkerA(string uniqueId)
{
UniqueId = uniqueId;
}
But I need to do the same for all the other workers.
EDIT
Is there a way to acheive that with ninject?
You can achieve what you want using Microsoft.Practices.Unity in the following manner:
public class WorkerA
{
[Dependency]
public string UniqueId { get; set; }
}
public class WorkerB
{
[Dependency]
public string UniqueId { get; set; }
}
And after that :
var container = new UnityContainer();
container.RegisterType<WorkerA>(new InjectionProperty(nameof(WorkerA.UniqueId),"WorkerAValue"));
container.RegisterType<WorkerA>(new InjectionProperty(nameof(WorkerB.UniqueId), "WorkerBValue"));
Later, you can request the instances from the container with the desired properties configured:
var workerA = container.Resolve<WorkerA>();
var workerB = container.Resolve<WorkerB>();
You can do something like:
worker.GetType().GetField("prop")?.SetValue(worker, "guid");
You could create a singleton class to manage the GUID and deliver it to the child classes that way. This way you can still do it in a constructor but not have to pass it as a parameter
public class GUIDManager
{
private static GUIDManager _instance;
private Guid _activeGuid;
public Guid ActiveGuid {
get { return _activeGuid; }
set { _activeGuid = value; }
}
private GUIDManager()
{
if (_activeGuid == null)
_activeGuid = new Guid();
}
public static GUIDManager GetInstance()
{
if(_instance == null)
{
_instance = new GUIDManager();
}
return _instance;
}
}
public class WorkerB
{
public string UniqueId = string.Empty;
public WorkerB()
{
var manager = GUIDManager.GetInstance();
UniqueId = manager.ActiveGuid.ToString();
}
public void DoWork()
{
var worker = new WorkerC();
worker.DoWork();
}
}
From your question i'm not entirely clear about all the workers in the same request getting the same ID or not. If they all should get the same ID then it's simple:
Wrap the ID in a class and use InRequestScope():
public class BrowserTabId
{
public string browserTabId;
public BrowserTabId(string tabId)
{
if(string.IsNullOrEmpty(tabId))
{
throw new NullArgumentException();
}
this.browserTabId = tabId;
}
public string Id { get { return this.browserTabId; } }
}
Bind<BrowserTabId>()
.ToMethod(ctx =>
new BrowserTabId(HttpContext.Items["BrowserTabId"] as string)))
.InRequestScope();
For testability reasons you can also slap on an interface IUniqueRequestId and create the binding for that.
This will result in all workers / objects created during the same request receiveing the same BrowserTabId. If you don't want to use c-tor injection you can use property injection instead. If you don't want to inject the value all the type, then use a When(..) condition to specify when to inject and when not to. Combine this with the null-object pattern to keep ninject from complaining that it can't inject a requested type.
Property Injection
Adapt a worker as follows:
public class WorkerA
{
[Inject]
public BrowserTabId BrowserTabId { get; set; }
....
}
Note, however, for this to work, like normal constructor injection, it is necessary that either the WorkerA is instanciated by ninject or that Ninject is informed about its existence by Ninject.Inject(workerAInstance)
Scoping
Since you mention that the lifetime of the ID in your actual application is somewhat more complicated, I guess you will have to use something different than InRequestScope - maybe roll your own scope (by using InScope(...)). Or Maybe, InCallScope() is as viable alternative. However, without knowing what exactly it is what you need, it's a bit difficult to advise you properly.
I have a Prototype using WPF + MVVM + PRISM + ENTITY FRAMEWORK
The problem is that im very confuse if i use the ENTITY FRAMEWORK Entities as the Model of the MVVM pattern. I have a Business Logic Layer, and i had problems using mappers on this layer, because im very unhappy on the conversion (Map problem).
What i can do to simplify the code, use a real Model not the Entitie object(for me use the Entitie as model is incorrect on the frontend), with the MVVM pattern on mind... and stay good for changes on the future, it will have 200+ entities on the final version...
Thats my layers...(Please forget about Mapping, since i taked it off putting the EF entities on the ViewModel, but the image represents the correct layers)
Im not using the repository too since i can add it on the end with changes only on the BLL.
VIEW MODEL:
my current prototype do a getall, put it on a grid, and on selectchanged of the grid i put selected item on the textbox, and the save button update this changes to the database.
public class CadastroClienteViewModel : BindableBase, ICadastroClienteViewModel
{
private readonly IClienteBLL _clienteService;
#region Model
//public Cliente ObCliente { get; private set; }
public int ClienteID
{
get { return ((Cliente)cliItems.CurrentItem).ClienteID; }
set
{
((Cliente)cliItems.CurrentItem).ClienteID = value;
OnPropertyChanged("ClienteID");
}
}
public string Nome
{
get { return ((Cliente)cliItems.CurrentItem).Nome; }
set
{
((Cliente)cliItems.CurrentItem).Nome = value;
OnPropertyChanged("Nome");
}
}
#endregion
public CadastroClienteViewModel(IClienteBLL ServiceCliente)
{
//ObCliente = new Cliente();
_clienteService = ServiceCliente;
this.SaveCommand = new DelegateCommand(ExecuteMethodSave);
this.RefreshCommand = new DelegateCommand(ExecuteMethodRefresh, CanExecuteMethodRefresh);
RefreshCommand.Execute(null);
}
private void ExecuteMethodSave()
{
_clienteService.ClienteBLL_Update(((Cliente)cliItems.CurrentItem));
RefreshCommand.Execute(null);
}
private bool CanExecuteMethodRefresh()
{
return true;
}
private void ExecuteMethodRefresh()
{
var personViewModels = _clienteService.ClienteBLL_GetAll();
//cliente = new ObservableCollection<Cliente>(personViewModels);
cliItems = new ListCollectionView(personViewModels.ToList());
cliItems.CurrentChanged += CliItemsOnCurrentChanged;
//OnPropertyChanged("cliente");
OnPropertyChanged("cliItems");
}
private void CliItemsOnCurrentChanged(object sender, EventArgs eventArgs)
{
//OnPropertyChanged("ObCliente");
}
public ICommand SaveCommand { get; private set; }
public ICommand RefreshCommand { get; private set; }
//public ObservableCollection<Cliente> cliente { get; private set; }
public ICollectionView cliItems { get; private set; }
}
MODEL(Im not using it... but i would like):
public class MCliente
{
public int ClienteID { get; set; }
public string Nome { get; set; }
}
EF Entitie:
namespace Sistema.DataEntities.Models
{
public class Cliente
{
public Cliente()
{
}
public int ClienteID { get; set; }
public string Nome { get; set; }
}
BLL:
public class ClienteBLL : IClienteBLL
{
readonly ISistemaContext _context;
public ClienteBLL(ISistemaContext context)
{
_context = context;
}
public IEnumerable<Cliente> ClienteBLL_GetAll()
{
return _context.Cliente.AsEnumerable();
}
public Cliente ClienteBLL_GetByID(int id)
{
return _context.Cliente.Find(id);
}
public bool ClienteBLL_Adicionar(Cliente Obcliente)
{
_context.Cliente.Add(Obcliente);
return _context.SaveChanges() > 0;
}
public bool ClienteBLL_Update(Cliente Obcliente)
{
_context.Cliente.Attach(Obcliente);
_context.Entry(Obcliente).State = EntityState.Modified;
return _context.SaveChanges() > 0;
}
public bool ClienteBLL_Delete(int id)
{
var clubMember = _context.Cliente.Find(id);
_context.Cliente.Remove(clubMember);
return _context.SaveChanges() > 0;
}
I'm adding this as an answer (not a comment) even if it's not a final answer to your question (cause it's opinion-based) but it doesn't fit as a comment. That's just what I would do for a WPF application that requires a database.
I would entirely drop the idea of directly connecting your WPF application to your database. I would build a 3-tiers architecture, i.e. I would create a stateless webservice that does all the stuff on server side.
So you would have:
the database
the webservice (using WCF), that is connected to the database, that does all the data stuff for you (I would even make it responsible of the business stuff too)
the WPF application, that is connected to the webservice:
the View layer is your XAML + your code-behind
the ViewModel layer is, well, your ViewModels (out of scope of your question, but feel free to ask if you have any question about that layer). The ViewModels asynchronously call the webservice
the Model is the client WCF proxy
Some benefits of this approach:
depending on the hardware/network harchitecture, could be a huge performance benefit to only make ONE call to the server instead of N calls (assuming the latency between the DB and the webservice (both on "server side") is lower than the one between the WPF application and the database)
more scalable
all benefits of the stateless approach: one Entity Framework context instantiation per webservice requests, so much easier to deal with concurrency issues (in case you have N WPF instances running concurrently)
easier to maintain (loose coupling between tiers)
easier to test (assuming you actually build tests)
better security (no need to expose a direct access to the database over the network)
I'm working on a website, where I need to retrieve pricelists, from another database on the same SQL Server as my Umbraco database.
It's a requirement that it has to be in a separate database.
I have made a new connection string Pricelist and used EF database-first.
PriceList repository:
namespace UmbracoCMS.Repository{
using System;
using System.Collections.Generic;
public partial class Prisliste
{
public string Kode { get; set; }
public string Speciale { get; set; }
public string Ydelsesgruppe { get; set; }
public string Gruppe { get; set; }
public string Ydelse { get; set; }
public string Ydelsestekst { get; set; }
public string Anaestesi { get; set; }
public string Indlæggelse { get; set; }
public Nullable<double> Listepris { get; set; }
public Nullable<int> WebSort { get; set; }
public string YdelsesTekstDK { get; set; }
public string Frapris { get; set; }
public Nullable<int> Sortering { get; set; }
}
}
PriceListController class:
using System;
using System.Linq;
using System.Web.Mvc;
using UmbracoCMS.Repository;
namespace UmbracoCMS.Controllers{
public class PriceListController : Umbraco.Web.Mvc.SurfaceController {
[HttpGet]
public PartialViewResult GetPriceList(string contentTitle){
var db = new PricelistContext();
var query = from b in db.Prislistes orderby b.Speciale select b;
Console.WriteLine("records in the database:");
foreach (var item in query)
{
Console.WriteLine(item.Speciale);
}
return PartialView("~/views/partials/PriceList.cshtml");
}
}
}
What I want is to load the prices for a treatment, based on a property on the document type. I'm just not sure how do this in umbraco since I'm fairly new a umbraco.
So when a treatment page is requested, I need to take the property ContentTitle value. Use it to retrieve all records with the same Speciale and display them in a list/table.
With a query
.where(b.Speciale = contentTitle)
It would be great if someone could help a little, or lead me in the right direction.
Also is it possible to do it in the same http request? Or should I use partial view or macros to both get the properties of the document type, from the umbraco database, and the records from the pricelist database at the same time when a user go to the treatment page?
Or is there a better way to do this?
Update:
Thanks a lot, for the great answer Ryios.
I got a question more.
using System;
using System.Linq;
using System.Web.Mvc;
namespace UmbracoCMS.Controllers
{
public class PriceListSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public ActionResult GetPriceList(string contentTitle)
{
PricelistContext.RunInContext(db =>
{
var result = db.Prislistes.OrderBy(p => p.Speciale);
});
return View(result);
}
}
}
I got it working, so it call the method and the data from the Pricelist Database is shown in:
var result = db.Prislistes.OrderBy(p => p.Speciale);
Now I just need to get the list of prices out to the view again, so I can show a list or table of the prices.
Do you have a suggestion on how I can this in Umbraco. Normally I would return a ViewModel in MVC like:
return View(new ListViewModel(result));
and use it in the view like:
#model Project.ViewModels.ListViewModel
So I can loop through it.
But I want to still have the properties from the the "Home"/"TreatmentPage" Document type.
Should I do it with a partialView or is there a better way?
Solved
I thought I wanted to share it, if anyone else is in a similar situaction.
Controller:
namespace UmbracoCMS.Controllers
{
public class PriceListSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public PartialViewResult PriceList(string contentTitle)
{
List<Prisliste> result = null;
PricelistContext.RunInContext(db =>
{
result = db.Prislistes.Where(p => p.Speciale == contentTitle)
.OrderBy(p => p.Speciale).ToList();
});
var model = result.Select( pl => new PrislistVm()
{
Speciale = pl.Speciale,
Listepris= pl.Listepris
});
return PartialView(model);
}
}
}
ViewModel:
namespace UmbracoCMS.ViewModels
{
public class PrislistVm
{
public PrislistVm()
{
Results = new List<Prisliste>();
}
public List<Prisliste> Results { get; set; }
public string Speciale { get; set; }
public double listepris { get; set; }
}
}
View/PriceListSurface:
#model IEnumerable<UmbracoCMS.ViewModels.PrislistVm>
#{
ViewBag.Title = "PriceList";
}
<h2>PriceList</h2>
#foreach (var item in Model)
{
#item.Speciale
#item.Listepris
}
Your going to have a memory leak if you load your EF context like that. I recommend creating a method to wrap it for you with a llambda callback. Put it in your context class.
public static void RunInContext(Action<PricelistContext> contextCallBack)
{
PricelistContext dbContext = null;
try
{
dbContext = new PricelistContext();
contextCallBack(dbContext);
}
finally
{
dbContext.Dispose();
dbContext = null;
}
}
//Example Call
PricelistContext.RunInContext(db => {
var result = db.PrisListes.OrderBy(p => p.Speciale);
//loop through your items
});
To get the Value of the DocumentType, it depends on the calling context. Assuming you are using a Razor Template that is attached to the document type, that is associated with a Content Page.
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
Layout = "ContentPageLayout.cshtml";
}
#* Call GetPriceList on PriceListController with Parameter contentTitle *#
#Html.Action("GetPriceList", "PriceListSurface", new { contentTitle = Model.Content.GetPropertyValue<string>("ContentTitle") });
In the above example, I have created a document type with a property called ContentTitle that is associated with a view called ContentPage. Then I created content in the backoffice Content section called "Home" that uses the document type. Giving me a url like
http://localhost/home
Also, your SurfaceController will not work. Umbraco's logic for mapping the routes for surface controllers has some requirements for your surface controller's naming conventions. You have to end the name of the class with "SurfaceController" and then it get's called PriceListSurfaceController, then it maps the controller with a name of "PriceListSurface".
Here's the documentation for the SurfaceController features.
http://our.umbraco.org/documentation/Reference/Mvc/surface-controllers
Using a surface controller is the right logic. It's not good practice to have your Data Layer code calls in the UmbracoTemplatePage. 1, because RazorTemplates are interpreted/compiled and SurfaceController's are JIT compiled int the dll, so SurfaceController code is WAY faster. 2 Because you can make asynchronous Controller calls in MVC Razor. If it was all in the view it would make it really difficult to convert everything to be asynchronous. It's best to keep server side logic in a controller.
Optionally, you can Hijack an Umbraco route and replace it with a custom controller that doesn't have to inherit from SurfaceController, which makes it possibly to surface content to the browser that is or isn't part of umbraco.
http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers
You can also create a new section in the backoffice to manage your Price List "the ui framework for building one is written against AngularJS"
http://www.enkelmedia.se/blogg/2013/11/22/creating-custom-sections-in-umbraco-7-part-1.aspx
Im having some problems saving an object (FeatureType) that have a 1-M relationship with Section.
public class FeatureType
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("SectionId")]
public Section Section { get; set; }
public virtual List<ItemType> ItemTypes { set; get; }
}
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public int Order { get; set; }
public virtual List<FeatureType> Features { get; set; }
}
If The ItemTypes are new i have no problem and the insert is done correctly.
But if i want to add some existing ItemTypes im getting this Error:
An entity object cannot be referenced by multiple instances of
IEntityChangeTracker.
I have been reading about this problem but i havent found a way to solve it, and it might be because of how its designed my application.
Whem im mappinig from my viewModel to my Model, im getting the section ID and getting the section Object from my SectionRepository as this:
private Section GetSection()
{
var section = _sectionRepository.GetSection(SectionId);
return section;
}
And this is what is giving me the problem, as the section is now been tracked by the SectionRepository that have its own context.
How can i solve this? I have tried just creating a new section with the existing ID but it just create me an empty object.
private Section GetSection()
{
var section = new Section{Id=SectionId};
return section;
}
UPDATE
To save my entity i just use :
_repository.Create(featureType.ToModel());
public FeatureType ToModel()
{
var ft = new FeatureType
{
Name = Name,
ControlType = (ControlType)ControlType,
Order = Order,
Required = Required,
RequiredText = RequiredText,
ItemTypes = GetItemTypes().ToList(),
Section = GetSection(),
};
return ft;
}
UPDATE 2: This is how i have my repositories, i wouldn't like to manage any EF in my controller but with some kind of repository or service.
public class EFBaseRepository
{
protected MyContext Db = new MyContext();
public void Dispose(bool disposing)
{
Db.Dispose();
}
}
public class EFFeatureTypeRepository : EFBaseRepository, IFeatureTypeRepository
{
public IQueryable<FeatureType> GetFeatureTypes
{
get { return Db.FeatureTypes.Include("Section").Include("ItemTypes"); }
}
public Message Create(FeatureType feature)
{
try
{
Db.FeatureTypes.Add(feature);
Db.SaveChanges();
return new Message();
}
catch (Exception e)
{
throw;
// return new Message(e, string.Format("Error Creating {0}", feature.GetType()));
}
}
//..Other Methods
}
You say that the SectionRepository has its own context. That is going to cause you problems. The repositories should share a context. The context is a combination of the unit of work and repository patterns. You need to separate the two patterns:
How to migrate towards unit-of-work and repository pattern
EDIT
You can avoid having the DbContext in the Controller by implementing your own Unit Of Work pattern.
public interface IUnitOfWork : IDisposable
{
ISectionRepository SectionRepository {get;}
//etc
int Save();
}
then in your controller:
public ActionResult Create(FeatureTypeCreate featureType)
{
_Uow.SectionRepository.Create(featureType.ToModel());
_Uow.Save(); //Saving is the responsibility of the Unit Of Work
//not the Repository
}
More references:
Implementing the Repository and Unit of Work
Repository and Unit of Work in Entity Framework
John Papa's original source code
Simply, the error you're getting means that the entities were returned from a different instance of your DbContext than from which they are now trying to be saved. Make sure that you're not doing something like using two different usings around your repository and that your repository always makes use of the same DbContext per instantiation.