I have a model PersonalInfo like so:
public string FirstName { get; set;}
public string LastName { get; set;}
public string Email { get; set; }
public IEnumerable<Addresses> Addresses{ get; set; }
And my Addresses model include something like:
public int AddressId { get; set; }
public string StreedAddress { get; set; }
public string State { get; set; }
I need to use stored procedure to insert these into the database, but I need to use dapper to do that .. Sorry I am new.. I tried something like below and it did not work:
var Id = connection.Execute("usp_Insert", personalInfo, commandType: CommandType.StoredProcedure);
foreach (Addresses address in personalInfo.Addresses){
connection.Execute("Insert", new {address, Id}, null, commandType: CommandType.StoredProcedure);
};
I maybe doing a lot of things wrong here, I could use a table valuded param but I don't know how it works with dapper.
UPDATE:
So I guess if I don't want to pass the Addresses in the first one, can I exclude that from sending first? I still want to pass the whole model not one prop at a time.
Thanks
The problem is that dapper can't tell - with stored procedures - what parameters are needed. There are database APIs to query that, but it is hugely costly to do that all the time. For regular queries, it can understand well enough what is needed, but for stored procedures: they are mysteries. Consequently dapper just tries to include everything it can see.
Your best bet is a "new" projection that selects the properties you need for your parameters.
Related
I have the following two models within my Blazor Server project:
Vergadering:
public class Vergadering
{
[Key]
public int Id { get; set; }
public string Naam { get; set; }
public DateTime DatumTijd { get; set; }
public ICollection<Bestuurslid> Aanwezigen { get; set; }
public string? Notulen { get; set; }
public ICollection<Vergadering>? HoofdVergadering { get; set; }
public ICollection<Vergadering>? GekoppeldeVergaderingen { get; set; }
public ICollection<Bestand>? Bestanden { get; set; }
public string? UserLastEditId { get; set; }
public IdentityUser? UserLastEdit { get; set; }
public DateTime? LastEdit { get; set; }
public ICollection<VergaderingAgendaItem>? vergaderingAgendaItems { get; set; }
}
VergaderingAgendaItem:
public class VergaderingAgendaItem
{
public int Id { get; set; }
public string Omschrijving { get; set; }
public bool Afgerond { get; set; }
public int? ParentId { get; set; }
public VergaderingAgendaItem? Parent { get; set; }
public int VergaderingId { get; set; }
public Vergadering Vergadering { get; set; }
public string? UserAangedragenId { get; set; }
public IdentityUser? UserAangedragen { get; set; }
}
This results in three tables:
Vergaderingen
VergaderingAgendaItems
VergaderingVergadering
In my repository I have the following update method:
public async Task ChangeAfgerondStatusAsync(VergaderingAgendaItem item)
{
using (var _db = _factory.CreateDbContext())
{
_db.VergaderingAgendaItems.Update(item);
await _db.SaveChangesAsync();
}
}
Whenever the Vergadering does not have a GekoppeldeVergadering this update method does not create any problem.
But whenever the Vergadering does have a GekoppeldeVergadering and I update a VergaderingAgendaItem of that Vergadering I get this error:
An error occurred while saving the entity changes. See the inner exception for details.
Looking at the command prompt that opens up while running the project I saw the following query and error.
Queries:
Error:
An exception occurred in the database while saving changes for context type 'AVA_ZICHT.Data.ApplicationDbContext'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
Microsoft.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'PK_VergaderingVergadering'. Cannot insert duplicate key in object 'dbo.VergaderingVergadering'. The duplicate key value is (4, 3).
How is it that EF Core tries to update the GekoppeldeVergadering in VergaderingVergadering table. My method states VergaderingAgendaItem.Update()?
When handed a detached entity and told to Update it, EF will consider any associated entities as well. Since those references aren't tracked by the DbContext, the context will see those entities as new items to be inserted. This can result in duplicate key exceptions (as you are seeing) or inserting duplicate data with new PKs if those keys are set up as Identity columns.
One way to get around this issue is to use Automapper configured to just update the columns you expect to change:
public async Task ChangeAfgerondStatusAsync(VergaderingAgendaItem item)
{
using (var _db = _factory.CreateDbContext())
{
var existingItem = _db.VergaderingAgendaItems.Single(x => x.Id == item.Id);
Mapper.Map(item, existingItem);
await _db.SaveChangesAsync();
}
}
Alternatively this can be done manually by copying values from item to existingItem. existingItem is tracked entity so once it's updated, just call SaveChanges. The advantage of this over Update is that the resulting UPDATE SQL statement will only be for any columns that have actually changed, and it won't execute an UPDATE if nothing has actually changed.
This assumes we only want to copy fields from that entity, and none of the child/related entities. If you want to alter the collections/associations then you will need to eager load them and handle these separately. For instance changing the UserLastEdit reference, this is likely something you would want to eager-load so that it can be updated with the current User record.
My general advice is to avoid working with detached entities for concerns like this and instead use POCO view models. The trouble with using detached entities is that these are often incomplete representations of entity state, at worst, something deserialized from view state and cast into an Entity object. View Models can also be scaled down to just the data your client needs and what data is allowed to change. When it gets back to the server there is no confusion about what it is vs. what it pretends to be. Another consideration of applying updates which is important in multi-user systems is detecting stale data. Writing updates like this applies a "last in wins" approach where you should ideally check that the current DB data state concurrency token matches the token/version at the time that this user's original version was read. The attraction of using detached entities is the thought of avoiding a round-trip to the DB when performing an update, but in all honesty you should justify a round trip to ensure that the record is actually valid, the user actually can update that record, and the record hasn't been updated by someone else in the time this user was editing it.
I'm working on a project that want's to control data access in a multi-tenant system. I've got a table set up which has a row on it that says what tenant the object applies to. Let's call this property
ClientObject.ClientOrgId
I want to set something up so that anytime this table is accessed the only results that are returned are results that match some piece of data in the users session. I.e.
ClientObject.ClientOrgId == UserSession.ClientOrgId
and I ideally want to do this restriction on the table model instead of re-implementing it for every query created.
I've found the Autofilter attribute in the service stack documentation, and it looks like the thing that I want to use, but I've been unable to get it working. An example of my code is below, and I'm not seeing any filtering whenever I set the user sessions ClientOrgID to anything different.
[Authenticate]
[Route("/clientObject", HttpMethods.Post)]
[Api("Creates a Client Object")]
public class CreateClientObject : ICreateDb<ClientObjectTableModel>, IReturn<ClientObjectMutationResponse>
{
[ValidateNotEmpty]
public string ClientName{ get; set; }
[ValidateNotEmpty]
public string ClientLocation { get; set; }
[ValidateNotEmpty]
[ValidateNotNull]
public Guid? ClientOrgId { get; set; }
}
[AutoFilter(QueryTerm.Ensure, nameof(ClientObjectTableModel.ClientOrgId), Eval= "userSession.ClientOrgId")]
public class ClientObjectTableModel : AuditBase
{
[AutoId]
public Guid Id { get; set; }
[Required]
public string ClientName { get; set; }
[Required]
public string ClientLocation { get; set; }
[Required]
public Guid ClientOrgId { get; set; }
}
I even went off the rails and tried something like
[AutoFilter(QueryTerm.Ensure, nameof(ClientObjectTableModel.ClientLocation), Value = "The Fourth Moon Of Mars")]
with the expectation that nothing would get returned, and yet I'm still seeing results.
All AutoQuery CRUD Attribute like [AutoFilter] should be applied to the AutoQuery Request DTO, not the data model.
Have a look at how to populate Tenant Ids with AutoPopulate and how it's later used to filter results with [AutoFilter].
I want to update a mediate table's primary keys. This post suggests to take another primary key and don't change it, but we're using our tables in other non-EF projects, I didn't designed them and I'm not able to change them. Do I have any other option? Anything? Even deleting the old record and inserting a new one. I just don't know how to retrieve the old values.
This is my class:
public class ZChangeUnits : User
{
[Key]
[Column(TypeName = "VARCHAR", Order = 0), StringLength(4)]
public string CCode1 { get; set; }
[ForeignKey("CCode1")]
public virtual ZUnits ZUnits1 { get; set; }
[Key]
[Column(TypeName = "VARCHAR", Order = 1), StringLength(4)]
public string CCode2 { get; set; }
[ForeignKey("CCode2")]
public virtual ZUnits ZUnits2 { get; set; }
[Column(TypeName = "NUMERIC")]
[DecimalPrecision(18, 5)]
public decimal NZarib { get; set; }
}
UPDATE
I've posted the schema below, the left table is used for unit conversion. This is actually the suggested method in this answer.
I can't think of any other way to implement that. Whether I should update my table's design or some EF code will do the job. Any help and solution is welcome. :)
I'm experimenting with db4o as a data store, so to get to grips with it I thought I'd build myself a simple issue tracking web application (in ASP.NET MVC). I've found db4o to be excellent in terms of rapid development, especially for small apps like this, and it also negates the need for an ORM.
However, having come from a SQL Server/MySQL background I'm a little unsure of how I should be structuring my objects when it comes to relationships (or perhaps I just don't properly understand the way object databases work).
Here's my simple example: I have just two model classes, Issue and Person.
public class Issue
{
public string ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? SubmittedOn { get; set; }
public DateTime? ResolvedOn { get; set; }
public Person AssignedBy { get; set; }
public Person AssignedTo { get; set; }
}
public class Person
{
public string ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
The ID properties are just GUID strings generated by the .NET Guid.NewGuid() helper.
So here's how I initially thought the application would work; please ignore any security concerns etc and assume we already have a few Person objects stored in the database:
User logs in. Query the database for the Person which matches the username and password, and store his/her GUID id as a session variable. Redirect to app home screen.
Logged in user creates a new issue ticket, selecting the user to assign it to from a drop-down list. They fill in the other details (Title, Description etc), and then submit the form.
Query the Person objects in the database (by their GUID ID's) to get an object representing the logged in user and one representing the user the ticket has been assigned to. Create a new Person object (populated with the posted form data), assign the Person objects to the Issue object's AssignedBy and AssignedTo properties, and store it.
This would mean I have two Person objects stored against each Issue record. But what happens if I update the original Person—do all the stored references to that Person in the various issue objects update, or do I have to handle that manually? Are they references, or copies?
Would it be better/more efficient to just store a GUID string for the AssignedBy and AssignedTo fields (as below) and then look up the original person based on that each time?
public class Issue
{
public string ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? SubmittedOn { get; set; }
public DateTime? ResolvedOn { get; set; }
public string AssignedByID { get; set; }
public string AssignedToID { get; set; }
}
I think I'm just stuck in a certain way of thinking which is confusing me. If someone could explain it clearly that would be most helpful!
Object-Databases try to provide the same semantics as objects in memory. The rule of thumb is: It works like objects in memory. Object databases store references between the objects in the database. When you update the object, that object is updates. And if you have a reference to that objects, you see the changed version.
In your case, the Issue-objects refer to the person object. When you update that person, all Issues which refer to it 'see' that update.
Of course, primitive types like int, strings, longs etc are handled like value objects and not a reference objects. Also arrays are handled like value objects in db4o, this means a array is stored together with the object and not as a reference. Everything else is stored as a reference, even collections like List or Dictionaries.
Please take a look at:
http://developer.db4o.com/Documentation/Reference/db4o-7.4/java/reference/html/reference/basic_concepts/database_models/object-relational_how_to.html
Best!
Ok, I asked this question before, but deleted it as the way I went about describing my problem was wrong.
Firstly, let me state that Im creating a .NET3.5 Winforms app using C# and Plinqo (Professional Linq to Objects) as my ORM. Here's my situation: I have a DataGridview that is populated from a SortableBindingList<T> - in my case, formed from a List<Task> which is simply represented as follows:
public class Task {
public long TaskID { get; set; }
public string TaskDescription { get; set; }
public enumPriority TaskPriority { get; set; }
public DateTime DueDate { get; set; }
public double PercentageComplete { get; set; }
}
Now, I want to provide a Dialog to my user to allow him/her to Filter this list. I envision passing in a list of property names and associated DataType into the Dialog that I can use to populate a ComboBox. So the user will choose which property they want to query from the comboBox and based on the selection the appropriate comparers and UI control will be made available for the user to enter in thier criteria. Lastly, it will contain an AND/OR togglebutton at the end which the user can use to add additional criterion. Each criterion will be an object of type FilterItem as shown below:
public class FilterItem {
public string MappedPropertyName { get; set; }
public enumComparer Comparer { get; set; }
public object FilterValue { get; set; }
public enumOpertor Operator { get; set; }
}
After the user constructs his/her query, I intend to pass this as a List<FilterItem> back to my calling form, which can then iterate thru the list and allow me to filter the original List<Task>.
This is all fine, and something that I can put together with ease. But I want to make sure that the ACTUAL filter mechanism I go with is as strongly-typed as possible, and not using bulit up strings like in the Dynamic Query Library. (I used to do something similar previously with ADO.NET, DataViews and dynamically constructing a RowFilter string)
I've read up on Joseph Albahari's PredicatBuilder and an article on tomasp.net, but I seem heavily confused with it and expression trees in general.
I sincerely seek your assistance in helping me better understand these concepts, and how to go about using it up so that my intended architecture can work with it.
Much appreciation!
Additionally, I know I can do something like:
private SortableBindingList<Task> GetSortedTaskList()
{
List<Task> list = new List<Task>();
var query = DataUtil.GetUserTasks(xSys.Current.UserID);
if (/*description condition met*/)
{
query = query.Where(x => x.TaskDescription.Contains(FilterDesc));
}
if (/*due date condition met*/)
{
query = query.Where(x => x.DueDate >= FilterDate);
}
if (/*priority condition met*/)
{
query = query.Where(x => x.TaskPriority == FilterPriority);
}
...
list = query.ToList();
return new SortableBindingList<ArcTask>(list);
}
but this does not seem very scalable and 'dynamic'.