Linq2DB Add Property to Model - linq2db

Consider a simple model class and repository like this:
public class User
{
[Column, Nullable] public string Username { get; set; }
[Column, Nullable] public string Status { get; set; }
[Column, Nullable] public bool Expired { get; set; }
}
public class UserRepository : IUserRepository
{
public IQueryable<User> Get(IDataContext context)
{
using (context)
{
return context.GetTable<User>();
}
}
}
I'd like to add an additional property to the instances of the User class on reads, but this property is not needed on updates or inserts. The desired model would look something like this:
public class User
{
[Column, Nullable] public string Username { get; set; }
[Column, Nullable] public string Status { get; set; }
[Column, Nullable] public bool Expired { get; set; }
public string HostName {get; set;}
}
Essentially I'd like to pass in a hostname value provided by the caller that will travel with the objects. I can do this by using a select statement in my repo class:
using (context)
{
return from user in context.GetTable<User>()
select new User()
{
Username = user.Username,
Status = user.Status,
Expired = user.Expired,
Hostname = hostName
}
}
But this feels wrong, and I'm not sure I want to do this in my repo anyway. Also, many of my POCO classes have a large number of properties and this will be a eyesore. Is there an idiomatic in Linq2DB way to add an arbitrary property to objects retrieved from the DB? Thanks!

You probably can look at OnEntityCreated event of context. Something like that:
context.OnEntityCreated += args =>
{
if (arg.Entity is IHostable hostable)
{
// I see problem here - where to get hostName itself
// because context anyway is not multithreaded, it could
// be a field of current context
hostable.Hostname = context._hostName;
// or
hostable.Hostname = ((IHostProvider)ars.DataContext).HostName;
}
};

Related

Model binding Asp net core 2.2

I have the following complex model:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public int UserId { get; set; }
}
I need to bind the whole model in my action method using [FromBody], while Id property should come [FromQuery]. My action method looks like this:
public IActionResult Delete([FromBody]User userRequest)
{
// Some code
}
The thing is that I can't change the model, because it is 3-rd party and also, I can't have Id as the second parameter in action method, because I have validation logic for userRequest where I need the Id. Any ideas?
Use a DTO/view model and map over to User. For example:
public class UserDTO
{
public string UserName { get; set; }
public int UserId { get; set; }
}
Then:
public IActionResult Delete(int id, [FromBody]UserDTO userRequest)
{
var user = new User
{
Id = id,
UserName = userRequest.UserName,
UserId = userRequest.UserId
}
// do something
}

Partial Updates for Entities with Repository/DTO patterns in MVC (prepping for API)

