I have a base controller that loads an EF6 entity in a protected Dictionary member. But somehow it stops other controllers from updating that entity. Below is the simplified code -
public abstract class BaseController : Controller {
protected IDictionary<string, int> MyList;
public BaseController() {
MyList = new Dictionary<string, int>();
foreach (var rc in db.MyTable.Where(r => r.IsActive).ToList())
MyList.Add(rc.Name, rc.Id);
ViewBag.MyListViewBag = MyList;
}
}
public class MyController : BaseController {
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Name,...other properties")] MyTable mt) {
if (ModelState.IsValid)
{
db.Entry(mt).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(mt);
}
}
The error is "Attaching an entity of type failed because another entity of the same type already has the same primary key value" and it is thrown at db.Entry(mt).State = EntityState.Modified;
As you can see, BaseController loads the entity in a list first, that should severe any connections (at least I thought it would). Is there a way around it?
Your mt variable, which you take from action arguments, is not a part of EF tracked objects. So, you should take it from db and then update it's necessary properties:
public ActionResult Edit([Bind(Include = "Id,Name,...other properties")] MyTable mt)
{
if (ModelState.IsValid)
{
var temp = db.MyTable.Single(mt.Id);
temp.Name = mt.Name;
//copying of other properties...
db.SaveChanges();
return RedirectToAction("Index");
}
return View(mt);
}
Related
How can a many to many mapping be done in Quarkus when using hibernate-reactive-panache extension together with reactive-pg-client. I tried it, failed terribly and resorted to the non-reactive way of implementing it. Here is my non-reactive way of implementing it.
#Entity
#Table(name = "categories")
public class CategoryEntity extends PanacheEntityBase{
//some code here
#Fetch(FetchMode.JOIN)
#ManyToMany(mappedBy = "categories")
#JsonManagedReference
public Set<ProductEntity> products = new HashSet<>();
public void addProduct(ProductEntity product){
boolean added = products.add(product);
if(added){
product.categories.add(this);
}
}
#Transactional
public static void create(Category category){
CategoryEntity entity = new CategoryEntity();
entity.description = category.description;
entity.name = category.name;
ProductEntity pe = ProductEntity.findByName(category.productName);
if(pe != null){
pe.addCategory(entity);
}
entity.persistAndFlush();
}
Here is the ProductEntity...
#Table(name = "products")
#Entity
public class ProductEntity extends PanacheEntityBase{
#Fetch(FetchMode.JOIN)
#ManyToMany(
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.DETACH,
CascadeType.REFRESH
})
#JoinTable(
name = "product_category",
joinColumns = {#JoinColumn(name = "product_id")},
inverseJoinColumns = {#JoinColumn(name = "category_id")},
uniqueConstraints = {
#UniqueConstraint(columnNames = {"product_id", "category_id"})
}
)
#JsonBackReference
public Set<CategoryEntity> categories = new HashSet<>();
public void addCategory(CategoryEntity category){
boolean added = categories.add(category);
if(added){
category.products.add(this);
}
}
public static ProductEntity findByName(String name){
return find("name", name).firstResult();
}
#Transactional
public static void create(Product product){
ProductEntity entity = new ProductEntity(
product.name, product.imageURL, product.description, product.status, product.price
);
CategoryEntity ce = CategoryEntity.findByName(product.categoryName);
if(ce != null){
ce.addProduct(entity);
}
entity.persistAndFlush();
}
The main roadblock that I faced when I implemented it in a reactive way was I could not call a category from the database in order to link it with a product I was creating. Not that the code couldn't compile it just couldn't run.
When creating a product I wrote the following code...
public static Uni<ProductEntity> create(Product product){
ProductEntity entity = new ProductEntity(
product.name, product.imageURL, product.description, product.status, product.price
);
//this adding of a category could not work i.e a category in the database
//would not be retrieved to be linked with the product I was creating.
CategoryEntity.findByName(product.categoryName)
.onItem().transform(category -> entity.addCategory(category));
return entity.persistAndFlush();
I also tried
CategoryEntity cEntity = CategoryEntity.findByName(product.categoryName).await().indefinitely();
productEntity.addCategory(cEntity);
I would be surprised with a runtime error even after annotating the method with #Blocking
NOTE: I have some pojos, Product and Category. The Product POJO has a categoryName, and the Category POJO has a productName. I use them when creating new products and categories.
Thanks for your help in advance.
I have been trying to add custom DAC record which is the in database. But it is now working. Here is how I have tried to accomplish.
public class SquarePOSTransactionInquiry : PXGraph<SquarePOSTransactionInquiry>
{
public PXSave<MasterTable> Save;
public PXCancel<MasterTable> Cancel;
public PXFilter<MasterTable> MasterView;
public PXSelect<INSquarePOSTransaction> INSquarePOSTransactions;
public PXAction<MasterTable> calc;
[PXUIField(DisplayName = "Sync Square Transactions")]
[PXProcessButton()]
protected virtual IEnumerable Calc(PXAdapter adapter)
{
PXLongOperation.StartOperation(this, () =>
{
using (var scope = new PXTransactionScope())
{
INSquarePOSTransaction trans = new INSquarePOSTransaction();
trans.TransacationCD = "new";
trans.Description = "Another new";
var test = this.INSquarePOSTransactions.Insert(trans);
this.INSquarePOSTransactions.Cache.IsDirty = true;
//this.INSquarePOSTransactions.Update(trans);
this.Actions.PressSave();
scope.Complete();
}
});
return adapter.Get();
}
public SquarePOSTransactionInquiry()
{
}
[Serializable]
public class MasterTable : IBqlTable
{
}
}
I tried setting cache IsDirty property to false, but that didn't help too. But the strange part is updating the DAC is working. I have even looked into other Business Logic codes from other pages and it looks same like I have tried above. Could you please tell me what I am missing?
Thanks.
Within the method that you pass to StartOperation(),
you have to create a new instance of the graph and invoke the processing method on that instance.
What I'm trying to do is create a site in Orchard that doesn't have a way for a user to register. An administrator will create the users.
What I have is module that defines the parts, records, views, etc. That is basically working.
Now what I'm trying to do is add a UserPart (from Orchard.Users) to one of the parts in my module.
I'm not sure how to do that. I need the fields displayed for the UserPart with the fields for the parent part in the same view. This also needs to be done in a way that when a save happens, all of the UserPart fields get sent to the Orchard.Users module.
Any suggestions, pointers or links on how to do that?
Thanks!
UPDATE...
The Activating Filter is an interesting idea. I initially chose the migration route. For now, I'll try and get that method working.
For simplicity, let's say I have a "Company" type (there's more to the actual type) that has a "CompanyName" and a UserPart.
Here's what the different pieces look like...
Migrations.cs (simplified)
public int Create()
{
SchemaBuilder.CreateTable("CompanyPartRecord", table => table.ContentPartRecord()
.Column("CompanyName", DbType.AnsiString, c => c.WithLength(50))
.Column("UserId", DbType.Int32));
SchemaBuilder.CreateForeignKey("FK_CompanyPartRecord_UserPartRecord", "CompanyPartRecord", new[] {"UserId" }, "Orchard.Users", "UserPartRecord", new[] { "Id" })
ContentDefinitionManager.AlterTypeDefinition("Company", type => type.WithPart("CommonPart").WithPart("UserPart"));
}
CompanyPartRecord
public class CompanyPartRecord : ContentPartRecord
{
public virtual string CompanyName { get; set; }
public virtual int? UserId { get; set; }
}
CompanyPart
public class CompanyPart : ContentPart<CompanyPartRecord>
{
internal LazyField<UserPart> UserPartField = new LazyField<UserPart>();
public string CompanyName
{
get { return Record.CompanyName; }
set { Record.CompanyName = value; }
}
public UserPart User
{
get { return UserPartField.Value;}
set { UserPartField.Value = value; }
}
}
Handler
public class CompanyPartHandler : ContentHandler
{
private readonly IContentManager _manager;
public CompanyPartHandler(IRepository<CompanyPartRecord> repository, IContentManager manager)
{
_manager = manager;
Filters.Add(StorageFilter.For(repository));
OnActivated<CompanyPart>(OnActivatedHandler);
}
private void OnActivatedHandler(ActivatedContentContext context, CompanyPart part)
{
if(part.User == null)
{
part.User = _manager.Create<UserPart>("User");
}
else
{
part.User = _manager.Get<UserPart>(part.User.Id);
}
}
}
Driver
public class CompanyPartDriver : ContentPartDriver<CompanyPart>
{
protected override DriverResult Editor(CompanyPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Company_Edit", () => shapeHelper.EditorTemplate(TemplateName: "Parts/Company",
Model: part, Prefix: Prefix));
}
protected override DriverResult Editor(CompanyPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
Controller
public class AdminCompanyController : Controller, IUpdateModel
{
private readonly IOrchardServices _services;
private readonly INotifier _notifier;
private readonly IContentManager _contentManager;
private readonly ITransactionManager _transactionManager;
private readonly Localizer T = NullLocalizer.Instance;
public AdminCompanyController(IOrchardServices services)
{
_services = services;
_notifier = services.Notifier;
_contentManager = services.ContentManager;
_transactionManager = services.TransactionManager;
}
public ActionResult Create()
{
var company = _contentManager.New<CompanyPart>("Company");
var model = _contentManager.BuildEditor(company);
return View(model);
}
[HttpPost, ActionName("Create")]
public ActionResult CreatePOST()
{
var contentItem = _contentManager.New<CompanyPart>("Company");
var model = _contentManager.UpdateEditor(contentItem, this);
if (!ModelState.IsValid)
{
_transactionManager.Cancel();
return View(model);
}
_contentManager.Create(contentItem.ContentItem);
_notifier.Information(T("Company has been saved"));
return RedirectToAction("Index");
}
public ActionResult Edit(int Id)
{
var contentItem = _services.ContentManager.Get(Id);
dynamic model = _services.ContentManager.BuildEditor(contentItem);
return View(model);
}
[HttpPost, ActionName("Edit")]
public ActionResult EditPOST(int Id)
{
var contentItem = _contentManager.Get<CompanyPart>(Id);
var model = _contentManager.UpdateEditor(contentItem, this);
_notifier.Information(T("Company has been saved"));
return RedirectToAction("Index");
}
public ActionResult Delete(int Id)
{
var contentItem = _contentManager.Get<CompanyPart>(Id);
_contentManager.Destroy(contentItem.ContentItem);
return RedirectToAction("Index");
}
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)
{
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
}
public void AddModelError(string key, LocalizedString errorMessage)
{
ModelState.AddModelError(key, errorMessage.ToString());
}
}
View (create)
#{ Layout.Title = T("Add Company").ToString(); }
#using (Html.BeginFormAntiForgeryPost())
{
#Display(Model)
}
Editor Template
#model SDS.Models.CompanyPart
<fieldset>
#Html.LabelFor(m => m.CompanyName)
#Html.TextBoxFor(m => m.CompanyName)
</fieldset>
#*
What goes here to display UserPart?
*#
So here's where I'm at. I can see the ContentItem (CompanyType). I can put in the name and save it. The name is getting saved to the db. Right now the UserPart is getting saved to the db, but all of the fields are blank.
The part I'm stuck on is what to put in the editor template to display the UserPart fields so that the values get to the UserPart driver and ultimately the db.
Any ideas on how to do that?
Thanks!
So you don't attach parts to parts, you attach parts to content items, and you can do that in multiple ways.
You can do it through the admin screen, but that isn't a code driven solution and would have problems if you have multiple environments or need to redeploy a fresh version of code.
You can attach the part when you create a new content item in the migration. This might be a good solution, if you already ran your migration you could possibly do it with an update migration. This allows the part to be managed through the admin screen, but has downsides because it can be removed and if you have code that relies on the part then you will start having errors.
The last way and best way is to attach the part dynamically using an Activating Filter.
ActivatingFilter class - Attaches a part to a content type from code. As opposed to attaching parts via migrations, parts attached using this filter will neither be displayed in the Dashboard, nor users will be able to remove them from types. It's a legitimate way of attaching parts that should always exist on a given content type.
So to do this:
1. Add a reference to Orchard.Users to your custom project.
2. Create a handler for you part. Such as MyPartHandler
3. Then add the activating handler like so
Filters.Add(ActivatingFilter.For<UserPart>("MyContentType"));
So now anywhere in your code you can access the UserPart if you already have your part, or the content item using
var userPart = myPart.As<UserPart>();
I have generic Result<T> generic class which I use often in methods to return result like this
public Result<User> ValidateUser(string email, string password)
There is ILoggingService interface in Result class for logging service injection but I do not find a way to inject actual implementation.
I tried to execute the code below but TestLoggingService intance is not injected into LoggingService property. It always return null. Any ideas how to solve it?
using (var kernel = new StandardKernel())
{
kernel.Bind<ILoggingService>().To<TestLoggingService>();
var resultClass = new ResultClass();
var exception = new Exception("Test exception");
var testResult = new Result<ResultClass>(exception, "Testing exception", true);
}
public class Result<T>
{
[Inject]
public ILoggingService LoggingService{ private get; set; } //Always get null
protected T result = default(T);
//Code skipped
private void WriteToLog(string messageToLog, object resultToLog, Exception exceptionToLog)
{
LoggingService.Log(....); //Exception here, reference is null
}
You are creating the instance manually using new. Ninject will only inject objects created by kernel.Get(). Furthermore it seems you try to inject something into a DTO which is not recommended. Better do the the logging in the class that created the result:
public class MyService
{
public MyService(ILoggingService loggingService) { ... }
public Result<T> CalculateResult<T>()
{
Result<T> result = ...
_loggingService.Log( ... );
return result;
}
}
Here is a sample of what I am trying to accomplish:
public class BaseClass<T>
{
public static T GetByID(int ID)
{
// Need database name here that is determined at design time in the derived class.
var databaseName = "";
// do some stuff involving database name that gets me object by ID here.
return default(T);
}
}
public class DerivedClass : BaseClass<DerivedClass>
{
private string DatabaseName { get; set; }
}
Basically, how would I access the derived "DatabaseName" in the base class static GetByID method?
EDIT: After I posted this, I tried one more thing. I played with attributes earlier, and failed, but I think my brain was mushy. Just tried again and ran a test, and it is working. Here is the updated sample.
public class BaseClass<T>
{
public static T GetByID(int ID)
{
// Need database name here that is determined at design time in the derived class.
var databaseName = ((DatabaseAttribute)typeof(T).GetCustomAttributes(typeof(DatabaseAttribute), true).First()).DatabaseName;
// do some stuff involving database name that gets me object by ID here.
return default(T);
}
}
[Database("MyDatabase")]
public class DerivedClass : BaseClass<DerivedClass>
{
}
public class DatabaseAttribute : Attribute
{
public DatabaseAttribute(string databaseName)
{
DatabaseName = databaseName;
}
public string DatabaseName { get; set; }
}
Base class to derived class is a one-way inheritance: The base class has no knowledge of the existance of a derived class, and so it can't access it.
In addition to that you will have a hard time accessing a non-static property from a static method.
I know you've already answered your own question, but some improvements....
Add a where clause to guarantee inheritance, it means any static methods can make use of inherited methods. You might also want to add the new() clause if you wish to be able to create instances of the inherited class.
public class BaseClass<T> : where T : BaseClass<T>
{
static readonly string databaseName;
static BaseClass() {
// Setup database name once per type of T by putting the initialization in
// the static constructor
databaseName = typeof(T).GetCustomAttributes(typeof(DatabaseAttribute),true)
.OfType<DatabaseAttribute>()
.Select(x => x.Name)
.FirstOrDefault();
}
public static T GetByID(int ID)
{
// Database name will be in the static field databaseName, which is unique
// to each type of T
// do some stuff involving database name that gets me object by ID here.
return default(T);
}
}
[Database("MyDatabase")]
public class DerivedClass : BaseClass<DerivedClass>
{
}
public class DatabaseAttribute : Attribute
{
public DatabaseAttribute(string databaseName)
{
DatabaseName = databaseName;
}
public string DatabaseName { get; set; }
}