I'm working with creating a partial view to allow the end user to add an item for a later submission.
I have the following class defined as a ViewModel:
public class AddItemsContext : DbContext
{
public DbSet<Item> Items { get; set; }
}
Connection string:
I then have an AJAX call that uses this function to add an item to my partialview:
[HttpPost]
public ActionResult Index_AddItem(SearchedItem viewModel, string Text, string Value)
{
AddItemsContext db = new AddItemsContext();
string test = Request.Form.GetValues("cbxPerm")[0].ToString(); // to be used later
Item NewItem = new Item();
NewItem.ItemName = "test"; // just set it to a temp var for testing
db.Items.Add(NewItem);
db.SaveChanges();
return PartialView("_AddItems", db.Items);
}
The item list does build in my partial view but then I noticed the list persists even after stopping and restarting my app. I thought it may be writing in the SQL DB but I don't see it there either.
I found this: where is data stored? but I don't have any other datasource defined other than my main app's DB.
I also read this: DbContext disposing? which appears to be saying that the data should be disposed on exit.
This partial view needs to add/remove items before the form is submitted for a DB write.
I'm trying to understand why/where the data is being stored permanently?
Related
Anyone using DevExtreme to create ASP.NET MVC project here? I just want to create a simple drop down box that bind data source come from the controller. Here is how it works inside the controller, I create a list of object, then I add the model having two fields "Key" and "Value" into the list. So I pass the list of object in return view in the controller.
As you can see from the screenshot above while I debugging the View, the Model is the list of object that I passed in, it has data for sure. You can see there are two fields "Key" and "Value" from each item, and I also specified the ValueExpr and DisplayExpr. When I run the project, this is the result I get. I just don't know why it is still blank while my data does exist. Am I missing some code?
Without a little more detail it is tough to know for sure, but when I have run into this issue, I fixed it by checking:
If the model was in the wrong format (Not a list/IEnumerable)
The model class fields were set to private
I would also look at this link/demo DevExtreme has for SelectBox: https://js.devexpress.com/Demos/Widgetsgallery/Demo/SelectBox/Overview/Mvc/Light/
OR
DropdownBox:
https://js.devexpress.com/Demos/WidgetsGallery/Demo/DropDownBox/SingleSelection/Mvc/Light/
Otherwise, this is what I use to fill my chosen editor (I am using Dapper for SQL call):
View
#(Html.DevExtreme().SelectBox()
.ID("DemoSelectBox")
.DataSource(d => d.Mvc()
.LoadAction("GetDemoData"))
.DisplayExpr("DemoName")
.ValueExpr("DemoValue"))
Controller
[HttpGet]
public ActionResult GetDemoData(DataSourceLoadOptions loadOptions)
{
List<DemoData> demoDataGrid= new List<DemoData>();
using (SqlConnection connection = new SqlConnection(_connectionString))
{
demoDataGrid= connection.Query<DemoData>(
sql: #"SELECT DemoValue
,DemoName
FROM DemoDatabaseTable"
).ToList();
}
return Content(JsonConvert.SerializeObject(DataSourceLoader.Load(demoDataGrid, loadOptions)), "application/json");
}
Model Class
public class DemoData
{
public string DemoValue{ get; set; }
public string DemoName{ get; set; }
}
I have this action method in MVC which retrieves data from table using a DbContext:
public ActionResult Index()
{
TwitterContext context = new TwitterContext();
List<TwitterUser> Users = context.User.ToList();
return View(Users);
}
It retrieves the values as expected:
But when I use the same code as a service, it retrieves nothing:
I am calling the service method from the client like this
public class TwitterController : Controller
{
// GET: Twitter
public ActionResult Index()
{
TwitterServiceReference.TwitterContractClient client = new TwitterServiceReference.TwitterContractClient("BasicHttpBinding_ITwitterContract");
List<TwitterServiceReference.TwitterUser> user = client.User().ToList();
return View(user);
}
}
I am using the same connection strings in both cases
<add name="TwitterContext"
connectionString="Integrated Security=true;initial Catalog=TwitterDatabase;server=MYNAME-PC\SQLEXPRESS"
providerName="System.Data.SqlClient"/>
I have all the data filled in the table:
Why is the data empty? How to solve this?
UPDATE:
After checking the SQL Server i see that a database is generated with the name
"TwitterService.TwitterContext , the database is autogenerated when i run the app, and the autogenerated tables data is empty, that is why i am getting empty data returned.
i think you forgot to copy your web.config connection string to that app.config.This might be the issue.
Edit:
i mean the project in which your DataContext class resides
The TwitterUser type you are using in MVC is in namespace Twitter.Models but the one you are using in WCF is in namespace TwitterServiceReference. Therefore, EF will treat them like different things. Your TwitterContext is also in different namespaces and as a consequence this is what is happening (from your comment):
I Just checked the SQL Server Management studio, i think the code created database named "TwitterService.TwitterContext" don't know how and with the same tables as DbContext class
And since it is a brand new database, it has no data and therefore in the WCF service you are not getting any records.
You can have different contexts and use the same database by specifying the same connection string as shown below:
public class TwitterContext : DbContext
{
public MovieDBContext()
: base("TwitterContext")
{
}
}
A Better Approach
Create a separate project and put all your DbContext related code in that project. Most people will refer to this as the Data Access Layer. Then use that project in both the MVC application and the WCF services. This way you will share the TwitterUser and other types in both, and possibly more, applications. You may use Repository Pattern to implement this layer.
But what if my MVC application needs additional info?
There will be cases where TwitterUser may not be sufficient for your MVC views and you may need additional properties. In that case, you can create a Model specifically for that view (some people call this view model but I stay away from that since that is a term used in MVVM and it is very different). The class can be designed like this:
public class TwitterUserModel
{
public TwitterUserModel(TwitterUser user)
{
this.TwitterUser = user;
}
public string AnotherPropertyNeededByView {get; set;}
public TwitterUser TwitterUser { get; set; }
}
Or you can use AutoMapper or similar mappers to map from TwitterUser to TwitterUserModel.
After some trials i got the success
i just added this code
public class TwitterContext : DbContext
{
//Added this Code with the connection string
public TwitterContext() : base(#"Integrated Security=true;server=MYNAME-PC\SQLEXPRESS;database=TwitterDatabase")
{
//Disable initializer
Database.SetInitializer<TwitterContext>(null);
}
public DbSet<TwitterUser> User { get; set; }
public DbSet<TwitterUserData> UserData { get; set; }
}
I first tried adding "TwitterContext" as base("TwitterContext") which is the name of my connection string and matches with my DbContext class name which did not work, so i directly added the connection string, the data is retrieved now and is preventing from generating new database.
In my database table I have a column in which the values are manipulated before saving into the database. The logic for the manipulation was added at a later stage of development, after a lot of values were inserted to the table. Now I want to edit the contents of the table to manipulate the values of the existing contents.
My approach
To call the edit function for all the items in the table as the manipulation logic is added in the EDIT action method as well.
When I call the edit function while looping through the contents in the database, I get a null reference exception which is not there when I use the edit function from the UI.
EDIT Action method
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
// Valuestring from another function
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
The call to this function was made from the HomeController. I commented all the return statements in the EDIT method while calling it from the HomeController.
Exception
Object reference not set to an instance of an object.
Question
Why does the edit work while called from the UI without any Exception , but not from HomeController?
Why is the user null even when I call the function from a different controller? Using windows authentication. How do I get the user name if the user has already been authenticated?
EDIT - Added how the Edit function is called
//Home controller (But I have also tried calling the function from a different controller where the call to the method is from the UI
public ActionResult Index()
{
test();
return View();
}
public void test()
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
}
}
Update - General Overview of the problem
The question can be generalized so as to find an answer how the User property is defined when using Windows Authentication.
Is it that the controller gets access to the Windows user property only when it is called from the UI?
Is it not possible to access the User Property in a function that is not called via UI?
And the best thing as far as I know about the issue is that, the User is defined in the Controller where the testmethod is called but not in the Edit in another controller.
You are partially correct when you are saying you can only access the User property only accessing from the UI. The correct answer is -
When accessing the Edit method through the UI, it is actually going through the ASP.Net controller initializer pipeline and that environment takes care of the current browser session and assigns the User variable. HttpContext is available at this time.
But when you are creating the controller variable like this -
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
You are bypassing all those initialization codes. No HttpContext and this is just another normal object and thus it does not have the User property populated.
ANSWER TO QUESTIONS:
Is it that the controller gets access to the Windows user property only when it is called from the UI?
=> Yes, absolutely right, because you are going through the ASP.Net pipeline. But it is not only for Windows user, it is all those things that re in a HttpContext.
Is it not possible to access the User Property in a function that is not called via UI?
=> Only if, you manually assign it otherwise NO.
MORE INSIGHT:
Basically, what you are trying to achieve is a very poor design. Nowhere, remember nowhere, you are supposed to call a controller from inside a controller unless it is a subclass to base method call. The only way you can call another controller from inside another controller is redirecting the execution by using "Redirect" methods. No one will stop you from calling controller like this, but this shows poor design principle..
The best way to solve your situation is like this -
public class ModelService {
public void Edit(IPrincipal user, SetValue setValue){
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
Then in both the controllers constructors -
public class ControllerOne : Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
_modelService.Edit(User, Setvalue);
}
}
//controller 2
public class HomeController: Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Index()
{
foreach(var item in db.SetValue.ToList())
{
_modelService.Edit(User, item);
}
}
}
You can take help from IoC Container for dependency injection, that is even better approach.
Invoking a Controller from within another Controller is probably getting some data (as the User) to be missing. I wouldn't be making much effort understanding why (unless curious), since doing it this way might be considered as bad design. (I bet some other info would be missing as well, maybe cookies and such). the better thing you can do is to separate the logic from your Controllers into some service layer and call the methods with the IPrincipal User as parameter. If you are forced to do it the way you described, then send the user as a parameter to the other controller.
public ActionResult Index()
{
test(User);
return View();
}
public void test(IPrincipal user)
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item, user);
}
}
And the oter controller
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue, IPrincipal user = null)
{
var currentUser = User == null? user : User;//or somthing like that
}
Didn't check this code. but it should give you something to work with.
I am currently typing this from my phone, so I don't have any code at hand. But I'll try to explain the situation.
Taking the example of a Recipe and Variations to this Recipe. The Recipe in this case holds a collection of Variations.
I have a Create view that takes the Recipe as the model.
I want this view to contain a list of Variations to the Recipe already. So there is an 'Add variation' button that opens a dialog with a partial view to add this variation.
Given that this is a yet non existing Recipe, I can't save the variation to the database yet, because it requires the RecipeId. So I think I am looking for a temporary place to store the new variations until I am saving the Recipe, and then also save the variations with it.
As MVC is stateless, I can't really save the variations anywhere temporarily, except for possibly the viewdata or tempdata, or I could add a bunch of hidden fields to the page via JavaScript, so that the Variation entries will be taken into the post triggered by saving the Recipe. All of which don't sound particularly tidy solutions.
What would be the most clean, ideal solution here?
A couple of possible solutions:
Reconsider to use a wizard flow for creation instead - create recipe screen (no variations), then a chain of create variation screens. After that when you edit you can do it in the same page since the recipe is saved already.
As dreza mentionned, the variation partial view could return html to the creation page that adds the fields needed in the form for a full submit of the recipe + variations.
Pre-generate the RecipeId on the create form and reference to it for every actions, so you could save Variation (Id, RecipeId, ...) in the database before saving Recipe if you have different table/document. Then you lose the autogenerate id from SQL/Entity tho, and it's harder to maintain.
If you'd use a document database, you could have had the Recipe document have a list of VariationId and create the Variation on it's own without any direct reference to Recipe directly (No RecipeId in Variation). In SQL/Entity tho you would need 3 tables - Recipe (RecipeId), Variation (VariationId), RecipeVariation (RecipeId,VariationId) - it's uglier.
However, with complex, error prone UI like a parent + childs and such, another approach, and one I used extensively, is to create InputContexts associated to users and have InputModels are well as your normal models. These are semi-temporary models that exists until the user finishes to do his entry, they are usually used as ViewModels directly. For example, you could have:
public class InputContext
{
public int Id { get; set; }
public int UserId { get; set; }
public string RouteName { get; set; }
public DateTime TimeStamp { get; set; }
//...
}
public class RecipeInput
{
public int Id { get; set; }
public int ContextId { get; set; }
// ...
}
public class VariationInput
{
public int Id { get; set; }
public int ContextId { get; set; }
// ...
}
Your partial view that creates variation could simply save them to VariationInput with the current ContextId and that's it.
Then in the controller:
[HttpGet]
public ActionResult Create()
{
// We should check if one exists and reuse it instead, but omitted for clarity
var context = new InputContext(GetUserId(), GetRouteName());
db.Contexts.Add(context);
db.Save(context);
var model = new RecipeInput(context.Id);
return View(model);
}
[HttpPost]
public ActionResult Create(RecipeInput model)
{
// Save the current input to DB
db.RecipeInputs.Update(model);
db.Save();
// Do validation and return Create view on error...
// load the context and related variations created
var context = db.Contexts.Find(model.ContextId);
var children = db.VariationInputs.Where(x => x.ContextId == context.Id).ToList();
// Create the actual models from the input.
var recipe = new Recipe();
// set values from model
foreach (var child in children )
{
var variation = new Variation();
// set values from child
recipe.Variations.Add(variation);
}
db.Recipes.Add(recipe);
db.Save();
// Cleanup if it worked
db.RecipeInputs.Delete(model.Id);
foreach (var child in children )
{
db.VariationInputs.Delete(child.Id);
}
// you could keep the Contexts as logs or delete them
//...
}
This is quite more complicated, but it can be used to save user data even between browser restart, session timeouts (they leave for lunch and come back and have to input everything again...), etc, and do some other neat tricks.
I usually store them in another database/partition/schema, and when I use SQL as a backend (because that's what the client wants), I often try use a Document Database (RavenDB or MongoDB for example) to store the context and input models as it is much nicer to play with in terms of MVC. This is not client data so it doesn't matter much.
If you need to see the list of variations in the Create Recipe page, you simply need to do have a partial view/ajax that gets a list of VariationInput in the current ContextId and display these.
I have created a program with Windows Forms in C# and the architecture is something like this
* BaseClass.cs
* EntityClass.cs
* ControllerClass.cs
* DataAccessClass.cs
* Viewer.cs
So basically the Entity class inherits from the base class, and the data access class goes and retrieves data from a MySql database.
What I want to do is to be able to use the data pulled without having to call the data access class if I already pulled it once.. where can I put this data so I can access it anywhere?..
I was reading about serializing into memory.. but I want to get a good advise here on where to put it and if possible how. I would really appreciate any pointers.
Regards
Seems like you want to continue with the MVC pattern, and introduce a "Model" component that is shared between your 2 views. The Model would be initialized/refreshed using your DataAccessClass. Each form (or View) should have a reference to the Model. I would not suggest using serialization as you are just sharing an in-memory C# object in the same process between 2 different Forms.
For example:
public class MyFirstView : Form
{
private ModelClass m_model;
public MyFirstView(ModelClass model)
{
m_model = model;
m_model.OnDataRefresh += this.Model_OnDataRefresh;
}
}
public class MySecondView : Form
{
private ModelClass m_model;
public MySecondView(ModelClass model)
{
m_model = model;
m_model.OnDataRefresh += this.Model_OnDataRefresh;
}
}
public class ModelClass
{
private DataAccessClass m_dataAccess;
public event EventHandler OnDataRefresh = {}; // fired when data is refreshed
public void EnsureDataIsLoaded(); // queries the db if we haven't already
public void RefreshData(); // refreshes the data from the db
public IList<Entity> GetDataList(); // access to data items
}
For each Form/View, you can use the Form.Load event to refresh the view with the model data. Perhaps you could have a method on the ModelClass.EnsureDataIsLoaded() that will use the DataAccessClass to query the database if the you haven't already.
And lastly, if the model changes, you need some way to push the changes to the view(s). One way to do this is have the model fire an event when the data is refreshed, and each view subscribes to that event.
I would suggest using a static class to hold the data. If you will have multiple forms opened simultaneously, you could have a Dictionary within the static class keyed by some form identifier.
You shouldn't need to serialize anything, you can just hold on the to the references in some sort of cache layer.
Assuming that your data access classes take some sort of query as a parameter, you can store the results in a dictionary keyed by the query. Then you check the cache first for query results, if not there go to the database, retrieve it, and cache it. If you are using lambda expressions for your queries, you can call the .GetHashCode() method to get a unique hash for that lambda.
EX:
IDictionary<string, IEnumerable<T>> Cache;
IEnumerable<T> GetData<T>(string query)
{
var key = typeof(T).Name + query;
if (!this.Cache.ContainsKey(key))
{
// get from database
var data = SomeRepository.GetData(query);
this.Cache[key] = value;
}
return this.Cache[key]
}
I suggest you to take a look at the System.Runtime.Caching.MemoryCache class. It might serve your need.