I've built my Domain model layer, my repository layer, and now I'm working on my DTO layer to be used by a webApi project. I'm in the middle of implementing an Update service method, and I'm wondering about partial updates. Here's my DTO class:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
Here's my Update method in my Repository:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
My question is: What if I only need to update the Details of a person via my webAPI? Is the convention to construct an entire PersonDto and Update the entire object using SetValues, or is there any way I can specify that I only want a single field updated so that I don't have to send a ton of data over the wire (that I don't really need)?
If it is possible to do partial updates, when is it ever good to update the entire entity? Even if I have to update 5/7 properties, it requires that I send old data for 2/7 to re-write so that SetValues doesn't write nulls into my fields from my DTO.
Any help here would be awesome... totally new to this stuff and trying to learn everything right. Thank you.
I've taken similar approach to do optimization, and I've faced same issues with null values when attaching (not just null, you'll have issue with boolean as well). This is what I've come up with:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
That is extension method for DbContext. Here are the interfaces IDTO and IEntity:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
I'm using my custom EnsureUpdatedAttribute to annotate what properties should always be updated (to deal with nulls / default values not being tracked):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
And this is a sample of usage:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent() is just extension for returning 204 (NoContent).
Hope this helps.
Theres a few options you have, you can create a stored procedure to update the required parts (I wouldnt do this), or you can manually select the fileds to update on the model before saving the context changes with EF.
Heres an example how to update a specific field:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
It will depend on your scenario what you want to do, but generally speaking its fine to send your whole entity to be updated, and this is how i would approach your situation potentially changing in the future if needed.

How do I use (Try)UpdateModel?

What is the right way to use (Try)UpdateModel?
When I run this:
TryUpdateModel returns true,
ViewData has no errors,
but my Proxy is not updated.
Action Method
public void Save(string TypeName, int Id, FormCollection idontknow) {
var types = Assembly.GetExecutingAssembly().GetTypes();
var ObjectType=(from t in types where t.Name == TypeName select t).First();
var Proxy = context.Set(ObjectType).Find(Id); // EF 4.1
if (TryUpdateModel(Proxy, TypeName)) {
var x = ViewData.GetModelStateErrors(); // no errors
}
}
Posted Data
TypeName=Thing&Id=1&Thing.Id=1&Thing.Name=hello&Thing.OptionID=2
Thing Class
public class Thing : Base {
public virtual Nullable<int> OptionID { get; set; }
public virtual Option Option { get; set; }
public virtual ICollection<ListItem> ListItems { get; set; }
}
public class Base {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
[NotMapped]
public virtual int? EntityState { get; set; }
}
EDIT: I also tried passing the form collection explicitly
TryUpdateModel(Proxy, TypeName, idontknow)
EDIT #2: (in response to NickLarsen)
Restarted VS and server, no change.
Values are actually in the FormCollection.
Mock data works! I know I must be messing up something here.
Using debugger to check values.
I stripped all the EF stuff and tried to get just that query string to populate the model with the values... and it worked just fine.
//controller class
public ActionResult Save(string TypeName, int Id, FormCollection idontknow)
{
var Proxy = new Thing
{
Id = 33,
OptionID = 2234,
Name = "tony",
};
if (TryUpdateModel(Proxy, TypeName))
{
ViewBag.Message = "WInner";
}
return RedirectToAction("Index");
}
//end controller class
public class Thing : Base
{
public virtual Nullable<int> OptionID { get; set; }
}
public class Base
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
Honestly I can't figure think of what in your code would keep it from working, but I would suggest going through the list 1 by one and testing after each step...
Save your progress and restart VS and your development server
Check that the values are actually in the form data, maybe something is getting in the way there.
Mock up some trash data like I did. (checking if the problem has something to do with EF)
How are you identifying that Proxy isn't being updated? In the debugger, on the page, etc?
Edit your question with the answer to all of the above questions.

Returning Associated Members via Ria DomainService Invoke Method

I got this DomainService method I'm calling from my SL ViewModel using the Invoke attribute:
[Invoke]
public ServiceModel.Recipy GetRecipyById(int recipyId)
{
return new Recipy
{
RecipyId = 1,
Name = "test",
Description = "desc",
Author = new Author
{
AuthorId = 1,
Name = "Johan"
}
};
}
The code in my ViewModel looks like this:
public RecipyViewModel()
{
context.GetRecipyById(1, RecipyLoadedCallback, null);
}
private void RecipyLoadedCallback(InvokeOperation<Recipy> obj)
{
_name = obj.Value.Name;
_description = obj.Value.Description;
_authorName = obj.Value.Author.Name;
}
The Recipy and Author POCO/ServiceModel classes:
public class Recipy
{
[Key]
public int RecipyId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[Association("Author", "RecipyId", "AuthorId")]
[Include]
public Author Author { get; set; }
}
public class Author
{
[Key]
public int AuthorId { get; set; }
public string Name { get; set; }
}
Now, the code works fine, except that the associated Author is not transfered over to the client/viewmodel, the Author property of Recipy is null. I thought that using the [Associate] and [Include] attributes would do the trick?
Thanks for any help, I'm trying hard to grok the DomainService/RIA stuff and I'm close to giving up and go "normal" WCF/REST instead :)
As I understand it, [Invoke] doesn't support complex hierarchies at the moment, so I solved it by making sure I had the correct attributes for [Include] and [Association] on the collection, and went back to using a normal RIA query method instead.

How to catch Non-integer values mapped to integer properties in UpdateModel()

Let's say you have an object called Person that looks like this:
class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int NumberOfCatsNamedEnder { get; set; }
}
I have a simple HTML form that exposes the properties that gets posted to an ASP.NET MVC action inside of my PersonController class. The issue I have is that if someone puts in the letter 'A' for NumberOfCatsNamedEnder, I get a The model of type 'Person' was not successfully updated. error. Since this happens while trying to update the Model, I can't find any way to check to see if someone passed in a non-integer value without resorting to
if(!IsInteger(formCollection["NumberOfCatsNamedEnder"]))
{
ModelState.AddModelError(
"NumberOfCatsNamedEnder",
"Ender count should be a number");
}
Is there a better way to do this? I was able to find some information on custom ModelBinders; is that what is needed?
I really like the approach of using a presentation model. I'd create a class like this:
class PersonPresentation
{
public int ID { get; set; }
public string Name { get; set; }
public string NumberOfCatsNamedEnder { get; set; }
public void FromPerson(Person person){ /*Load data from person*/ }
}
Then your controller action can bind the view to a PersonPresentation:
public ActionResult Index()
{
Person person = GetPerson();
PersonPresentation presentation = new PersonPresentation();
ViewData.Model = presentation.FromPerson(person);
return View();
}
...and then accept one in your Update method and perform validation:
public ActionResult Update(PersonPresentation presentation)
{
if(!IsInteger(presentation.NumberOfCatsNamedEnder))
{
ModelState.AddModelError(
"NumberOfCatsNamedEnder",
"Ender count should be a number");
}
...
}

Resources