I'm following the demo grid-edit inline
It works but not change to database, it only change the list that popolated
what i have to do next
here the code
public static IList<Book> All()
{
IList<Book> result = (IList<Book>)HttpContext.Current.Session["Books"];
if (result == null)
{
HttpContext.Current.Session["Books"] = result =
(from book in new LibraryModel().Books
select new Book
{
PKiBookID=book.PKiBookID,
SBookName = book.SBookName,
SBookWriter= book.SBookWriter,
SDescription=book.SDescription,
BStatus=book.BStatus,
FKiBookType=book.FKiBookType
}).ToList();
}
return result;
}
public static Book One(Func<Book, bool> predicate)
{
return All().Where(predicate).FirstOrDefault();
}
public static void Insert(Book book)
{
//book.PKiBookID = All().OrderByDescending(p => p.PKiBookID).First().PKiBookID + 1;
All().Insert(0,book);
}
public static void Update(Book book)
{
Book target = One(p => p.PKiBookID == book.PKiBookID);
if (target != null)
{
target.SBookName = book.SBookName;
target.SBookWriter = book.SBookWriter;
target.SDescription = book.SDescription;
target.BStatus = book.BStatus;
target.BookType = new LibraryModel().BookTypes.Single(s => s.PKiBookTypeID == book.FKiBookType);
}
}
This exception is quite well known and there are many posts how to avoid it. It is also covered in the Documentation.
Related
I got an MVC 5 application that i'm porting to asp.net Core.
In the MVC application call to controller we're made using AngularJS $resource (sending JSON) and we we're POSTing data doing :
ressource.save({ entries: vm.entries, projectId: vm.project.id }).$promise...
that will send a JSON body like:
{
entries:
[
{
// lots of fields
}
],
projectId:12
}
the MVC controller looked like this :
[HttpPost]
public JsonResult Save(List<EntryViewModel> entries, int projectId) {
// code here
}
How can I replicate the same behaviour with .NET Core since we can't have multiple [FromBody]
you cannot have multiple parameter with the FromBody attibute in an action method. If that is need, use a complex type such as a class with properties equivalent to the parameter or dynamic type like that
[HttpPost("save/{projectId}")]
public JsonResult Save(int projectId, [FromBody] dynamic entries) {
// code here
}
As pointed out in the comment, one possible solution is to unify the properties you're posting onto a single model class.
Something like the following should do the trick:
public class SaveModel
{
public List<EntryViewModel> Entries{get;set;}
public int ProjectId {get;set;}
}
Don't forget to decorate the model with the [FromBody] attribute:
[HttpPost]
public JsonResult Save([FromBody]SaveViewModel model)
{
// code here
}
Hope this helps!
It's still rough but I made a Filter to mimic the feature.
public class OldMVCFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Method != "GET")
{
var body = context.HttpContext.Request.Body;
JToken token = null;
var param = context.ActionDescriptor.Parameters;
using (var reader = new StreamReader(body))
using (var jsonReader = new JsonTextReader(reader))
{
jsonReader.CloseInput = false;
token = JToken.Load(jsonReader);
}
if (token != null)
{
var serializer = new JsonSerializer();
serializer.DefaultValueHandling = DefaultValueHandling.Populate;
serializer.FloatFormatHandling = FloatFormatHandling.DefaultValue;
foreach (var item in param)
{
JToken model = token[item.Name];
if (model == null)
{
// try to cast the full body as the current object
model = token.Root;
}
if (model != null)
{
model = this.RemoveEmptyChildren(model, item.ParameterType);
var res = model.ToObject(item.ParameterType, serializer);
context.ActionArguments[item.Name] = res;
}
}
}
}
}
private JToken RemoveEmptyChildren(JToken token, Type type)
{
var HasBaseType = type.GenericTypeArguments.Count() > 0;
List<PropertyInfo> PIList = new List<PropertyInfo>();
if (HasBaseType)
{
PIList.AddRange(type.GenericTypeArguments.FirstOrDefault().GetProperties().ToList());
}
else
{
PIList.AddRange(type.GetTypeInfo().GetProperties().ToList());
}
if (token != null)
{
if (token.Type == JTokenType.Object)
{
JObject copy = new JObject();
foreach (JProperty jProp in token.Children<JProperty>())
{
var pi = PIList.FirstOrDefault(p => p.Name == jProp.Name);
if (pi != null) // If destination type dont have this property we ignore it
{
JToken child = jProp.Value;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, pi.PropertyType);
}
if (!IsEmpty(child))
{
if (child.Type == JTokenType.Object || child.Type == JTokenType.Array)
{
// nested value has been checked, we add the object
copy.Add(jProp.Name, child);
}
else
{
if (!pi.Name.ToLowerInvariant().Contains("string"))
{
// ignore empty value when type is not string
var Val = (string)child;
if (!string.IsNullOrWhiteSpace(Val))
{
// we add the property only if it contain meningfull data
copy.Add(jProp.Name, child);
}
}
}
}
}
}
return copy;
}
else if (token.Type == JTokenType.Array)
{
JArray copy = new JArray();
foreach (JToken item in token.Children())
{
JToken child = item;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, type);
}
if (!IsEmpty(child))
{
copy.Add(child);
}
}
return copy;
}
return token;
}
return null;
}
private bool IsEmpty(JToken token)
{
return (token.Type == JTokenType.Null || token.Type == JTokenType.Undefined);
}
}
I have an MVC 5 web app which talks to my class library for db needs and the class library uses Entity Framework 6 for that.
Below are 2 methods from the class library.
Both of them are initiating a new context. How do I make 'em use only one context instead, without using class level variable?
Also, if these 2 methods were to save stuff, context.SaveChanges(), how can I wrap them both into one transaction, even if the save happens in different classes?
public int FindUnknownByName(string name, string language)
{
using (var context = new ScriptEntities())
{
int languageId = this.FindLanguage(language);
var script = context.scripts.Where(l => l.Name == name && l.Unknown == true && l.LanguageId == languageId).FirstOrDefault();
if (script != null)
{
return script.Id;
}
return 0;
}
}
public int FindLanguage(string language)
{
using (var context = new ScriptEntities())
{
var lang = context.languages.Where(l => l.Name == language).FirstOrDefault();
if (lang != null)
{
return lang.Id;
}
return 0;
}
}
Make the context a private class variable.
private ScriptEntities context = new ScriptEntities()
public int FindUnknownByName(string name, string language)
{
int languageId = this.FindLanguage(language);
var script = context.scripts.Where(l => l.Name == name && l.Unknown == true && l.LanguageId == languageId).FirstOrDefault();
if (script != null)
{
return script.Id;
}
return 0;
}
public int FindLanguage(string language)
{
var lang = context.languages.Where(l => l.Name == language).FirstOrDefault();
if (lang != null)
{
return lang.Id;
}
return 0;
}
Also implement IDisposable and dispose context.
You can use extension methods:
public int FindUnknownByName(this ScriptEntities context, string name, string language)
{
int languageId = context.FindLanguage(language);
var script = context.scripts.Where(l => l.Name == name && l.Unknown == true && l.LanguageId == languageId).FirstOrDefault();
if (script != null)
{
return script.Id;
}
return 0;.
}
public int FindLanguage(this ScriptEntities context, string language)
{
var lang = context.languages.Where(l => l.Name == language).FirstOrDefault();
if (lang != null)
{
return lang.Id;
}
return 0;
}
then:
using (var context = new ScriptEntities())
{
var language = context.FindUnknownByName('name', 'language')
}
I have two methods for a database entity which gets the translation for selected language if it has one, or the default language translation.
public string GetName(int? LanguageId, int? DefaultLanguageId)
{
string retval = "";
var textInSelectedLanguage = this.CategoryTexts.Where(w => w.LanguageId == LanguageId).SingleOrDefault();
if (textInSelectedLanguage == null)
{
retval = this.CategoryTexts.Where(w => w.LanguageId == DefaultLanguageId).SingleOrDefault().Name;
}
else
{
retval = textInSelectedLanguage.Name;
}
return retval;
}
public string GetDescription(int? LanguageId, int? DefaultLanguageId)
{
string retval = "";
var textInSelectedLanguage = this.CategoryTexts.Where(w => w.LanguageId == LanguageId).SingleOrDefault();
if (textInSelectedLanguage == null)
{
retval = this.CategoryTexts.Where(w => w.LanguageId == DefaultLanguageId).SingleOrDefault().Description;
}
else
{
retval = textInSelectedLanguage.Description;
}
return retval;
}
As you can see they are very similar. If I have more properties to translate, this won't be a good implementation. The behavior is similar for the other translations.
How can reduce this code to one method?
I tried to use reflection but I didn't had any results.
Later...
I reduced my code to one method which return me the an entity instance with all the properties in the selected language or default language:
public CategoryText GetTranslation(int? DesiredLanguageId, int? DefaultLanguageId)
{
CategoryText retval = null;
var textInSelectedLanguage = this.CategoryTexts.Where(w => w.LanguageId == DesiredLanguageId).SingleOrDefault();
if (textInSelectedLanguage == null)
{
retval = this.CategoryTexts.Where(w => w.LanguageId == DefaultLanguageId).SingleOrDefault();
}
else
{
retval = textInSelectedLanguage;
}
return retval;
}
I think this method can be easily made generic by trying to find a way to replace my CategoryTexts Dbset with any other DbSet database entity. How can I do this?
Assuming you have an appropriate base class you could write it:-
public T GetTranslation<T>(DbSet<T> set, int? DesiredLanguageId, int? DefaultLanguageId)
where T:SomeBaseClassThatHasPropertyLanguageId
{
return
set.SingleOrDefault(w => w.LanguageId == DesiredLanguageId) ??
set.SingleOrDefault(w => w.LanguageId == DefaultLanguageId);
}
I somehow solved this.
I have an Internationalization class which with the following method:
public static T GetDbEntityTranslation<T>(ITranslatable Entity)
{
return (T)Entity.GetTranslation<T>(GetDefaultLanguage().Id, GetChosenLanguage().Id);
}
The ITranslatable interface:
public interface ITranslatable
{
T GetTranslation<T>(int? DesiredLanguageId, int? DefaultLanguageId);
}
My category class:
public partial class Category : ITranslatable
{
private LSEntities db = new LSEntities();
public T GetTranslation<T>(int? DesiredLanguageId, int? DefaultLanguageId)
{
CategoryText retval = null;
retval = db.CategoryTexts.Where(w => w.LanguageId == DesiredLanguageId && w.CategoryId == this.Id).SingleOrDefault()
?? this.CategoryTexts.Where(w => w.LanguageId == DefaultLanguageId && w.CategoryId == this.Id).SingleOrDefault()
?? this.CategoryTexts.Where(w => w.CategoryId == this.Id).FirstOrDefault();
if (retval == null)
throw new Exception("No translation found for this object");
return (T)(object)retval;
}
}
The problem was that I need to get my translation based on my category ID which is stored in Category class. My translations are searched in CategoryTexts property.
Hi Im using kendo ui grid in my project.
This is my code to insert records in database.
public static void Insert(StudentViewModel student)
{
student.StudentId = All().OrderByDescending(p => p.StudentId).First().StudentId + 1;
//All().Insert(0, student);
UniRegEntities uniRegEntities = new UniRegEntities();
Student stu =new Student();
stu.FName = student.FirstName;
stu.LName = student.LastName;
stu.Gender = uniRegEntities.Genders.Where(x => x.Title == student.Gender).FirstOrDefault();
stu.Id = student.StudentId;
uniRegEntities.Students.Add(stu);
uniRegEntities.SaveChanges();
}
And this is my update statement.
public static void Update(StudentViewModel student)
{
UniRegEntities context = new UniRegEntities();
var studentToUpdate = context.Students.Where(x => x.Id == student.StudentId).FirstOrDefault();
studentToUpdate.FName = student.FirstName;
studentToUpdate.LName = student.LastName;
studentToUpdate.Gender = context.Genders.Where(x => x.Title == student.Gender).FirstOrDefault();
context.SaveChanges();
}
Anyone can suggest me the delete method?
You can either get an entity from the DB and then delete it or create one and then delete it.
So:
var e = // Get
ctx.DeleteObject(e);
ctx.SaveChanges();
or
var e = new Foo() { FooId = id };
ctx.Entity.Attach(e);
ctx.DeleteObject(e);
ctx.SaveChanges();
Applied to your situation:
You are getting a record so you want to use DeleteObject()
public static void Update(StudentViewModel student)
{
UniRegEntities context = new UniRegEntities();
var studentToDelete = context.Students.Where(x => x.Id == student.StudentId).FirstOrDefault();
context.Students.DeleteObject(studentToUpdate);
context.SaveChanges();
}
context.Students.Remove(context.students.Single(x=>x.Id==student.Id));
Can you please try with below code snippet?
using (var db= new AppContext(ConnectionStr))
{
try
{
con.Configuration.AutoDetectChangesEnabled = false;
var o = new Student { StudentId = student.StudentId };
db.Students.Attach(o);
db.Students.Remove(o);
db.SaveChanges();
}
catch (Exception ex)
{
throw new Exception(ex.InnerException.Message);
}
finally
{
con.Configuration.AutoDetectChangesEnabled = true;
}
}
I have 2 ComboBoxes on my form. I create the bindings as follows:
TestClass myclass = new TestClass("Instruments");
myclass.Add(instr1 = new TestClass("INSTR1"));
myclass.Add(instr2 = new TestClass("INSTR2"));
myclass.Add(instr3 = new TestClass("INSTR3"));
myclass.Add(instr4 = new TestClass("INSTR4"));
instr1.Add(app1 = new TestClass("app1"));
instr1.Add(app2 = new TestClass("app2"));
instr1.Add(app3 = new TestClass("app3"));
instr1.Add(app4 = new TestClass("app4"));
instr2.Add(app5 = new TestClass("app5"));
instr2.Add(app6 = new TestClass("app6"));
instr2.Add(app7 = new TestClass("app7"));
instr2.Add(app8 = new TestClass("app8"));
mysource = new BindingSource(myclass, null);
selectedComboBox1.DataSource = mysource;
selectedComboBox1.DisplayMember = "NAME";
mysource2 = new BindingSource(selectedComboBox1, "SelectedItem");
selectedComboBox2.DataSource = mysource2;
selectedComboBox2.DisplayMember = "NAME";
The class used for the binding looks as follows
class TestClass : BindingList<TestClass>, INotifyPropertyChanged
{
public event RunTestChanged RunTestChangedEventHandler;
public TestClass()
{
this.test = "";
this.name = "";
this.runTest = true;
}
public TestClass(string name)
{
this.test = "";
this.name = name;
this.runTest = true;
}
public TestClass LIST
{
get
{
return this;
}
}
public string NAME
{
get
{
return this.name;
}
set
{
this.name = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("NAME"));
}
}
}
public string TEST
{
get
{
return this.test;
}
set
{
this.test = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("TEST"));
}
}
}
public bool RUNTEST
{
get
{
return runTest;
}
set
{
runTest = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("RUNTEST"));
}
RunTestArgs myargs = new RunTestArgs(value);
if (RunTestChangedEventHandler != null)
{
RunTestChangedEventHandler(this, myargs);
}
}
}
private bool runTest;
private string name;
private string test;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}
when the form first loads the 2 comboboxes are filled as they should be with the expected items. However, if i change an item in selectedComboBox1, the items in selectedComboBox2 aren't updated. I know that I can subscribe to the selectedComboBox1 SelectedIndexChanged event and then rebind the DataSource on selectedComboBox2 and everything will work as expected.
For example:
void selectedComboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
mysource2.DataSource = selectedComboBox1.SelectedItem;
mysource2.DataMember = null;
}
Another alternative that works is to perform the databinding as follows:
mysource = new BindingSource(myclass, null);
mysource2 = new BindingSource(mysource, "LIST");
mysource3 = new BindingSource(mysource2, "LIST");
selectedComboBox1.DataSource = mysource;
selectedComboBox1.DisplayMember = "NAME";
selectedComboBox2.DataSource = mysource2;
selectedComboBox2.DisplayMember = "NAME";
However I wanted to know if there was a way to avoid having to subscribe to the event or performing the databinding in a different manner and just have the 2nd ComboBox be updated via the BindingSource using the SelectedItem property. In the end I'm curious to know how to get the BindingSource to be updated via the SelectedItem databinding and if it's not possible what is preventing it from working.
Thank you for your help.
i have the same issue and got resolved by binding Name to SelectedValue of combobox and set ValueMember to be "NAME" property
selectedComboBox1.DisplayMember = "NAME";
selectedComboBox1.ValueMember = "NAME";