TenantId is null when saving an entity in a Discriminator based multi tenant application in Grails 3.2 with GORM 6 - grails-orm

I am trying to implement a MultiTenant application using GORM 6.0.0.RC2.
When a domain class that implements MultiTenant is saved via the GORM's save() method, the tenantId property is not set to the current tenantId. It is always null and hence fails validation. However Tenants.currentId() returns the correct tenant id based on the specified tenant resolver class.
Is it the application's responsibility to set the tenantId on an instance of the domain class when it is saved or will GORM take care of it by setting the property appropriately before being saved?
My Domain Person class
class Person implements MultiTenant<Person> {
String id
String tenantId
String name
}
and the code to save an instance of the Person class is
new Person(name: "pmohan").save(failOnError: true)
it always fails with a validation exception indicating tenantId is null.
But the tenant resolver according to the configuration below resolves to the right tenantId.
gorm:
multiTenancy:
mode: DISCRIMINATOR
tenantResolverClass: com.MyTenantResolver
Also the Tenants.currentId returns the value as expected. I was expecting the save() method to populate the tenantId property automatically based on MyTenantResolver class.

Related

Saving related entities in EF with transient dbContext fails

I have a asp net core 3.1 web application. We use repository pattern with entity framework.
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(_configuration.GetConnectionString("MyConnString")), ServiceLifetime.Scoped);
Made up scenario, just bec everybody knows the "Customer-orders"-scenario:
When I create a new Order and want to connect it to an existing Customer I write something like this. Works ok.
var order = new Order(){...}
order.Customer = _dbContext.Customers.FirstOrDefault(c=>c.CustomerId = 42);
_dbContext.Orders.Add(order);
_dbContext.SaveChanges();
However. For other reasons we need to have dbContext transient, initiated like below:
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(_configuration.GetConnectionString("MyConnString")), ServiceLifetime.Transient);
This makes the code above fail. Error message is the Customer is already attached to dbContext ("The instance of entity type 'Customer' cannot be tracked because another instance with the same key value for {'CustomerId'} is already being tracked. When attaching existing entities, , ensure that only one entity instance with a given key value is attached")
or, if I test setting order.Customer = null:
The error message says "cant insert Id on identity column" (which it shouldnt, the customer already exists). ("Cannot insert explicit value for identity column in table 'Users' when IDENTITY_INSERT is set to OFF.")

EF Core: conditionally computed column

In my model I have AccountType property, which is mostly generated by database trigger, but sometimes can also be directly assigned:
public class Account
{
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public int AccountType { get; set; }
}
The attribute [DatabaseGenerated] allows me to automatically get database generated value back, whenever databaseContext.SaveChanges() executed.
However, this attribute seems to also prevent sending a value, if
I wish to assign AccountType explicitly. What is the best way to implement such "partially" generated/computed column?
However, this attribute seems to also prevent sending a value, if I wish to assign AccountType explicitly. What is the best way to implement such "partially" generated/computed column?
If you want to set a explicit value to a property decorected with [DatabaseGenerated(DatabaseGeneratedOption.Computed)] , you need to drop down to the lower level metadata API and set AfterSaveBehavior/BeforeSaveBehavior like below :
IMutableProperty.BeforeSaveBehavior Property is used for Add state
modelBuilder.Entity<Test>()
.Property(t => t.TestType)
.Metadata.BeforeSaveBehavior=PropertySaveBehavior.Save;
IMutableProperty.AfterSaveBehavior Property is used for Update state
modelBuilder.Entity<Test>()
.Property(t => t.TestType)
.Metadata.AfterSaveBehavior =PropertySaveBehavior.Save;
The PropertySaveBehavior indicates how changes to the value of a property will be handled by Entity Framework change tracking which in turn will determine whether the value set is sent to the database or not.

How to pass multiple parameters in #PUT method?

