When using OwnsOne to map complex types, the sql column name is prefixed with the attribute name. Is it possible to specify the prefix name in the mapping?
This is my mapping:
e.OwnsOne(x => x.Attributes, cb =>
{
cb.OwnsOne(a => a.Supplier);
});
I would like the sql column to be prefixed with "Attr_" Instead of "Attributes_". Is this possible?
You could write an extension method to override the names of all columns;
public static void WithPrefix<T, R>(this OwnedNavigationBuilder<T, R> builder, string prefix) where T:class where R:class
{
foreach (var p in builder.OwnedEntityType.GetProperties())
p.SetColumnName($"{prefix}{p.Name}");
}
.OwnsOne(e => e.Address, cb => cb.WithPrefix(""));
Ivan Stoev's answer from the question comments:
It has to be done through the corresponding OwnsOne builder action argument. e.g. .OwnsOne(e => e.Address, cb => { cb.Property(e => e.Postcode).HasColumnName("Postcode"); });
(Making this a community wiki, just marking the question as being answered.)
Related
On my Swagger page, I am (mostly) able to order the operations as described on the Swashbuckle page.
Below the operations is a "Schemas" section showing the data structures used by the actions. These data structures appear in a arbitrary order. I would like to sort them.
The question Swagger sort Schema Defintions superficially looks like the same question, but in that question "sort" is used in the sense of "sorting the items into different bins", not "ordering a list" which is what I want.
I have made a document filter that "works", but when I look at the code I wrote, I die a little inside.
Is there a more correct way to do this?
Edit: To be specific, what I object to about this code is that it is "working" by sorting the entries in a Dictionary, which is just bad ( see this question ).
Turns out the answer was simply to use a SortedDictionary:
openApiDoc.Components.Schemas = new System.Collections.Generic.SortedDictionary<string, OpenApiSchema>(openApiDoc.Components.Schemas);
Actually, you were on the right track with the document filter!
In Startup.cs, ConfigureServices method:-
services.AddSwaggerGen(c =>
{
// ...
// For our document filtering needs.
c.DocumentFilter<DocumentFilter>();
});
And here is the document filter implementation:-
using System.Linq;
public class DocumentFilter : IDocumentFilter
{
public DocumentFilter()
{
}
// Implements IDocumentFilter.Apply().
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
if (swaggerDoc == null)
return;
// Re-order the schemas alphabetically.
swaggerDoc.Components.Schemas = swaggerDoc.Components.Schemas.OrderBy(kvp => kvp.Key, StringComparer.InvariantCulture)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}
when I look at the code I wrote, I die a little inside - embrace the glory of LINQ, and you will be proud of your code!
This wouldn't change the final display order for me, but MikeBeaton's suggestion whilst reporting a Sort Schema Issue on GitHub worked like a charm..
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swagger, _) =>
{
if (swagger.Components != null && swagger.Components.Schemas != null)
{
var replacement = new Dictionary<string, OpenApiSchema>();
foreach (var kv in swagger.Components.Schemas.OrderBy(p => p.Key))
{
replacement.Add(kv.Key, kv.Value);
}
swagger.Components.Schemas = replacement;
}
});
})
I'm using genemu_jqueryselect2_entity for a multiple selection field within a form (located in an Sonata admin class) for a so called Uni (university) entity:
->add('courses', 'genemu_jqueryselect2_entity',array('multiple' => true, 'class' => 'PROJECT\UniBundle\Entity\Course'))
But the selected entries are not filled into my entity. With firebug I was able to detect, that the ids of the courses are passed correctly via POST.
Maybe the field is not correctly mapped to the Uni entity, but I have no idea why.
This is the adding method of my Uni entity, which doesn't even get called:
public function addCourse(\PROJECT\UniBundle\Entity\Course $courses)
{
$this->courses[] = $courses;
return $this;
}
How can I get the field to be mapped with the courses attribute of Uni? How could I debug this?
Any help will be appriciated!
Try writing that method like this:
public function addCourse(\PROJECT\UniBundle\Entity\Course $course)
{
$this->courses[] = $course;
$course->setUniversity($this); // Or similar.
return $this;
}
Otherwise foreign key is not set on a course row in the DB.
Try to create method setCourses
public function setCourses(\Doctrine\Common\Collections\Collection $courses)
{
$this->courses = $courses;
...
I don't know why, but the method addCourse isn't called.
Anyway, Tautrimas Pajarskas's answer was usefull to me so I gave an upvote.
The foreign key relationship was the necessary and missing part of my code.
I implemented it in the university sonata admin like this:
private function addUniToCourses ($university) {
foreach($university->getCourses() as $course) {
if(!$course->getUniversities()->contains($university)) {
$course->addUniversity($university);
}
}
}
public function prePersist($university) {
$this->addUniToCourses($university);
}
public function preUpdate($university) {
$this->addUniToCourses($university);
}
This was the solution to my problem.
I had the same problem a while ago: Symfony2, $form->bind() not calling adder methods of entity
Solution:
For the adder (addCourse()) to be called, you have to disable the by_reference option of the field:
->add('courses', 'genemu_jqueryselect2_entity',
array(
'by_reference' => false, // This line should do the trick
'multiple' => true,
'class' => 'PROJECT\UniBundle\Entity\Course'))
Want to use objects of a public class in cshtml, but got a runtime error: Only parameterless constructors and initializers are supported in LINQ to Entities. What is wrong with the following statement? Thanks for any help!
#foreach (var obj in ViewData["IncompleteList"] as IEnumerable<Games.TeamAction>)
The controller fills the ViewBag, like
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity.Where(a => a.activityID == id)
.Select(s => new TeamAction(s.teamID, s.name, id, s.type));
ViewBag.IncompleteList = incomplete;
The class TeamAction (part of the namespace Games) is quite simple:
public class TeamAction
{
public TeamAction()
{
}
....
public int teamID {get; set;}
public string teamName { get; set; }
public int activityID { get; set; }
public int actionType { get; set; }
}
The answer is in the error message, once you figure out how to interpret it.
You are creating an IEnumerable via LINQ to Entities (the Entity Framework LINQ provider) like so:
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.Select(s => new TeamAction(s.teamID, s.name, id, s.type));
Note that your Select call includes a lambda expression that calls the TeamAction constructor that takes 4 parameters. As soon as you ask for the first element in the IEnumerable, LINQ tries to execute your query. At that point, it parses your lambda expression and tries to translate it into an Entity Framework query that it can run. But, as the exception message says:
Only parameterless constructors and initializers are supported
You cannot include the parameterized constructor in your LINQ query because LINQ to Entities doesn't know how to execute it. To fix the problem you have a few options.
Option One: IQueryable -> IEnumerable
The easiest way around this is to make sure the EF LINQ provider never sees the offending lambda, by forcing your IQueryable into an IEnumerable before it gets there. dbIncAct.ImcompleteActivity is probably a DbSet<>, and DbSet<>.Where returns an IQueryable that is still dependent on LINQ 2 Entities. To break that dependency, you can do:
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.AsEnumerable()
.Select(s => new TeamAction(s.teamID, s.name, id, s.type));
That will force your EF query to run up through the Where part and return an enumerable collection of IncompleteActivity entities. That thing (some internally-defined List-like object) is then used to call Select, completely apart from EF.
The downside here is that you're forcing the EF query, which probably hits a database, to happen immediately. If you don't want that, your only choice is to eliminate the parameterized constructor, using one of the other two options.
Option Two: Object Initializers
Depending on what that constructor did, you may or may not be able to easily fix it. If your constructor is just there to set properties on your newly created object, you're in luck. C# introduced the new object initializer syntax to go along with LINQ and lambdas for precisely this reason:
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.Select(s => new TeamAction
{
TeamId = s.teamID,
Name = s.name,
Id = id,
Type = s.type
});
Option Three: Refactor
If your constructor does any actual work, then you'll need to do some refactoring. Try to move as much logic into your default TeamAction() constructor as you can. You can also put some of the logic into the property setters, though you should try to minimize that as much as you can.
If your object really does require some complex initialization, the typical pattern to have an initialization method that gets called early in the life cycle, e.g:
var x = new X { ... };
x.InitializeMe();
You could do this, for example, inside your #for loop, or as a separate step immediately after you create your query.
Change it to object initialization instead:
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity.Where(a => a.activityID == id)
.Select(s => new TeamAction { teamID = s.teamID, teamName = s.name, activityID = s.id, actionType = s.type });
I have the following methods on the server (RIA services):
public IQueryable<Customer> GetCustomers()
{
return ObjectContext.Customers;
}
public IQueryable<Customer> GetCustomersWithInvoicesAndInvoiceItemsAsync()
{
return ObjectContext.Customers
.Include("Invoices.InvoiceItems");
}
These items are loaded on client as IEnumerable<>, lets name the CollectionA and CollectionB. My problem is next: If I add/remove a Customer in CollectionA, CollectionB will not be aware of it. Editing is working as expected, since both of collections contain same entities. Is there a way I can load Invoices separately, and relate them in the client, so I would work only on one collection of customers, and not two?
Service-side (beside the GetCustomers method):
public IQueryable<Invoice> GetInvoicesAndInvoiceItemsAsync()
{
return ObjectContext.Invoices.Include("InvoiceItems");
}
Client-side, after getting customers and invoices in separate calls (as IEnumerables):
var query = customers.Join(invoices, c => c.CustomerId, i => i.CustomerId,
(c,i) => Tuple.Create(c, i) );
This will give you a list of Customer - Invoice pairs. Or use GroupJoin:
var query = customers.GroupJoin(invoices, c => c.CustomerId, i => i.CustomerId,
(c,i) => Tuple.Create(c, i) );
This will give you a list of Customer - Invoices (plural) pairs.
I have a linq to sql object that has some references to some other tables
I am trying to map it to a vm but nothing ever gets captures.
Mapper.CreateMap<A, G>();
// A is the linq to sql object
A.MyList // this is a collection that I am trying to get the value out of A.MyList.Id
// G is my View Model
public class G
{
public string AMyListId {get; set;}
}
vm = Mapper.Map<List<A>, List<G>>(aListOfAFromDb);
This always comes back from null. I thought I would have to do it manually so I tried
Mapper.CreateMap<A, G>().ForMember(dest => dest.AMyList , opt => opt.MapFrom(src =>????));
but since I am getting it from a list it won't give any of the properties to choose from.
Edit
I realized that I should not have a list of "MyList" it should be a one to one. I still am having problems trying to do what I want to do.
I have this
Mapper.CreateMap();
A.Vip.UserId // again my linq object is A
// G is my View Model
public class G
{
public string IsVip {get; set;}
}
vm = Mapper.Map<List<A>, List<G>>(aListOfAFromDb);
Mapper.CreateMap<A, G>().ForMember(dest => dest.IsVip, opt => opt.AddFormatter<VipFormatter>());
public class VipFormatter : IValueFormatter
{
public string FormatValue(ResolutionContext context)
{
bool value = (bool)context.SourceValue;
if (value)
{
return "Yes";
}
return "No";
}
}
yet nothing every gets bound. I am not sure why. Do I have to do change my property to "AVipUserId"? Or somehow tell it to map?
From what I can see in your code, and in addition to my comment above, you don't need AutoMapper for this one:
List<A> dbItems;
IEnumerable<G> results = dbItems.Select(x => x.MyList.MyListID);
In fact, you can't map A to G, because you're going to create multiple "G" objects for each "A" object.
Let me know if I misunderstood the question here.
UPDATE:
I would change "G" to use a boolean property and then do the following:
Mapper.CreateMap<A, G>().ForMember(dest => dest.IsVip, opt => opt.MapFrom(src => src.Vip == null));
Or, whatever logic you use to determine if it is a VIP or not.
How about:
List<G> items = // whatever
var result = items.Select(g => Mapper.Map<G, A>(g));