I have a requirement to implement a method like this in Apache CXF JAX-RS(in a concurrent scenario)
#PUT
#Path("/customers/123")
public void updateConcurrentCustomer(Customer existingCustomer,Customer updatedCustomer,boolean forceUpdate){
......
}
In request body, i need to call this method something like this (no root element).
<Customer>
.....existing data....
</Customer>
<Customer>
......updated data....
</Customer>
<boolean>true</boolean>
How this data binding can be achieved?
I tried creating a composite wrapper Resource class like this
#XmlRootElement
public class CustomCustomer implements java.io.Serializable
{
private Customer existingCustomer;
private Customer updatedCustomer;
private boolean forceUpdate;
.....
.....
}
It works well. But i dont want to create this wrapper class.
My concurrency scenario:
customer123 object is in state A.
user1 changes customer123 to state B.
user2 changes customer123 to state C.
user3 changes customer123 to state D. (all at same time)
only high priority user sets forceUpdate flag and finally that update will be overwritten than others.
existingCustomer - will be used to detect conflict changes. It will be in state A
According to the definition
The PUT method requests that the enclosed entity be stored under the
supplied Request-URI. If the Request-URI refers to an already existing
resource, the enclosed entity SHOULD be considered as a modified
version of the one residing on the origin server.
So first of all your customer needs a unique identifier like /customers/{id}. Otherwise the server can't know where the resource should be stored.
Then you don't need to pass the existingCustomer. Either there is none (new resource) or the server already knows him (because you addressed him with a unique URL).
The forceUpdate is also not senseful here. A PUT should modify if the resource already exists so forceUpdate is true be definition.
Sometimes you can't use the clear semantics of a PUT. For instance if the client does not know the id and you don't want that the client chooses one (he can't guarantee uniqueness). Then you can use a POST and the server will return the Location where he stored the Resource.
Also if you want to update only in special cases maybe depending on some other parameters a POST is the appropriate Method.

findBy URI not working in Spring Data Rest

By default, in Spring Data Rest the #Id of the entity is not exposed. In line with the REST rules, we're supposed to use the URI of the resource to refer to it. Given this assumption, the findBy queries should work if you pass a URI to them, but they don't.
For example, say I have a one-to-many relationship between Teacher and Student. I want to find students by teacher.
List<Student> findByTeacher(Teacher teacher)
http://localhost:8080/repositories/students/search/findByTeacher?teacher=http://localhost:8080/repositories/teachers/1
This doesn't work because the framework is attempting to convert the teacher URI to a Long.
I get this error that says "Failed to convert from type java.lang.String to type java.lang.Long".
Am I missing something?
You could expose #Id s by configuring web intializer
//Web intializer
#Configuration
public static class RespositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
config.exposeIdsFor(Teacher.class);
}
}
Its good to change List to Page
List findByTeacher(Teacher teacher)
to
Page<Student> findByTeacher(#Param("teacher) Teacher teacher, Pageable pageable);
Also note #Param annotation is required along with Pageable. The latter is required because return type "Page"
3.Latest snapshots, not milestones work fine
See https://jira.spring.io/browse/DATAREST-502
Depending of your version of Spring Data, it would work as you want or not. If you are with Spring Data 2.4, you need to pass the URI. If you are with a previous version, you need to pass the id.

How to use a Model object across a number of Controllers?

SCENARIO: After the user has successfully logged in, I want to create an Account object (Account class is a part of my model) and fetch and store the user's information in it. I want this Account object to be accessible to all my controllers (HomeController, AccountController etc) and also, I want the ability to edit its content from inside any controller. (1) How can I achieve this scenario? (2) How can I pass a model object from one controller to another?
I intend to keep it till the user logs out
Then you can use session. But I suggest not saving a lot of information. So for example you have this model or entity:
public class AccountModel {
public int Id {get;set;}
public string Username {get;set;}
// and a whole lot more of properties
}
I suggest you just store an identifier field, either the Id or the Username. Then when another request comes in and you need to either validate the request or update that same model, you can:
Fetch the data again for that user and all relevant information
Update the necessary fields
Save it in your database
So you would do something like:
// to save it in a session after logging in
Session["current_user_id"] = id_variable;
// to retrieve it from session after another request comes in
var id = (int)Session["current_user_id"];
// fetch from your database
// do the necessary update
// persist the changes
(1) How can I achieve this scenario?
You could retrieve the Account model from your datastore when you need to access it.
(2) How can I pass a model object from one controller to another?
See (1). Since you are retrieving it from the data store you don't need to be passing it around.